ffmpeg关于io的输入输出操作
ffmpeg 无论转码是本地文件输入输出,例如将mp4文件 转成 flv文件,或者从rtmp流接入到rtsp流,这时候我们在输入输出端拿不到数据流,只能拿到输入输出的地址,这时候ffmpeg的 io就出场了,道理是把输入输出流写入到缓存中,在做处理。应用场景例如:从http 拿到的数据流,从摄像头拿到的数据流,这些都能作为io输入,将mux后的数据write到io用于存成文件,或者librtmp打包出去,这些都用于io输出。具体操作如下:
1:ffmpeg的IO输入
Io_Buf_Info buf_info;
//这里必须用av_malloc申请,用calloc申请会崩溃
buf_info.BufLen = DEMUX_MAX_IO_BUF_SIZE;
buf_info.Buf = (char * )av_malloc(buf_info.BufLen);
m_list_format_io_buf[rtmpaddress_id] = (buf_info);
AVIOContext * pb = NULL;
AVInputFormat *piFmt = NULL;
AVFormatContext * m_pFmt = NULL;
//step1:申请一个AVIOContext,传入回调函数
//填写当前要读的rtmpaddress_id 回调里面才能知道去读哪一个地址的流
param->use_rtmpaddress_id = rtmpaddress_id;
pb = avio_alloc_context((unsigned char *)buf_info.Buf, buf_info.BufLen,0, param,
param->read_packet_cb, NULL,param->seek_cb);
if (!pb)
{
printf("ERROR : avio alloc failed!\n");
return -1;
}
//step2:探测流格式
if (av_probe_input_buffer2(pb, &piFmt, "", NULL,0,0) < 0)
{
printf("ERROR : probe failed!\n");
return -1;
}
else
{
printf("SUCCESS :probe success!\n");
printf("format: %s[%s]\n", piFmt->name, piFmt->long_name);
}
m_pFmt = avformat_alloc_context();
m_pFmt->pb = pb; //step3:这一步很关键
//step4:打开流
if (avformat_open_input(&m_pFmt, "", piFmt, NULL) < 0)
{
printf("ERROR : avformat open failed.\n");
return -1;
}
else
{
printf("SUCCESS : open stream success!\n");
}
av_dump_format(m_pFmt, 0, "", 0);
这样就和本地文件一样用av_read_frame读取数据read_packet_cb中放入数据。
2:ffmpeg的IO输出
<pre name="code" class="cpp"> Module_StreamInfo * module_streaminfo = in_video->second;
AVFormatContext * formatcontext = avformat_alloc_context();
AVIOContext * pb = NULL;
AVStream * ovideo_st = NULL;
Io_Buf_Info io_buf_info_video;
io_cb_write_pram * cb_pram_video = NULL;
cb_pram_video = (io_cb_write_pram *)malloc(sizeof(io_cb_write_pram )* 1);
cb_pram_video->thisclass = this;
cb_pram_video->pram = module_streaminfo;
m_list_cb_write_pram[module_streaminfo->stream_id] = (cb_pram_video);
//step1 申请一个AVFormatContext
if (m_mux_info.mux_format == MuxFormat_Flv)
{
avformat_alloc_output_context2(&formatcontext, NULL,"flv",NULL);
}
else if (m_mux_info.mux_format == MuxFormat_Mpegts)
{
avformat_alloc_output_context2(&formatcontext, NULL,"mpegts",NULL);
}
else if (m_mux_info.mux_format == MuxFormat_Mp4)
{
avformat_alloc_output_context2(&formatcontext, NULL,"mp4",NULL);
}
else
{
avformat_alloc_output_context2(&formatcontext, NULL,"flv",NULL);
}
if (!formatcontext)
{
return -1;
}
//step2申请一个AVIOContext
//这里分配大小一定要是4096的整数倍,足够一帧大小否则中断
io_buf_info_video.BufLen = (module_streaminfo->VideoInfo.Width * module_streaminfo->VideoInfo.Height * 3 /20 + 100) /
4096 * 4096;
io_buf_info_video.Buf =(char *)calloc(module_streaminfo->BufLen,sizeof(char));
m_list_format_io_buf[in_video->first] = (io_buf_info_video);
pb = avio_alloc_context((unsigned char *)m_list_format_io_buf[in_video->first].Buf,
m_list_format_io_buf[in_video->first].BufLen,1,cb_pram_video, NULL, cb_write_data, NULL);
if (!pb)
{
printf("error : avio alloc failed!\n");
return -1;
}
//step3:这一步很关键,将输出的buf和AVFormatContext关联
formatcontext->pb = pb;
//The caller has supplied a custom AVIOContext, don't avio_close() it.
//AVFMT_FLAG_FLUSH_PACKETS这个值相当重要,他决定每次写av_interleaved_write_frame,是否立刻返回
formatcontext->flags = AVFMT_FLAG_CUSTOM_IO | AVFMT_FLAG_FLUSH_PACKETS;
//step4 添加流信息
ovideo_st = add_out_stream2(formatcontext,AVMEDIA_TYPE_VIDEO,module_streaminfo);
if (ovideo_st == NULL)
{
return -1;
}
//这里要填写1否则中断
av_dump_format(formatcontext, 0, "", 1);
ret = avformat_write_header(formatcontext, NULL);
if (ret != 0)
{
printf("Call avformat_write_header function failed.\n");
return -1;
}
avio_flush(pb);
这样在cb_write_data回调中就有要的数据了。
如有错误请指正: