Я пытаюсь получить доступ к экрану моего 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 терпит неудачу
Вопрос в том, что я что-то пропустил?