Unity: преобразование Texture2D в YUV420P с помощью FFmpeg - PullRequest
0 голосов
/ 10 июня 2018

Я пытаюсь создать игру в Unity, где каждый кадр визуализируется в текстуру, а затем объединяется в видео с помощью FFmpeg.Вывод, созданный FFmpeg, в конечном итоге должен быть отправлен по сети на пользовательский интерфейс клиента.Тем не менее, я борюсь в основном с той частью, где кадр перехватывается и передается небезопасному методу в виде байтового массива, где он должен обрабатываться в дальнейшем FFmpeg.Я использую оболочку FFmpeg.AutoGen .

Метод рендеринга в текстуру:

private IEnumerator CaptureFrame()
{
    yield return new WaitForEndOfFrame();

    RenderTexture.active = rt;
    frame.ReadPixels(rect, 0, 0);
    frame.Apply();

    bytes = frame.GetRawTextureData();

    EncodeAndWrite(bytes, bytes.Length);
}

На данный момент небезопасный метод кодирования:

private unsafe void EncodeAndWrite(byte[] bytes, int size)
{
    GCHandle pinned = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    IntPtr address = pinned.AddrOfPinnedObject();

    sbyte** inData = (sbyte**)address;
    fixed(int* lineSize = new int[1])
    {
        lineSize[0] = 4 * textureWidth;
        // Convert RGBA to YUV420P
        ffmpeg.sws_scale(sws, inData, lineSize, 0, codecContext->width, inputFrame->extended_data, inputFrame->linesize);
    }

    inputFrame->pts = frameCounter++;

    if(ffmpeg.avcodec_send_frame(codecContext, inputFrame) < 0)
        throw new ApplicationException("Error sending a frame for encoding!");

    pkt = new AVPacket();
    fixed(AVPacket* packet = &pkt)
        ffmpeg.av_init_packet(packet);
    pkt.data = null;
    pkt.size = 0;

    pinned.Free();
    ...
}

sws_scale принимает sbyte** в качестве второго параметра, поэтому я пытаюсь преобразовать входной байтовый массив в sbyte**, сначала закрепив его GCHandle и выполнив явное преобразование типов впоследствии.Однако я не знаю, правильно ли это.

Более того, условие if(ffmpeg.avcodec_send_frame(codecContext, inputFrame) < 0) всегда вызывает исключение ApplicationException, где я также действительно не знаю, почему это происходит.codecContext и inputFrame являются моими объектами AVCodecContext и AVFrame, соответственно, и поля определены следующим образом:

codecContext

codecContext = ffmpeg.avcodec_alloc_context3(codec);
codecContext->bit_rate = 400000;
codecContext->width = textureWidth;
codecContext->height = textureHeight;

AVRational timeBase = new AVRational();
timeBase.num = 1;
timeBase.den = (int)fps;
codecContext->time_base = timeBase;
videoAVStream->time_base = timeBase;

AVRational frameRate = new AVRational();
frameRate.num = (int)fps;
frameRate.den = 1;
codecContext->framerate = frameRate;

codecContext->gop_size = 10;
codecContext->max_b_frames = 1;
codecContext->pix_fmt = AVPixelFormat.AV_PIX_FMT_YUV420P;

inputFrame

inputFrame = ffmpeg.av_frame_alloc();
inputFrame->format = (int)codecContext->pix_fmt;
inputFrame->width = textureWidth;
inputFrame->height = textureHeight;
inputFrame->linesize[0] = inputFrame->width;

Любая помощь в устранении проблемы будет принята с благодарностью:)

1 Ответ

0 голосов
/ 11 июня 2018

Проверьте примеры здесь: https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples

Особенно scaling_video.c.В FFmpeg масштабирование и преобразование формата пикселей - это одна и та же операция (сохраняйте параметры размера одинаковыми только для преобразования формата пикселя).

Эти примеры очень просты для следования.Попробуйте.

...