ffmpeg в C # (ScrCpy) - PullRequest
       30

ffmpeg в C # (ScrCpy)

0 голосов
/ 18 марта 2019

Я пытаюсь получить доступ к экрану моего Android-устройства, как это делает ScrCpy (https://github.com/Genymobile/scrcpy), но в C #. До сих пор я нажимал на сервер (jar-файл) и получал ввод. (Разрешение устройства и т. Д.) Но я не могу повторно реализовать процесс декодирования в c #, пока что должна быть какая-то ошибка. Библиотека f # для ffmpeg используется как ffmpeg.AutoGen (https://github.com/Ruslan-B/FFmpeg.AutoGen)

Вот код декодирования:

    using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;
using FFmpeg.AutoGen;

namespace Source.Android.Scrcpy
{
    public unsafe class Decoder
    {
        private const string LD_LIBRARY_PATH = "LD_LIBRARY_PATH";

        private AVFrame* _decodingFrame;
        private AVCodec* _codec;
        private AVCodecContext* _codec_ctx;
        private AVFormatContext* _format_ctx;

        public Decoder()
        {
            RegisterFFmpegBinaries();
            SetupLogging();

            this.InitFormatContext();
        }

        private void InitFormatContext()
        {
            _decodingFrame = ffmpeg.av_frame_alloc();
            _codec = ffmpeg.avcodec_find_decoder(AVCodecID.AV_CODEC_ID_H264);
            if (_codec== null)
            {
                throw new Exception("H.264 decoder not found");// run_end;
            }

            _codec_ctx = ffmpeg.avcodec_alloc_context3(_codec);
            if (_codec_ctx == null)
            {
                throw new Exception("Could not allocate decoder context"); //run_end
            }

            if (ffmpeg.avcodec_open2(_codec_ctx, _codec, null) < 0)
            {
                throw new Exception("Could not open H.264 codec");// run_finally_free_codec_ctx
            }

            _format_ctx = ffmpeg.avformat_alloc_context();
            if (_format_ctx == null)
            {
                throw new Exception("Could not allocate format context");// run_finally_close_codec;
            }
        }

        private void RegisterFFmpegBinaries()
        {
            switch (Environment.OSVersion.Platform)
            {
                case PlatformID.Win32NT:
                case PlatformID.Win32S:
                case PlatformID.Win32Windows:
                    var current = Environment.CurrentDirectory;
                    var probe = Path.Combine("FFmpeg", Environment.Is64BitProcess ? "x64" : "x86");
                    while (current != null)
                    {
                        var ffmpegDirectory = Path.Combine(current, probe);
                        if (Directory.Exists(ffmpegDirectory))
                        {
                            Console.WriteLine($"FFmpeg binaries found in: {ffmpegDirectory}");
                            RegisterLibrariesSearchPath(ffmpegDirectory);
                            return;
                        }
                        current = Directory.GetParent(current)?.FullName;
                    }
                    break;
                case PlatformID.Unix:
                case PlatformID.MacOSX:
                    var libraryPath = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
                    RegisterLibrariesSearchPath(libraryPath);
                    break;
            }
        }
        private static void RegisterLibrariesSearchPath(string path)
        {
            switch (Environment.OSVersion.Platform)
            {
                case PlatformID.Win32NT:
                case PlatformID.Win32S:
                case PlatformID.Win32Windows:
                    SetDllDirectory(path);
                    break;
                case PlatformID.Unix:
                case PlatformID.MacOSX:
                    string currentValue = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
                    if (string.IsNullOrWhiteSpace(currentValue) == false && currentValue.Contains(path) == false)
                    {
                        string newValue = currentValue + Path.PathSeparator + path;
                        Environment.SetEnvironmentVariable(LD_LIBRARY_PATH, newValue);
                    }
                    break;
            }
        }

        [DllImport("kernel32", SetLastError = true)]
        private static extern bool SetDllDirectory(string lpPathName);

        private AVPacket GetPacket()
        {
            var packet = ffmpeg.av_packet_alloc();
            ffmpeg.av_init_packet(packet);

            packet->data = null;
            packet->size = 0;

            return *packet;
        }

        private static int read_raw_packet(void* opaque, ushort* buffer, int bufSize)
        {
            var buffSize = 1024;
            var remaining = dt.Length - dtp - 1;
            var written = 0;
            for (var i = 0; i < buffSize && i+dtp < dt.Length; i++)
            {
                buffer[i] = dt[i+dtp];
                written++;
            }

            dtp += written;

            if (written <= 0)
            {
                return ffmpeg.AVERROR_EOF;
            }

            return written;
        }

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate int av_read_function_callback(void* opaque, ushort* endData, int bufSize);

        private static byte[] dt;
        private static int dtp;

        public Bitmap DecodeScrCpy(byte[] data)
        {
            if (data.Length == 0)
            {
                return null;
            }

            byte* _buffer;
            ulong _bufferSize = 1024*2;
            _buffer = (byte*)ffmpeg.av_malloc(_bufferSize);
            if (_buffer == null)
            {
                throw new Exception("Could not allocate buffer"); // run_finally_free_format_ctx;
            }

            fixed (byte* dataPtr = data)
            {
                dt = data;
                dtp = 0;
                fixed (AVFormatContext** formatCtxPtr = &_format_ctx)
                {
                    var mReadCallbackFunc = new av_read_function_callback(read_raw_packet);
                    AVIOContext* avio_ctx = ffmpeg.avio_alloc_context(_buffer, (int)_bufferSize, 0, null, //TODO: IntPtr.Zero nutzen?
                        new avio_alloc_context_read_packet_func{Pointer = Marshal.GetFunctionPointerForDelegate(mReadCallbackFunc) }, 
                        null, null);
                    if (avio_ctx == null)
                    {
                        ffmpeg.av_free(dataPtr);
                        throw new Exception("Could not allocate avio context"); //goto run_finally_free_format_ctx;
                    }

                    _format_ctx->pb = avio_ctx;

                    if (ffmpeg.avformat_open_input(formatCtxPtr, null, null, null) < 0)
                    {
                        throw new Exception("Could not open video stream"); // goto run_finally_free_avio_ctx;
                    }

                    var packet = GetPacket();

                    while (ffmpeg.av_read_frame(_format_ctx, &packet) == 0)
                    {
                        if (ffmpeg.LIBAVDEVICE_VERSION_INT >= ffmpeg.AV_VERSION_INT(57, 37, 0))
                        {
                            int ret;
                            if ((ret = ffmpeg.avcodec_send_packet(_codec_ctx, &packet)) < 0)
                            {
                                throw new Exception($"Could not send video packet: {ret}"); //goto run_quit
                            }

                            ret = ffmpeg.avcodec_receive_frame(_codec_ctx, _decodingFrame);
                            if (ret == 0)
                            {
                                // a frame was received
                            }
                            else if (ret != ffmpeg.AVERROR(ffmpeg.EAGAIN))
                            {
                                ffmpeg.av_packet_unref(&packet);
                                throw new Exception($"Could not receive video frame: {ret}"); //goto run_quit;
                            }
                        }
                    }
                }
            }

            return null;
        }
    }
}

Запись - это DecodeScrCpy со считанными данными из сетевого потока. Я заметил следующее:

  • read_raw_packet вызывается снова после того, как ffmpeg.AVERROR_EOF возвращается
  • ffmpeg.avformat_open_input терпит неудачу

Вопрос в том, что я что-то пропустил?

...