libavcodec,如何以不同的帧速率转码视频?
-
10-10-2019 - |
题
我正在通过V4L从相机中抓住视频帧,我需要以MPEG4格式对其进行转编码以通过RTP依次流式传输它们。
一切实际上都“工作”,但是在重新编码时我没有什么:输入流产生15fps,而输出为25fps,并且每个输入帧都以一个单个视频对象序列进行转换(我用简单的检查对此进行了验证在输出bitstream上)。我想接收器正确解析了mpeg4 bitstream,但RTP包装化是错误的。我应该如何在一个或多个Avpacket中将编码的Bitstream分开?也许我错过了明显的,我只需要寻找B/P框架标记,但是我认为我没有正确使用编码API。
这是我的代码的摘录,它基于可用的FFMPEG样本:
// input frame
AVFrame *picture;
// input frame color-space converted
AVFrame *planar;
// input format context, video4linux2
AVFormatContext *iFmtCtx;
// output codec context, mpeg4
AVCodecContext *oCtx;
// [ init everything ]
// ...
oCtx->time_base.num = 1;
oCtx->time_base.den = 25;
oCtx->gop_size = 10;
oCtx->max_b_frames = 1;
oCtx->bit_rate = 384000;
oCtx->pix_fmt = PIX_FMT_YUV420P;
for(;;)
{
// read frame
rdRes = av_read_frame( iFmtCtx, &pkt );
if ( rdRes >= 0 && pkt.size > 0 )
{
// decode it
iCdcCtx->reordered_opaque = pkt.pts;
int decodeRes = avcodec_decode_video2( iCdcCtx, picture, &gotPicture, &pkt );
if ( decodeRes >= 0 && gotPicture )
{
// scale / convert color space
avpicture_fill((AVPicture *)planar, planarBuf.get(), oCtx->pix_fmt, oCtx->width, oCtx->height);
sws_scale(sws, picture->data, picture->linesize, 0, iCdcCtx->height, planar->data, planar->linesize);
// encode
ByteArray encBuf( 65536 );
int encSize = avcodec_encode_video( oCtx, encBuf.get(), encBuf.size(), planar );
// this happens every GOP end
while( encSize == 0 )
encSize = avcodec_encode_video( oCtx, encBuf.get(), encBuf.size(), 0 );
// send the transcoded bitstream with the result PTS
if ( encSize > 0 )
enqueueFrame( oCtx->coded_frame->pts, encBuf.get(), encSize );
}
}
}
解决方案
最简单的解决方案是使用两个线程。第一线程将完成您的问题中概述的所有内容(解码,缩放 /颜色空间转换,编码)。部分转码的帧将写入与第二个线程共享的中间队列。该队列的最大长度将在这种特殊情况下(从低比特率转换为较高的比特率)1帧。第二个线程将是从输入队列中读取的循环帧:
void FpsConverter::ThreadProc()
{
timeBeginPeriod(1);
DWORD start_time = timeGetTime();
int frame_counter = 0;
while(!shouldFinish()) {
Frame *frame = NULL;
DWORD time_begin = timeGetTime();
ReadInputFrame(frame);
WriteToOutputQueue(frame);
DWORD time_end = timeGetTime();
DWORD next_frame_time = start_time + ++frame_counter * frame_time;
DWORD time_to_sleep = next_frame_time - time_end;
if (time_to_sleep > 0) {
Sleep(time_to_sleep);
}
}
timeEndPeriod(1);
}
当CPU功率足够且需要更高的保真度和平滑度时,您不仅可以通过某种插值来计算输出框架,而且可以通过某种插值来计算更多框架(类似于MPEG编解码器中使用的技术)。接近输出框架时间戳与输入框架时间戳记,应分配给此特定输入框架的权重越多。
不隶属于 StackOverflow