近日自己用下載的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。