天天看點

ffmpeg rtp傳輸使用

1 ffmpeg rtp

   ffmpeg估計已經成為多媒體 流媒體開發的必備工具,編解碼不可缺少的夥伴,rtp協定本身是非常優良的協定,剛進入協定程式設計的夥伴可以使用ffmpeg的rtp程式設計來入門,抓包,這裡是示例,同時講一下原理。

2 簡單使用

2.1 包含頭檔案

别忘了加上extern “C”,ffmpeg是純c程式

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
           

2.2 指令行

看指令

//推RTP

//ffmpeg - re - i cw.ts - vcodec copy - acodec copy - f rtp_mpegts rtp://238.123.46.66:8001 推rtp

//推UDP

//ffmpeg - re - i cw.ts - vcodec copy - acodec copy - f mpegts udp://238.123.46.66:8001 推udp

2.3 查找編碼器

if (v != NULL)
{
	//查找編碼器
	AVCodec *cv = avcodec_find_encoder(AV_CODEC_ID_H264);
	//建立流
	m_st_v = avformat_new_stream(m_fmtctx, cv);
	//從context拷貝codecpar
	avcodec_parameters_from_context(m_st_v->codecpar, v);
}
           

2.4 設定參數

   RTP 協定中,最需要設定的是payload和ssrc,這是唯一指定一路流的辨別,除了源端口和源IP,ssrc是真正指明自己是哪一路的,DHCP或者移動網絡中,IP位址和端口号可能會變化,不能使用這種方式來指明唯一的一路流,實際上,應該使用信令伺服器配合sdp協定來擷取ssrc和其他參數,sip協定和rtsp協定等都是信令的作用。

   為什麼要自己設定ssrc和payloadtype?你可以根據伺服器傳回來設定這些值,尤其是經過解析sdp協定來拿到payload,不然僅僅是一個值,怎麼可能知道到底是什麼編碼,是以sdp協定名字叫做會話描述協定。

av_opt_set_int(m_fmtctx->priv_data, "payload_type", 96, 0);
	av_opt_set_int(m_fmtctx->priv_data, "ssrc", ssrc, 0);
           

2.4 發送

   調整時間戳發送,pts和dts分别意義為呈現時間戳 – present 和解碼時間戳–decode,實際上,這個時間戳不要較真,不用拿着一知半解的函數算來算去,記住這個時間戳就是真實的時間而已,假定有一個靜音檢測,一段時間沒有聲音,千萬别按照很多例子,依然按照死的公式計算時間

current = 目前時間 time[n] 表示第n幀時間

time[1] = 第一幀時間

time[2] = current - time[1]

   其實pts、dts可以放置真正的目前時間,本來就是指明目前幀的時間的,播放器應該拿到第一幀關鍵時間戳後計算後面相對時間就行。就這麼簡單 ,不要被眼花缭亂的時間計算公式給蒙了,但是這裡要說明RTP時間如視訊發送出去實際上是要使用90000基數的,為什麼要用這個基數呢?因為計算幀數涉及到整除的問題,這個90000是恰到好處而已。

void send(AVPacket * pkt)
	{
		//寫檔案頭
		pkt->stream_index = m_st_v->index;
		pkt->pts = av_rescale_q(pkt->pts, m_p_v->time_base, m_st_v->time_base);
		pkt->dts = av_rescale_q(pkt->dts, m_p_v->time_base, m_st_v->time_base);
		pkt->pos = -1;
		av_interleaved_write_frame(m_fmtctx, pkt);
		av_packet_free(&pkt);
	}
           

3 完整class,實踐出真知

#pragma once
#if 1
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
#define HASH_PRIME_MIDDLE 10001531
static int hash_add(const char* key, int prime)
{
	int hash, i;
	int len = (int)strlen(key);
	for (hash = len, i = 0; i < len; i++)
		hash += key[i];
	return (hash % prime);
}
class RTP_Wrapper
{
	AVFormatContext * m_fmtctx = NULL;
	AVStream * m_st_v = NULL;
	AVStream * m_st_a = NULL;
	AVCodecContext *m_p_v = NULL;
	AVCodecContext *m_p_a = NULL;
public:
	int init_rtp(AVCodecContext *v, AVCodecContext *a, const char * url)
	{
		int ret = -1;
		m_p_v = v;
		m_p_a = a;
		char host[128];
		int port = 0;
		char path[128];
		sscanf(url, "rtp://%[^:/]:%d/%s", host, &port, path);
		if (port == 0)
		{
			sscanf(url, "rtp://%[^:/]/%s", host, path);
			port = 6000;
		}
		int ssrc = hash_add(path, HASH_PRIME_MIDDLE);

		AVOutputFormat* fmt = av_guess_format("rtp", NULL, NULL);
		//AVOutputFormat* fmt = av_guess_format("rtp_mpegts", NULL, NULL);
		char rtp[128];
		sprintf(rtp, "rtp://%s:%d", host, port);
		ret = avformat_alloc_output_context2(&m_fmtctx, fmt, fmt->name,
			rtp);
		AVDictionary *options = NULL;
		av_dict_set(&options, "buffer_size", "102400", 0); //設定緩存大小,1080p可将值調大
		av_dict_set(&options, "send_buffer_size", "102400", 0); //設定緩存大小,1080p可将值調大
		av_dict_set(&options, "pkt_size", "1400", 0);
		//96 h264
		av_opt_set_int(m_fmtctx->priv_data, "payload_type", 96, 0);
		av_opt_set_int(m_fmtctx->priv_data, "ssrc", ssrc, 0);
		avio_open2(&m_fmtctx->pb, rtp,
			AVIO_FLAG_WRITE, NULL, &options);
		//初始化AVStream
		if (v != NULL)
		{
			//查找編碼器
			AVCodec *cv = avcodec_find_encoder(AV_CODEC_ID_H264);
			m_st_v = avformat_new_stream(m_fmtctx, cv);
			avcodec_parameters_from_context(m_st_v->codecpar, v);
		}
		if (a != NULL)
		{
			AVCodec *ca = avcodec_find_encoder(AV_CODEC_ID_AAC);
			m_st_a = avformat_new_stream(m_fmtctx, ca);
			avcodec_parameters_from_context(m_st_a->codecpar, a);
		}
		avformat_write_header(m_fmtctx, NULL);
		char sdp[256];
		av_sdp_create(&m_fmtctx, 1, sdp, sizeof(sdp));
		//FILE * fp = fopen("c:\\test.sdp", "wb");
		//fwrite(sdp, 1, strlen(sdp), fp);
		//fclose(fp);
		printf("%s\n", sdp);
		return 0;
	}
	//設定AVCodecContext編碼參數    
	void send(AVPacket * pkt)
	{
		//寫檔案頭
		pkt->stream_index = m_st_v->index;
		pkt->pts = av_rescale_q(pkt->pts, m_p_v->time_base, m_st_v->time_base);
		pkt->dts = av_rescale_q(pkt->dts, m_p_v->time_base, m_st_v->time_base);
		pkt->pos = -1;
		av_interleaved_write_frame(m_fmtctx, pkt);
		av_packet_free(&pkt);

	}
	void UnInit()
	{
		//銷毀資源
		if (!(m_fmtctx->oformat->flags & AVFMT_NOFILE))
			avio_close(m_fmtctx->pb);
		avformat_free_context(m_fmtctx);
	}
};
#endif
           

後面會繼續寫手動發送和接收rtp包

繼續閱讀