背景
版本ffmpeg 3.3.3;使用 FFmpeg实时接收rtp 音频和视频流,并生成视频文件。
在网上查阅了大量资料,基本上都是使用命令行来接收rtp媒体流的,由于项目中不得不使用ffmpeg接收RTP实时流。因此花了不少时间研究。
ffmepg 默认是不打开rtp协议的,需要添加白名单后,并根据sdp文件来接收rtp媒体流。sdp文件也很费劲,刚开始不明白sdp文件是啥,具体含义也不知道,研究半天才搞懂大概。
添加白名单时一定要申请单独的内存块,我在这个坑里痛苦了很久,之前直接赋值,老是崩溃,不明原因。后来经高人指点,幡然醒悟,白名单在avformat_close_input中会进行free,必须要申请内存才行。
废话不多说,直接上代码
//初始化输入上下文
AVFormatContext *p_ifmt_ctx_v = avformat_alloc_context();
AVFormatContext *p_ifmt_ctx_a = avformat_alloc_context();
AVInputFormat *p_ifmt_v = NULL;
AVInputFormat *p_ifmt_a = NULL;
eU8 buffer[1280] = {0};
eU8 port_v[10] = {0};
eU8 port_a[10] = {0};
eU8 sdp_file_name[128] = {0};
eU8 sdp_file_name_a[128] = {0};
FILE *fp = NULL;
p_ifmt_ctx_v ->flags |= AVFMT_NOFILE;
//添加白名单,这里很重要,如果不申请内存,在avformat_close_input中会宕
p_ifmt_ctx_v ->protocol_whitelist = av_malloc(sizeof("file,udp,rtp"));
memcpy(p_ifmt_ctx_v ->protocol_whitelist,"file,udp,rtp",sizeof("file,udp,rtp"));
p_ifmt_ctx_a ->flags |= AVFMT_NOFILE;
//添加白名单,这里很重要,如果不申请内存,在avformat_close_input中会宕
p_ifmt_ctx_a ->protocol_whitelist = av_malloc(sizeof("file,udp,rtp"));
memcpy(p_ifmt_ctx_a ->protocol_whitelist,"file,udp,rtp",sizeof("file,udp,rtp"));
//输入的文件格式为sdp
p_ifmt_v = av_find_input_format("sdp");
p_ifmt_a = av_find_input_format("sdp");
av_register_all();
avfilter_register_all();
avformat_network_init();
//生成sdp文件,ffmpeg根据sdp文件获取到ip地址和port,以及媒体格式信息
sprintf(sdp_file_name, "%d", threadid);
strcat(sdp_file_name,"-v");
strcat(sdp_file_name,".sdp");
fp = fopen(sdp_file_name,"wb+");
if(fp ==NULL)
{
printf("cannot open file sdp_file_name=%s \n",sdp_file_name);
return FAILURE;
}
sprintf(port_v,"%d",video_port);
strcat(buffer,"v=0\r\n");
//发送rtp流的源ip地址
strcat(buffer,"o=- 0 0 IN IP4 127.0.0.1\r\n");
strcat(buffer,"s=video\r\n" );
//接收rtp流的ip地址
strcat(buffer,"c=IN IP4 127.0.0.1\r\n" );
strcat(buffer,"t=0 0\r\n" );
strcat(buffer,"a=tool:libavformat 57.71.100\r\n");
strcat(buffer,"m=video ");
strcat(buffer," ");
// 接收rtp流的端口
strcat(buffer,port_v);
//98 代表的是媒体格式,即rtp头中携带的媒体格式,我这里的h264 格式类型为98
strcat(buffer," RTP/AVP 98\r\n");
strcat(buffer,"a=rtpmap:98 H264/90000\r\n");
fprintf(fp,"%s\n",buffer);
fclose(fp);
if ((ret = avformat_open_input(&p_ifmt_ctx_v , sdp_file_name, p_ifmt_v , NULL)) != 0)
{
printf( "Cannot open p_ifmt_ctx_v file %s\n",buffer);
return -1;
}
//收音频流的sdp文件
memset(sdp_file_name_a,0,sizeof(sdp_file_name_a));
memset(buffer,0,sizeof(buffer));
sprintf(sdp_file_name_a, "%d", threadid);
strcat(sdp_file_name_a,"-a");
strcat(sdp_file_name_a,".sdp");
fp = fopen(sdp_file_name_a,"wb+");
if(fp ==NULL)
{
printf("cannot open file sdp_file_name_a=%s \n",sdp_file_name_a);
return -1;
}
sprintf(port_a,"%d",audio_port);
strcat(buffer,"v=0\r\n");
strcat(buffer,"o=- 0 0 IN IP4 127.0.0.1\r\n");
strcat(buffer,"s=audio \r\n" );
strcat(buffer,"c=IN IP4 127.0.0.1\r\n" );
strcat(buffer,"t=0 0\r\n" );
strcat(buffer,"a=tool:libavformat 57.71.100\r\n");
strcat(buffer,"m=audio ");
strcat(buffer," ");
strcat(buffer,port_a);
//这里是amr格式类型,rtp头中显示126,根据实际进行修改
strcat(buffer," RTP/AVP 126\r\n");
strcat(buffer,"a=rtpmap:126 AMR/8000/1\r\n");
fprintf(fp,"%s\n",buffer);
fclose(fp);
if ((ret = avformat_open_input(&p_ifmt_ctx_a , sdp_file_name_a, p_ifmt_a , NULL)) != 0)
{
printf( "Cannot open p_ifmt_ctx_a file %s\n",in_filename_a);
return -1;
}
这里就打开了端口开始接收rtp媒体流了,接下来打印一下媒体流的信息,代码如下:
if ((ret = avformat_find_stream_info(p_ifmt_ctx_v, NULL)) < 0)
{
printf( "Cannot find ifmt_ctx_v stream information\n");
goto end;
}
if ((ret = avformat_find_stream_info(p_ifmt_ctx_a, NULL)) < 0)
{
printf( "Cannot find ifmt_ctx_a stream information\n");
goto end;
}
printf("===========Input Information================\n");
av_dump_format(ifmt_ctx_a, 0, in_filename_a, 0);
av_dump_format(ifmt_ctx_v, 0, in_filename_v, 0);
printf("============================================\n");
至此,ffmpeg根据sdp文件来接收rtp媒体流完成,在此记录一下。
参考 ffmpeg监听和接收rtp音视频流_阳光的威威的博客-CSDN博客_ffmpeg 监听