【发布时间】:2018-11-19 22:48:14
【问题描述】:
我正在尝试在 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_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 = ffmpeg.av_frame_alloc();
inputFrame->format = (int)codecContext->pix_fmt;
inputFrame->width = textureWidth;
inputFrame->height = textureHeight;
inputFrame->linesize[0] = inputFrame->width;
任何解决问题的帮助将不胜感激:)
【问题讨论】:
-
如果我可能会问,您是如何将 FFmpeg.AutoGen 包装器合并到您的统一项目中的?尝试做与您当前在问题中提出的问题类似的事情。
-
@a5t 自从我在 Unity 中使用 FFmpeg 已经很长时间了,但我认为如果你将 FFmpeg.AutoGen.dll 放入 Plugins 文件夹中,它应该可以正常工作。