因为工作方面的需要,想单独编译出一个迷你的ffmpeg,仅用于支持rtsp至rtmp的协议转换。一开始采用的编译配置为
./configure --disable-everything --disable-x86asm --enable-protocol=rtmp --enable-demuxer=rtsp --enable-parser=h264 --enable-muxer=flv
转协议命令为
./ffmpeg -rtsp_transport tcp -i rtsp://xx.xx.xx.xx/camera_live -c:v copy -c:a copy -f flv rtmp://xx.xx.xx.xx/live/test1
结果转码报错
[NULL @ 0x1217b80] non-existing PPS 0 referenced
Last message repeated 195 times
[rtsp @ 0x1214500] Stream #0: not enough frames to estimate rate; consider increasing probesize
[rtsp @ 0x1214500] Could not find codec parameters for stream 0 (Video: h264 (High), none(progressive)): unspecified size
Consider increasing the value for the 'analyzeduration' and 'probesize' options
因为这个rtsp流在sdp中没有填写sps、pps,因此ffmpeg解复用的时候获取不到流的参数信息。而且又没有带上解码器,也无法通过解码获取码流参数。因此加上h264解码器再编译一次
./configure --disable-everything --disable-x86asm --enable-protocol=rtmp --enable-demuxer=rtsp --enable-parser=h264 --enable-decoder=h264 --enable-muxer=flv
这样改了之后,虽然可以正确获取到视频流的参数,但是在连接到srs服务器后,立马被断开了。ffmpeg的报错信息为
av_interleaved_write_frame(): Connection reset by peer
[flv @ 0xa2f880] Failed to update header with correct duration.
[flv @ 0xa2f880] Failed to update header with correct filesize.
Error writing trailer of rtmp://192.168.1.47:18092/live/hikvis: Connection reset by peer
而srs的报错信息为
[2021-04-16 18:03:06.850][Error][cycle][22026][fh669396][4] serve error code=3001 : service cycle : rtmp: stream service : rtmp: receive thread : handle publish message : rtmp: consume message : rtmp: consume video : meta update video : demux SPS/PPS : avc decode sequence header
thread [22026][fh669396]: do_cycle() [src/app/srs_app_rtmp_conn.cpp:213][errno=4]
thread [22026][fh669396]: service_cycle() [src/app/srs_app_rtmp_conn.cpp:410][errno=4]
thread [22026][fh669396]: do_publishing() [src/app/srs_app_rtmp_conn.cpp:900][errno=11]
thread [22026][fh669396]: consume() [src/app/srs_app_recv_thread.cpp:399][errno=11]
thread [22026][fh669396]: handle_publish_message() [src/app/srs_app_rtmp_conn.cpp:1003][errno=11]
thread [22026][fh669396]: process_publish_message() [src/app/srs_app_rtmp_conn.cpp:1031][errno=11]
thread [22026][fh669396]: on_video_imp() [src/app/srs_app_source.cpp:2356][errno=11]
thread [22026][fh669396]: video_avc_demux() [src/kernel/srs_kernel_codec.cpp:767][errno=11]
thread [22026][fh669396]: avc_demux_sps_pps() [src/kernel/srs_kernel_codec.cpp:794][errno=11](Interrupted system call)
对应的报错代码段为
srs_error_t SrsFormat::avc_demux_sps_pps(SrsBuffer* stream)
{
// AVCDecoderConfigurationRecord
// 5.2.4.1.1 Syntax, ISO_IEC_14496-15-AVC-format-2012.pdf, page 16
int avc_extra_size = stream->size() - stream->pos();
if (avc_extra_size > 0) {
char *copy_stream_from = stream->data() + stream->pos();
vcodec->avc_extra_data = std::vector<char>(copy_stream_from, copy_stream_from + avc_extra_size);
}
if (!stream->require(6)) {
return srs_error_new(ERROR_HLS_DECODE_ERROR, "avc decode sequence header");
}
看来是取不到AVCC。按照这个线索,去调试一下ffmpeg代码。写入avcc的代码在libavformat/flvenc.c的flv_write_codec_header
if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264
|| par->codec_id == AV_CODEC_ID_MPEG4) {
...
if (par->codec_id == AV_CODEC_ID_AAC) {
...
} else {
avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags
avio_w8(pb, 0); // AVC sequence header
avio_wb24(pb, 0); // composition time
ff_isom_write_avcc(pb, par->extradata, par->extradata_size);
}
在这里打印一下par->extradata_size,发现长度为0。说明在解复用获取参数那一步还是有问题。进一步阅读ffmpeg代码查找问题原因。在 libavformat/utils.c 的 avformat_find_stream_info中,其执行逻辑大概为
- 中间一段for (;;)的循环,不停调用 read_frame_internal,尝试获取各个stream的参数。如果从容器中获取不到,则会调用 try_decode_frame 尝试调用解码器解码获取
通过打日志,发现不完全的ffmpeg编译配置,在解码并got_picture之后,avctx->extradata_size依然为0。但是完整的ffmpeg编译,avctx->extradata_size在got_picture不为0了。进一步的调试,发现在遇到sps、pps的时候,还没进入try_decode_frame ,extradata_size已经不为0了。再看一眼代码,发现
if (!st->internal->avctx->extradata) {
ret = extract_extradata(ic, st, pkt);
if (ret < 0)
goto unref_then_goto_end;
}
/* If still no information, we try to open the codec and to
* decompress the frame. We try to avoid that in most cases as
* it takes longer and uses more memory. For MPEG-4, we need to
* decompress for QuickTime.
*
* If AV_CODEC_CAP_CHANNEL_CONF is set this will force decoding of at
* least one frame of codec data, this makes sure the codec initializes
* the channel configuration and does not only trust the values from
* the container. */
try_decode_frame(ic, st, pkt,
(options && i < orig_nb_streams) ? &options[i] : NULL);
前面还调用了一个extract_extradata,extract_extradata有个初始化步骤
f = av_bsf_get_by_name("extract_extradata");
if (!f)
goto finish;
这里需要调用一个bitstream filter extract_extradata,显然我们没有把它编译起来,所以就没有生效。顺便粗略地看了一眼 libavcodec/extract_extradata_bsf.c,大致就是针对h264、h265码流,从码流中分解出一个个nal,然后把sps、pps拷贝出来。因此我们把ffmpeg的编译调整为
./configure --disable-everything --disable-x86asm --enable-protocol=rtmp --enable-demuxer=rtsp --enable-parser=h264 --enable-decoder=h264 --enable-bsf=extract_extradata --enable-muxer=flv
就可以正常工作了
网友评论