Как передавать кадры один за другим и захватывать декодированные кадры с помощью LibAV?C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Как передавать кадры один за другим и захватывать декодированные кадры с помощью LibAV?

Сообщение Anonymous »

Я интегрирую стороннюю систему, в которой отправляю TCP-пакет, например, с запросом. 5 кадров из потока прямой трансляции с камеры видеонаблюдения, и она отправляет эти 5 кадров один за другим. Каждый пакет представляет собой кадр в кодировке h264, обернутый некоторыми соответствующими данными.
Мне нужно написать код с использованием Libav, где я могу:
  • Передавайте кадры один за другим, используя AVIOContext (или что-то подобное).
  • Захватите декодированный кадр.
  • Нарисуйте его в окне ( Не имеет отношения к вопросу, пишу для контекста).
Я сделал то же самое с GStreamer, создав такой конвейер:

Код: Выделить всё

AppSrc -> H264Parser -> H264Decoder -> FrameGrabber
Приведенный ниже код — это то, что мне удалось написать на данный момент:

Код: Выделить всё

using System.Runtime.InteropServices;
using FFmpeg.AutoGen;

namespace AvIoCtxExperiment;

public static unsafe class Program
{
public static void Main()
{
ffmpeg.avdevice_register_all();
Console.WriteLine($"FFmpeg v: {ffmpeg.av_version_info()}");

// Generous buffer size for I frames (~43KB)
const int bufferSize = 50 * 1024;

var buff = (byte*)ffmpeg.av_malloc(bufferSize);
if (buff == null)
throw new Exception("Buffer is null");

// A mock frame provider. Frames are stored in separate files and this reads & returns them one-by-one.
var frameProvider = new FrameProvider(@"D:\Frames-1", 700);
var gch = GCHandle.Alloc(frameProvider);

var avioCtx = ffmpeg.avio_alloc_context(
buffer: buff,
buffer_size: bufferSize,
write_flag: 0,
opaque: (void*)GCHandle.ToIntPtr(gch),
read_packet: new avio_alloc_context_read_packet(ReadFunc),
write_packet: null,
seek: null);

var formatContext = ffmpeg.avformat_alloc_context();
formatContext->pb = avioCtx;
formatContext->flags |= ffmpeg.AVFMT_FLAG_CUSTOM_IO;

var openResult = ffmpeg.avformat_open_input(&formatContext, null, null, null);
if (openResult < 0)
throw new Exception("Open Input Failed");

if (ffmpeg.avformat_find_stream_info(formatContext, null) < 0)
throw new Exception("Find StreamInfo Failed");

AVPacket packet;
while (ffmpeg.av_read_frame(formatContext, &packet) >= 0)
{
Console.WriteLine($"GRAB: {packet.buf->size}");
ffmpeg.av_packet_unref(&packet);
}
}

private static int ReadFunc(void* opaque, byte* buf, int bufSize)
{
var frameProvider = (FrameProvider?)GCHandle.FromIntPtr((IntPtr)opaque).Target;
if (frameProvider == null)
{
return 0;
}

byte[] managedBuffer = new byte[bufSize];

var fBuff = frameProvider.NextFrame();
if (fBuff == null)
{
return ffmpeg.AVERROR_EOF;
}

int bytesRead = fBuff.Length;
fBuff.CopyTo(managedBuffer, 0);

if (bytesRead > 0)
{
Marshal.Copy(managedBuffer, 0, (IntPtr)buf, bytesRead);
Console.WriteLine($"READ size: {fBuff.Length}");
return bytesRead;
}

return ffmpeg.AVERROR_EOF;
}
}
Второе, что меня смущает, это то, что с AppSrc я могу передать кадр любого размера, но с LibAV он запрашивает размер буфера. В AppSrc я отвечаю за подачу кадров в конвейер. GStreamer уведомляет меня, когда сигналов достаточно. Но в libav он сам вызывает делегат read_packet.
Любая помощь будет очень признательна. Спасибо.
P.S. Я пишу приложение на C#, но пример кода на C подойдет, я могу адаптировать код самостоятельно.

Подробнее здесь: https://stackoverflow.com/questions/789 ... sing-libav
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»