Unity:使用FFmpeg

时间:2018-06-10 14:57:15

标签: unity3d ffmpeg texture2d

我试图在Unity中创建一个游戏,其中每个帧都渲染成纹理,然后使用FFmpeg将它们组合成一个视频。 FFmpeg创建的输出最终应通过网络发送到客户端UI。但是,我主要是抓住一个帧的部分,并作为一个字节数组传递给一个不安全的方法,它应该由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_scalesbyte**作为第二个参数,因此我首先尝试将输入字节数组转换为sbyte**,然后先将其GCHandle固定为if(ffmpeg.avcodec_send_frame(codecContext, inputFrame) < 0)。之后进行显式类型转换。但是,我不知道这是否是正确的方法。

此外,条件codecContext alwasy抛出一个ApplicationException,我也真的不知道为什么会这样。 inputFramecodecContext = 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; 分别是我的AVCodecContext和AVFrame对象,字段定义如下:

编解码器环境

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

inputFrame

from tkinter import *
from threading import Thread

def window():
        root = Tk()
        Window=Frame(root)
        Window.pack(side=TOP)

        label = Label(Window, text="Window").grid()

        root.mainloop()
def loop():
        label.configure(text="Something else")

if __name__ == '__main__':
        Thread(target = window).start()
        Thread(target = loop).start()

非常感谢任何帮助解决问题的方法:)

2 个答案:

答案 0 :(得分:1)

点击此处的示例:https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples

特别是scaling_video.c。在FFmpeg缩放和像素格式转换是相同的操作(保持大小参数只是像素格式转换)。

这些例子非常容易理解。试一试。

答案 1 :(得分:0)

我认为您的演员阵容不正确dragOptions: { cursor: 'pointer', zIndex: 2000, grid: [20, 20], containment: 'notNegative' } 因为地址是 IntPtr 对象,所以正确的转换可能应该是 sbyte** inData = (sbyte**)address;