之前做一个项目遇到一个问题:从网络中收到PS/TS流,需要从中分离出视频和音频,但是FFmpeg只支持标准的几种输入流协议(RTP/HTTP/RTSP/MMS),而我们的协议它不支持,所以就不能够用它来直接做接收。那能否由自己来接收数据然后让FFmpeg读取内存中的数据呢?答案当然是可以的,这就需要用到FFmpeg的探测流格式的功能,主要是用到了两个API: avio_alloc_context, av_probe_input_buffer。前者向FFmpeg传递用户自定义的Read,write, Seek的回调函数,即文件IO操作由用户去管理。下面是函数的原型:
AVIOContext *avio_alloc_context(
unsigned char *buffer,
int buffer_size,
int write_flag,
void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),//指定从内存中读取的方法,将buf_size字节大小的数据保存到buf
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),//对应的这是写内存的函数
int64_t (*seek)(void *opaque, int64_t offset, int whence));
后者则用于探测输入数据的格式,比如采用什么容器封装,这个是内部自动判断的,它会先缓冲一段数据,解析成功后,函数会返回0,如果返回值为负数则表示无法识别格式。
下面我会实现一个例子,从内存中读取数据,传递给FFmpeg,然后后面的操作就跟读文件一样了。
第一步,我们要做的工作是:打开一个文件-》创建输入流的上下文-》调用avio_alloc_context函数并传递read_packet函数的地址-》自动探测格式-》打开输入流。代码如下:
AVIOContext* pb = NULL;
AVInputFormat* piFmt = NULL;
BOOL bRet = m_file.Open(m_filePath.c_str(), CFile::modeRead);
if(!bRet)
{
TRACE("OpenFile %s Failed\n", m_filePath.c_str());
return -3;
}
uint8_t * input_buf = (uint8_t*)av_mallocz(sizeof(uint8_t)* BUF_SIZE);
pb = avio_alloc_context(input_buf, BUF_SIZE, 0, this, read_data, NULL, NULL);
//探测从内存中获取到的媒体流的格式
if (av_probe_input_buffer(pb, &piFmt, "", NULL, 0, 0) < 0)
{
TRACE("probe format failed\n");
return -4;
}
else
{
TRACE("format:%s[%s]\n", piFmt->name, piFmt->long_name);
}
m_inpu