ffmpeg/VLC連接rtmp視頻斷開的一個原因

問題

這幾天解決了一個問題。問題是這樣的,用ffmpeg向我做的rtmp server請求rtmp視頻,99%的情況視頻會在10秒鐘內斷開。ffmpeg會報一個mismatch的錯誤。打印是這樣的:RTMP packet size mismatch %d != %d。在ffmpeg代碼中是在rtmp_packet_read_one_chunk接口中,前後兩包所在幀的尺寸不匹配,需要斷開。

 

原因

發送的數據有一部分丟失,導致ffmpeg收到的幀有時候會不完整,ffmpeg校驗非常嚴格,當發現有一個幀的數據不完整的時候就斷開連接,就出現了連接幾秒鐘就斷開的現象。

 

解決辦法

確保發出去的每一幀都是完整的,不允許出現有一幀的部分數據沒發送成功,接下來發送的數據是下一包或下一幀的數據的情況出現。網絡不好的時候可以一幀一幀的丟棄數據,但絕不能一包一包的丟數據。

 

分析

1、ffmpeg的接收接口我找了很久,最後發現其實很簡單,就是簡單的recv()來實現,是下面這個接口。

static int tcp_read(URLContext *h, uint8_t *buf, int size)
{
    TCPContext *s = h->priv_data;
    int ret;

    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
        ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
        if (ret)
            return ret;
    }
    ret = recv(s->fd, buf, size, 0);
    if (ret == 0)
        return AVERROR_EOF;
    return ret < 0 ? ff_neterrno() : ret;
}

2、整個rtmp的數據接收,拼裝,和組合是下面這個接口實現的。在printtttttttttt那個地方我進行過打印比較,把接收到的數據和我server端發送的數據進行比較,最後發現是數據錯誤就是在這裏發現的。爲了進一步確認,還和wireshark抓包數據進行了比較,最終定位的原因。

static int rtmp_read(URLContext *s, uint8_t *buf, int size)
{
    RTMPContext *rt = s->priv_data;
    int orig_size = size;
    int ret;
	//printf("\n\n\n\n\n\n%s %d size=%d filename=%s max_packet_size=%d\n",__FUNCTION__,__LINE__,size,s->filename,s->max_packet_size);
    while (size > 0) {
        int data_left = rt->flv_size - rt->flv_off;
		//printf("%s %d %d - %d = %d\n\n",__FUNCTION__,__LINE__,rt->flv_size,rt->flv_off,data_left);
        if (data_left >= size) {
            memcpy(buf, rt->flv_data + rt->flv_off, size);
            rt->flv_off += size;
			//printf("%s %d here orig_size=%d\n",__FUNCTION__,__LINE__,orig_size);
            return orig_size;
        }
        if (data_left > 0) {
            memcpy(buf, rt->flv_data + rt->flv_off, data_left);
			//printtttttttttt
            buf  += data_left;
            size -= data_left;
            rt->flv_off = rt->flv_size;
			//printf("%s %d here data_left=%d\n",__FUNCTION__,__LINE__,data_left);
            return data_left;
        }
		//printf("%s %d here\n",__FUNCTION__,__LINE__);
        if ((ret = get_packet(s, 0)) < 0){
			//printf("%s %d here ret=%d\n",__FUNCTION__,__LINE__,ret);
			return ret;
		}
    }
    return orig_size;
}

3、爲什麼我這裏會出現發送不完整的情況呢?爲了消除各模塊之間的相互影響,這裏的socket必須是非阻塞狀態,send後必須馬上返回,再加上未知原因導致ffmpeg接收的不是特別特別的快,導致發送緩衝區經常出現滿的狀態,所以就出現了這個問題。如果是阻塞socket就不會有這個問題。

 

最後

倉促之間完成這篇文章,關鍵地方都已經指出,ffmpeg裏面的具體邏輯就不說了,抓住我上面提到的2個接口就足夠分析整個ffmpeg對簡單rtmp協議解析的過程。ffmpeg是一個嚴格的工具,不是爲了應用使用,而是爲了發現我們邏輯問題而存在的。flash纔是爲了應用,它就可以兼容數據不全的問題,不影響畫面。ffmpeg要求每一幀必須是完整的,一個字節都不能差。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章