近日自己用下載的ffmpeg-3.1.4代碼自己編譯來用,沒想到會碰到這么一下低級坑。
我用自己的編譯出來的庫總是會在用rtsp上傳視頻時崩掉,起初我還以為自己編譯的x264出問題,因為我是繞開使用pkg-config,手動修改了configure文件。但是不關事,我重新解壓代碼編譯不帶其它三方編碼庫的版本,也發生同樣的結果。
結果是我用這份代碼包編譯出來的庫,只能用rtsp作為輸入卻不能用作輸出。沒其它更加好的辦法,只好看代碼+gdb調試之。
過程不多說了,時間時間還是時間,需要很多時間。
程序在和流服務器完成了OPTION,ANNOUNCE,SETUP,SETUP,RECORD的rtsp常規五步后,在一個UDP包也未曾前發出崩掉,死在rtpenc.c里
rtp_write_packet Program received signal SIGSEGV, Segmentation fault. rtp_write_packet (s1=0x2ca28c0, pkt=0x22d548) at libavformat/rtpenc.c:524 524 rtcp_bytes = ((s->octet_count - s->last_octet_count) * RTCP_TX_RATIO_NUM) /
原因是rtpmuxer的context(AVFormatContext,category為MUXER,outputFormat的classname是"rtp output"),它的priv_data為0, 這個priv_data原本應該是一個RTPMuxContext,但卻是0。這就好辦啦,原以為只要在代碼中搜索到這個RTPMuxContext的構建之處就開展偵測。但這是純c項目,而且還是個priv_data,近似union多種解釋。
沒辦法只好由這個priv_data往上一點一點追索整個關系鏈。鎖定十幾個角色(對象)和幾十處操作。

過程省略。
原來以為是沒有對priv_data進行構建,最后偵測到priv_data在某處進行構建,卻在另一處被改成了0。原來如此,但不對,這項目的代碼被全世界的人使用過,可能會出現這么滑稽低級的問題嗎。
但真的出現了。

插入說明,一般InputFormat對應是demuxer,outputFormat對應是muxer。一個解碼為主,另一個編碼為主。
RTSPStream::transport_priv在muxer分支中是一個AVFormatContext,這個context的priv_data就是RTPMuxContext的指針。
RTSPStream::transport_priv在demuxer分支是一個RTPDEMUXContext,而這個context的ssrc位置正是上面行context的priv_data。
就這樣弄好的muxer context被作為demuxer而慘被亂寫。
找來較前的版本2.7.7進行比較。位置在libavformat/rtsp.c,函數int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st):
int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st)
{
RTSPState *rt = s->priv_data;
AVStream *st = NULL;
int reordering_queue_size = rt->reordering_queue_size;
if (reordering_queue_size < 0) {
if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP || !s->max_delay)
reordering_queue_size = 0;
else
reordering_queue_size = RTP_REORDER_QUEUE_DEFAULT_SIZE;
}
/* open the RTP context */
if (rtsp_st->stream_index >= 0)
st = s->streams[rtsp_st->stream_index];
if (!st)
s->ctx_flags |= AVFMTCTX_NOHEADER;
if (CONFIG_RTSP_MUXER && s->oformat && st) {
// !!!!!!!!!!
// 注意這里rtsp_st->transport_priv是作為AVFormatContext,進行Mux操作
// 伏筆 1
//
int ret = ff_rtp_chain_mux_open((AVFormatContext **)&rtsp_st->transport_priv,
s, st, rtsp_st->rtp_handle,
RTSP_TCP_MAX_PACKET_SIZE,
rtsp_st->stream_index);
/* Ownership of rtp_handle is passed to the rtp mux context */
rtsp_st->rtp_handle = NULL;
if (ret < 0)
return ret;
st->time_base = ((AVFormatContext*)rtsp_st->transport_priv)->streams[0]->time_base;
// !!!!!!!!!!!!
// 即使已經作為Mux打開,也不返回,依舊順流而下。
// 伏筆 2
//
} else if (rt->transport == RTSP_TRANSPORT_RAW) {
return 0; // Don't need to open any parser here
} else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT && st)
rtsp_st->transport_priv = ff_rdt_parse_open(s, st->index,
rtsp_st->dynamic_protocol_context,
rtsp_st->dynamic_handler);
else if (CONFIG_RTPDEC)
rtsp_st->transport_priv = ff_rtp_parse_open(s, st,
rtsp_st->sdp_payload_type,
reordering_queue_size);
if (!rtsp_st->transport_priv) {
return AVERROR(ENOMEM);
} else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RTP) {
// !!!!!!!!!!!!!!!
// 舊版本被打開過的Mux隨控制流自然流入這里沒有問題,因為沒有作其它操作
//
// 但是 !!!!!!!!!
// 3.14版本 !!!!
// 加入下面兩句,BUG就來了
//
// RTPDemuxContext* rtpctx = rtsp_st->transport_priv;
// rtpctx->ssrc = rtsp_st->ssrc;
// ^ !!!!!! 上面這句等於將AVFormatContext的priv_data賦值。
//
if (rtsp_st->dynamic_handler) {
ff_rtp_parse_set_dynamic_protocol(rtsp_st->transport_priv,
rtsp_st->dynamic_protocol_context,
rtsp_st->dynamic_handler);
}
if (rtsp_st->crypto_suite[0])
ff_rtp_parse_set_crypto(rtsp_st->transport_priv,
rtsp_st->crypto_suite,
rtsp_st->crypto_params);
}
return 0;
}
3.1.4版本加入了兩行代碼引入了BUG。
解決方法也就是腳痛醫腳手痛醫手,在3.1.4版本增加的兩行代碼前加oformat判斷,一般地outputFormat為Muxer而inputFormat為Demuxer。
