现在的位置: 首页 > 综合 > 正文

ortp学习笔记

2013年10月11日 ⁄ 综合 ⁄ 共 12454字 ⁄ 字号 评论关闭

ortp版本:ortp-0.18.0.tar.gz  

操作系统:window 7 32bit

1.windows下编译ortp.lib
直接打开ortp-0.18.0\build\win32native的工程文件即可,VS2008下无需任何修改,即可编译出动态链接库 ortp.lib以及ortp.dll。

2.使用ortp提供的测试程序:ortp-0.18.0\src\tests下的win_receive以及win_sender目录下程序
在windows7下使用win_receiver时会提示下如下错误,这个在错误在windows XP下是不存在的
QOSAddSocketToFlow failed to add a flow with error 87
具体问题暂时还没有深究,这个应该是一个系统兼容性问题,需要系统支持qwave.lib。
在查找了错误出处,对比了前几个ortp的版本后,对ortp0.18的源代码进行了修改
rtp_session_inet.c
/* set socket options (but don't change chosen states) */
/*
          rtp_session_set_dscp( session, -1 );
          rtp_session_set_multicast_ttl( session, -1 );
          rtp_session_set_multicast_loopback( session, -1 );
*/

3. 发送H.264视频帧
ortp提供的win_receive以及win_sender一次只发送160字节的数据。但我们在发送一帧H.264视频帧,每次需要发送2000-3000字节的数据,关于ortp发送H.264视频帧,可以参考:
除了上面两篇文章提到需要注意的负载类型和时间戳之外,在ortp中win_receive中还需要显示调用:
rtp_session_set_recv_buf_size(rtp_session_mgr .rtp_session , recv_bufsize);
这里的recv_bufsize必须要比win_sender中
sended_bytes = rtp_session_send_with_ts (rtp_session_mgr .rtp_session ,
                          ( uint8_t *) send_buffer,
                           wrapLen,
                          rtp_session_mgr.cur_timestamp );
的wrapLen要大,否则在receive端会出现如下错误:
ortp-warning-Error receiving RTP packet: Error code : 10040, err num [10040],error [-1]


4. 一个结合H.264编码,rtp发送,接收,保存mp4文件的小程序
编译环境:vs2008
压缩文件中包括了ffmpeg以及ortp的动态链接库,但你还需要在工程文件中修改它们的路径才能正确链接这两个库.
因为这个小程序是一个项目的一部分,源代码中还有些冗余代码,并没有来的及删掉,大家在看的时候需要注意下

receiver
#include <string.h>
#include "ortp/ortp.h"

extern "C"{
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
};

bool m_bExit = FALSE;

struct RtpSessionMgr
{
	RtpSession *rtp_session;
	int timestamp;
};


RtpSessionMgr rtp_session_mgr;
const int timestamp_inc = 3600; // 90000/25

const char recv_ip[] = "127.0.0.1";
const int recv_port = 8008;
const int recv_bufsize = 10240;
unsigned char *recv_buf;

/** 帧包头的标识长度 */ 
#define CMD_HEADER_LEN 10

/** 帧包头的定义 */
static uint8_t CMD_HEADER_STR[CMD_HEADER_LEN] = { 0xAA,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xFF };

/** 帧的包头信息 */
typedef struct _sFrameHeader
{
	/** 命令名称标识 */ 
	unsigned char cmdHeader[CMD_HEADER_LEN];

	/** 采集的通道号 0~7*/
	unsigned char chId;

	/** 数据类型,音频 或者 视频*/
	unsigned char dataType; 

	/** 缓冲区的数据长度 */ 
	uint32_t len;

	/** 时间戳 */
	uint32_t timestamp;

}FrameHeader;

///////ffmpeg///////////////////////////////////////////////////////////////////
AVOutputFormat *fmt;
AVFormatContext *oc;
AVStream *video_st;
AVCodecContext *codecContext;

const int image_width = 704;
const int image_height = 576;
const int frame_rate = 25;
static int frame_count;

BOOL ctrlHandlerFunction(DWORD fdwCtrlType) 
{ 
	switch (fdwCtrlType) 
	{ 
		// Handle the CTRL+C signal. 
		// CTRL+CLOSE: confirm that the user wants to exit. 
	case CTRL_C_EVENT: 
	case CTRL_CLOSE_EVENT: 
	case CTRL_BREAK_EVENT: 
	case CTRL_LOGOFF_EVENT: 
	case CTRL_SHUTDOWN_EVENT: 
		m_bExit = TRUE;
		return TRUE; 

	default: 
		return FALSE; 
	} 
} 

void rtpInit()
{
	int ret;
	WSADATA wsaData;

	/** 初始化winsocket */ 
	if ( WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
	{
		fprintf(stderr, "WAStartup failed!\n");
		return ;
	}

	ortp_init();
	ortp_scheduler_init();

	rtp_session_mgr.rtp_session = rtp_session_new(RTP_SESSION_RECVONLY);

	rtp_session_set_scheduling_mode(rtp_session_mgr.rtp_session, 1);
	rtp_session_set_blocking_mode(rtp_session_mgr.rtp_session, 1);
	rtp_session_set_local_addr(rtp_session_mgr.rtp_session, recv_ip, recv_port);

	rtp_session_enable_adaptive_jitter_compensation(rtp_session_mgr.rtp_session, TRUE);
	rtp_session_set_jitter_compensation(rtp_session_mgr.rtp_session, 40);

	rtp_session_set_payload_type(rtp_session_mgr.rtp_session, 34);
	rtp_session_set_recv_buf_size(rtp_session_mgr.rtp_session, recv_bufsize);

	rtp_session_mgr.timestamp = timestamp_inc;
}

int rtp2disk()
{
	int err;
	int havemore = 1;

	while (havemore)
	{
		err = rtp_session_recv_with_ts(rtp_session_mgr.rtp_session, 
										(uint8_t *)recv_buf, recv_bufsize, 
										rtp_session_mgr.timestamp, &havemore);

		if (havemore) 
			printf("==> Warning: havemore=1!\n");

		if (err > 0)
		{
			FrameHeader *frameHeader;

			printf("receive data is %d\n", err);
			
			frameHeader = (FrameHeader *)recv_buf;
			printf("frame_len = %d\n", frameHeader->len);

			AVPacket pkt;
			av_init_packet(&pkt);
			pkt.stream_index= video_st->index;
			pkt.data= recv_buf + sizeof(FrameHeader);
			pkt.size = frameHeader->len; // not the video_outbuf_size, note!
			// write the compressed frame in the media file
			err = av_write_frame(oc, &pkt);

			if (err != 0)
			{
				printf("av_write_frame failed\n");
			}
		}
	}

	return 0;
}

AVCodecContext* createCodecContext(AVFormatContext *oc)
{
	AVCodecContext *video_cc = avcodec_alloc_context();

	video_cc =  avcodec_alloc_context();
	if (!video_cc)
	{
		fprintf(stderr, "alloc avcodec context failed\n");
		exit(1);
	}

	video_cc->codec_id = (CodecID)CODEC_ID_H264;
	video_cc->codec_type = AVMEDIA_TYPE_VIDEO;

	video_cc->me_range = 16;  
	video_cc->max_qdiff = 4;  
	video_cc->qmin = 10;  
	video_cc->qmax = 51;  
	video_cc->qcompress = 0.6f;  

	/* put sample parameters */
	video_cc->bit_rate = 400000;

	/* resolution must be a multiple of two */
	video_cc->width = image_width;
	video_cc->height = image_height;

	/* time base: this is the fundamental unit of time (in seconds) in terms
	of which frame timestamps are represented. for fixed-fps content,
	timebase should be 1/framerate and timestamp increments should be
	identically 1. */
	video_cc->time_base.den = frame_rate;
	video_cc->time_base.num = 1;

	video_cc->gop_size = 12; /* emit one intra frame every twelve frames at most */
	video_cc->pix_fmt = PIX_FMT_YUV420P;

	// some formats want stream headers to be separate
	if(!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp"))
		video_cc->flags |= CODEC_FLAG_GLOBAL_HEADER;

	return video_cc;
}

void openVideo(AVFormatContext *oc)
{
	AVCodec *codec;

	/* find the video encoder */
	codec = avcodec_find_encoder(codecContext->codec_id);
	if (!codec) {
		fprintf(stderr, "codec not found\n");
		exit(1);
	}

	/* open the codec */
	if (avcodec_open(codecContext, codec) < 0) {
		fprintf(stderr, "could not open video codec\n");
		exit(1);
	}
}

void ffmpegEncodeInit()
{
	// initialize libavcodec, and register all codecs and formats
	av_register_all();

	char filename[] = "test.mp4";
	fmt = av_guess_format(NULL, filename, NULL);

	oc = avformat_alloc_context();
	oc->oformat = fmt;

	fmt->video_codec = (CodecID) CODEC_ID_H264;
	_snprintf(oc->filename, sizeof(oc->filename), "%s", filename);

	// add the video streams using the default format codecs and initialize the codecs
	video_st = NULL;
	if (fmt->video_codec != CODEC_ID_NONE) {
		video_st = av_new_stream(oc, 0);
		if (!video_st) {
			fprintf(stderr, "Could not alloc stream\n");
			exit(1);
		}
	}

	// alloc codecContext
	codecContext = createCodecContext(oc);
	video_st->codec = codecContext;

	// set the output parameters (must be done even if no parameters).
	if (av_set_parameters(oc, NULL) < 0) {
		fprintf(stderr, "Invalid output format parameters\n");
		exit(1);
	}

	dump_format(oc, 0, filename, 1);

	/* now that all the parameters are set, we can open the audio and
	video codecs and allocate the necessary encode buffers */
	if (codecContext)
		openVideo(oc);

	// open the output file, if needed
	if (!(fmt->flags & AVFMT_NOFILE)) {
		if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) {
			fprintf(stderr, "Could not open '%s'\n", filename);
			exit(1);
		}
	}

	// write the stream header, if any
	av_write_header(oc);
}

void ffmpegEncodeClose()
{
	int i;

	/* close each codec */
	if (video_st)
		avcodec_close(video_st->codec);

	// write the trailer, if any
	av_write_trailer(oc);

	/* free the streams */
	for(i = 0; i < oc->nb_streams; i++) {
		av_freep(&oc->streams[i]->codec);
		av_freep(&oc->streams[i]);
	}

	if (!(fmt->flags & AVFMT_NOFILE)) {
		/* close the output file */
		url_fclose(oc->pb);
	}

	/* free the stream */
	av_free(oc);
}

int main()
{
	recv_buf = (uint8_t *)malloc(recv_bufsize);

	rtpInit();
	ffmpegEncodeInit();

	// =============== INSTALL THE CONTROL HANDLER ===============
	if (SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ctrlHandlerFunction, TRUE) == 0)
	{
		printf("==> Cannot handle the CTRL-C...\n");
	}

	printf("==> RTP Receiver started\n");

	while (m_bExit == FALSE)
	{
		rtp2disk();
		
		rtp_session_mgr.timestamp += timestamp_inc;
	}

	printf("==> Exiting\n");

	free(recv_buf);

	ffmpegEncodeClose();

	rtp_session_destroy(rtp_session_mgr.rtp_session);
	ortp_exit();
}

sender

#include <ortp/ortp.h>
#include <string.h>

extern "C"{
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
};

struct RtpSessionMgr
{
	RtpSession *rtp_session;
	uint32_t timestamp_inc;
	uint32_t cur_timestamp;
};

RtpSessionMgr rtp_session_mgr;

const char g_ip[] = "127.0.0.1";
const int g_port = 8008;
const uint32_t timestamp_inc = 3600; // 90000 / 25

const int image_width = 704;
const int image_height = 576;
const int frame_rate = 25;
static int frame_count, wrap_size;

AVCodecContext *video_cc;

AVFrame *picture;

/** 帧包头的标识长度 */ 
#define CMD_HEADER_LEN 10

/** 帧包头的定义 */
static uint8_t CMD_HEADER_STR[CMD_HEADER_LEN] = { 0xAA,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xFF };

/** 帧的包头信息 */
typedef struct _sFrameHeader
{
	/** 命令名称标识 */ 
	unsigned char cmdHeader[CMD_HEADER_LEN];

	/** 采集的通道号 0~7*/
	unsigned char chId;

	/** 数据类型,音频 或者 视频*/
	unsigned char dataType; 

	/** 缓冲区的数据长度 */ 
	uint32_t len;

	/** 时间戳 */
	uint32_t timestamp;

}FrameHeader;

// set frame header 
FrameHeader frameHeader;

void rtpInit()
{
	char *m_SSRC;

	ortp_init();
	ortp_scheduler_init();
	printf("Scheduler initialized\n");

	rtp_session_mgr.rtp_session = rtp_session_new(RTP_SESSION_SENDONLY);

	rtp_session_set_scheduling_mode(rtp_session_mgr.rtp_session, 1);
	rtp_session_set_blocking_mode(rtp_session_mgr.rtp_session, 1);
	rtp_session_set_remote_addr(rtp_session_mgr.rtp_session, g_ip, g_port);
	rtp_session_set_send_payload_type(rtp_session_mgr.rtp_session, 34); // 34 is for H.263 video frame

	m_SSRC	= getenv("SSRC");
	if (m_SSRC != NULL) 
	{
		rtp_session_set_ssrc(rtp_session_mgr.rtp_session, atoi(m_SSRC));
	}

	rtp_session_mgr.cur_timestamp = 0;
	rtp_session_mgr.timestamp_inc = timestamp_inc;

	printf("rtp init success!\n");
}

int rtpSend(unsigned char *send_buffer, int frame_len)
{
	FrameHeader *fHeader = (FrameHeader *)send_buffer;
	fHeader->chId      = 0;
	fHeader->dataType  = 0; // SESSION_TYPE_VIDEO
	fHeader->len       = frame_len;
	fHeader->timestamp = 0;

	printf("frame header len = %d\n", fHeader->len);

	int wrapLen;
	wrapLen = frame_len + sizeof(FrameHeader);

	int sended_bytes;
	sended_bytes = rtp_session_send_with_ts(rtp_session_mgr.rtp_session, 
											(uint8_t *)send_buffer, 
											wrapLen,
											rtp_session_mgr.cur_timestamp);

	rtp_session_mgr.cur_timestamp += rtp_session_mgr.timestamp_inc;

	return sended_bytes;
}

void createCodecContext()
{
	video_cc =  avcodec_alloc_context();
	if (!video_cc)
	{
		fprintf(stderr, "alloc avcodec context failed\n");
		exit(1);
	}

	video_cc->codec_id = (CodecID)CODEC_ID_H264;
	video_cc->codec_type = AVMEDIA_TYPE_VIDEO;

	video_cc->me_range = 16;  
	video_cc->max_qdiff = 4;  
	video_cc->qmin = 10;  
	video_cc->qmax = 51;  
	video_cc->qcompress = 0.6f;  

	/* put sample parameters */
	video_cc->bit_rate = 400000;

	/* resolution must be a multiple of two */
	video_cc->width = image_width;
	video_cc->height = image_height;

	/* time base: this is the fundamental unit of time (in seconds) in terms
	of which frame timestamps are represented. for fixed-fps content,
	timebase should be 1/framerate and timestamp increments should be
	identically 1. */
	video_cc->time_base.den = frame_rate;
	video_cc->time_base.num = 1;

	video_cc->gop_size = 12; /* emit one intra frame every twelve frames at most */
	video_cc->pix_fmt = PIX_FMT_YUV420P;
}

AVFrame *allocPicture(int pix_fmt, int width, int height)
{
	AVFrame *picture;
	uint8_t *picture_buf;
	int size;

	picture = avcodec_alloc_frame();
	if (!picture)
		return NULL;
	
	size = avpicture_get_size((PixelFormat)pix_fmt, width, height);
	picture_buf = (uint8_t *)av_malloc(size);
	if (!picture_buf) {
		av_free(picture);
		return NULL;
	}
	avpicture_fill((AVPicture *)picture, picture_buf, (PixelFormat)pix_fmt, width, height);
	return picture;
}

void openVideo()
{
	AVCodec *video_codec;

	/* find the video encoder */
	video_codec = avcodec_find_encoder(video_cc->codec_id);
	if (!video_codec) {
		fprintf(stderr, "codec not found\n");
		exit(1);
	}

	/* open the codec */
	if (avcodec_open(video_cc, video_codec) < 0) {
		fprintf(stderr, "could not open video codec\n");
		exit(1);
	}

	/* allocate the encoded raw picture */
	picture = allocPicture(video_cc->pix_fmt, video_cc->width, video_cc->height);
	if (!picture) {
		fprintf(stderr, "Could not allocate picture\n");
		exit(1);
	}
}

/* prepare a dummy image */
void fill_yuv_image(AVFrame *pict, int frame_index, int width, int height)
{
	int x, y, i;

	i = frame_index;

	/* Y */
	for(y=0;y<height;y++) {
		for(x=0;x<width;x++) {
			pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3;
		}
	}

	/* Cb and Cr */
	for(y=0;y<height/2;y++) {
		for(x=0;x<width/2;x++) {
			pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2;
			pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5;
		}
	}
}

void ffmpegInit()
{
	// initialize libavcodec, and register all codecs and formats
	av_register_all();

	// create a codec context
	createCodecContext();

	// open H.264 codec
	openVideo();
}

void getEncodedFrame(unsigned char *buffer, int& len)
{
	int out_size;

	fill_yuv_image(picture, frame_count, video_cc->width, video_cc->height);

	// encode the frame
	out_size = avcodec_encode_video(video_cc, buffer, wrap_size-sizeof(FrameHeader), picture);

	len = out_size;
	frame_count++;
}

int main()
{
	unsigned char *send_outbuf;
	unsigned char *video_part;

	frame_count = 0;
	wrap_size = 20000;
	send_outbuf = (unsigned char *)malloc(wrap_size);

	// copy cmdHeader to frameInfo
	memcpy(frameHeader.cmdHeader,CMD_HEADER_STR,CMD_HEADER_LEN);

	memcpy(send_outbuf, &frameHeader, sizeof(FrameHeader));
	video_part = send_outbuf + sizeof(FrameHeader);

	ffmpegInit();
	rtpInit();

	while (1)
	{
		int frame_len;
		// get encode frame
		getEncodedFrame(video_part, frame_len);

		printf("encodecFrame length is : %d\n", frame_len);

		if (frame_len > 0)
		{
			rtpSend(send_outbuf, frame_len);
		}
	}

	rtp_session_destroy(rtp_session_mgr.rtp_session);

	free(send_outbuf);

	// Give us some time
	Sleep(250);

	ortp_exit();
}

抱歉!评论已关闭.