无法将libavformat / ffmpeg与x264和RTP同步

我一直在研究一些流媒体软件,它使用H.264通过网络从各种摄像机和流中获取实时信息。 为了实现这一点,我直接使用x264编码器(带有“zerolatency”预设)并提供NAL,因为它们可用于libavformat以打包到RTP(最终是RTSP)。 理想情况下,此应用程序应尽可能实时。 在大多数情况下,这一直运作良好。

不幸的是,存在某种同步问题:客户端上的任何video播放似乎都显示了一些平滑的帧,然后是短暂的暂停,然后是更多的帧; 重复。 此外,似乎有大约4秒的延迟。 我尝试过的每一个video播放器都会出现这种情况:Totem,VLC和基本的gstreamer管道。

我把它煮成了一个小小的测试用例:

#include  #include  #include  #include  #include  #include  #define WIDTH 640 #define HEIGHT 480 #define FPS 30 #define BITRATE 400000 #define RTP_ADDRESS "127.0.0.1" #define RTP_PORT 49990 struct AVFormatContext* avctx; struct x264_t* encoder; struct SwsContext* imgctx; uint8_t test = 0x80; void create_sample_picture(x264_picture_t* picture) { // create a frame to store in x264_picture_alloc(picture, X264_CSP_I420, WIDTH, HEIGHT); // fake image generation // disregard how wrong this is; just writing a quick test int strides = WIDTH / 8; uint8_t* data = malloc(WIDTH * HEIGHT * 3); memset(data, test, WIDTH * HEIGHT * 3); test = (test <> (8 - 1)); // scale the image sws_scale(imgctx, (const uint8_t* const*) &data, &strides, 0, HEIGHT, picture->img.plane, picture->img.i_stride); } int encode_frame(x264_picture_t* picture, x264_nal_t** nals) { // encode a frame x264_picture_t pic_out; int num_nals; int frame_size = x264_encoder_encode(encoder, nals, &num_nals, picture, &pic_out); // ignore bad frames if (frame_size oformat = fmt; snprintf(avctx->filename, sizeof(avctx->filename), "rtp://%s:%d", RTP_ADDRESS, RTP_PORT); if (url_fopen(&avctx->pb, avctx->filename, URL_WRONLY) codec; c->codec_id = CODEC_ID_H264; c->codec_type = AVMEDIA_TYPE_VIDEO; c->flags = CODEC_FLAG_GLOBAL_HEADER; c->width = WIDTH; c->height = HEIGHT; c->time_base.den = FPS; c->time_base.num = 1; c->gop_size = FPS; c->bit_rate = BITRATE; avctx->flags = AVFMT_FLAG_RTP_HINT; // write the header av_write_header(avctx); // make some frames for (int frame = 0; frame < 10000; frame++) { // create a sample moving frame x264_picture_t* pic = (x264_picture_t*) malloc(sizeof(x264_picture_t)); create_sample_picture(pic); // encode the frame x264_nal_t* nals; int num_nals = encode_frame(pic, &nals); if (num_nals < 0) printf("invalid frame size: %d\n", num_nals); // send out NALs for (int i = 0; i < num_nals; i++) { stream_frame(nals[i].p_payload, nals[i].i_payload); } // free up resources x264_picture_clean(pic); free(pic); // stream at approx 30 fps printf("frame %d\n", frame); usleep(33333); } return 0; } 

此测试显示白色背景上的黑线应平滑移动到左侧。 它是为ffmpeg 0.6.5编写的,但问题可以在0.80.10上重现(从我到目前为止测试过)。 我已经在error handling方面采取了一些快捷方式,以使这个例子尽可能短,同时仍然显示问题,所以请原谅一些讨厌的代码。 我还应该注意,虽然这里没有使用SDP,但我尝试使用已经有类似结果的SDP。 测试可以编译为:

 gcc -g -std=gnu99 streamtest.c -lswscale -lavformat -lx264 -lm -lpthread -o streamtest 

它可以直接用gtreamer播放:

 gst-launch udpsrc port=49990 ! application/x-rtp,payload=96,clock-rate=90000 ! rtph264depay ! decodebin ! xvimagesink 

你应该立即注意到口吃。 我在互联网上看到的一个常见“修复”是向管道添加sync = false:

 gst-launch udpsrc port=49990 ! application/x-rtp,payload=96,clock-rate=90000 ! rtph264depay ! decodebin ! xvimagesink sync=false 

这会导致播放流畅(并且接近实时),但是不是解决方案,只适用于gstreamer。 我想在源头解决问题。 我已经能够使用原始ffmpeg使用几乎相同的参数进行流式处理,并且没有任何问题:

 ffmpeg -re -i sample.mp4 -vcodec libx264 -vpre ultrafast -vpre baseline -b 400000 -an -f rtp rtp://127.0.0.1:49990 -an 

显然我做错了什么。 但它是什么?

1)您没有为发送到libx264的帧设置PTS(您可能应该看到“非严格单调PTS”警告)2)您没有为发送到libavformat的rtp muxer的数据包设置PTS / DTS(我没有100%肯定它需要设置,但我想它会更好。从源代码看起来像rtp使用PTS)。 3)恕我直言(33333)很糟糕。 它会导致编码器此时停止(增加延迟),而您可以在此期间编码下一帧,即使您仍然不需要通过rtp发送它。

PS顺便说一下你没有将param.rc.i_rc_method设置为X264_RC_ABR,所以libx264将使用CRF 23而忽略你的“param.rc.i_bitrate = BITRATE”。 在编码网络发送时使用VBV也是个好主意。