研究了使用FFMPEG保存H264流到AVI文件中形成录像的方法,下面是大致流程。
使用的FFMPEG版本 ffmpeg-2.6.9。然后我静态编译后使用的静态库,至于怎么静态编译看我之前的文章。
同时这个H264写入AVI的方法我这也有封装好的类。在文章最后提供。
1.变量定义
// 输出格式
AVOutputFormat *m_outFormat;
// 输出内容对象
AVFormatContext *m_FormatContext;
// 视频流对象
AVStream *m_AVStream;
// pts
double video_pts;
2.分配输出内容对象
注意最后一个参数为文件的文件名(带路径)倒数第二个是文件格式这里这“avi”
avformat_alloc_output_context2(m_FormatContext, NULL, “avi”,"1.avi");
3. 保存输出格式
m_outFormat = m_FormatContext->oformat;
4.添加视频流
m_AVStream = avformat_new_stream(m_FormatContext, 0);
如果失败了别忘了释放,否则有内存泄漏
// 释放分配的文本
avformat_free_context(m_FormatContext);
5.
填入视频的参数
AVCodecContext *c = m_AVStream->codec;
c->codec_id = AV_CODEC_ID_H264;//因为这里使用的是H264流,如果是其他的例如JPEG就修改成相应的参数
c->codec_type = AVMEDIA_TYPE_VIDEO;
//填入你视频的宽高
c->width = 1920;
c->height = 1080;
//填入帧率
c->time_base.den = 20;
c->time_base.num = 1;
c = NULL;
6.输出信息
av_dump_format(m_FormatContext, 0, "1.avi", 1);
7.打开输出文件
avio_open(&m_FormatContext->pb, "1.avi", AVIO_FLAG_READ_WRITE);
失败的话别忘了释放文本
avformat_free_context(this->m_FormatContext);
8. 写文件头
avformat_write_header(m_FormatContext,NULL);
9 然后就开始正式的写入数据了
a>你首先假设获取到了流数据存储到了buf里面吧
b>计算PTS
video_pts = (double)m_AVStream->pts.val * m_AVStream->time_base.num / m_AVStream->time_base.den;
c>写数据
AVPacket pkt;
av_init_packet(&pkt);
然后检索到H264的开始位和关键帧校验(00 00 00 01 65或者00 00 01 65),这个主要是关键帧的开始位前面有一些附加数据需要特殊处理一下。
如果找到了那么就进行操作d否则进行操作e
d> 获取开始位之前的数据长度比如你的数据00 00 00 01是从第51个字节开始的那么你获取到的这个长度应该是50将这个值我们设定为X吧,这个部分的数据是额外数据。
额外数据大小赋值
m_AVStream->codec->extradata_size = X;
额外数据分配内存,这个不用每次分配,因为指针存在m_AVStream里面我们下次还能用所以加上if的判断。记住一定要加上FF_INPUT_BUFFER_PADDING_SIZE这个长度。
FFMPEG里面说是有的解码器会一次读取32个字节或者64字节的数据长度如果不加的话就越界了。那么的话就有可能会崩溃例
if (m_AVStream->codec->extradata == NULL)
{
m_AVStream->codec->extradata = (uint8_t*)malloc(this->m_AVStream->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
}
将数据拷贝到内存里面
memcpy(m_AVStream->codec->extradata, &buf[0], X);
然后清空后面的数据,因为如果不清空有的解码器会崩溃入力MPEG
memset(&m_AVStream->codec->extradata[X], 0, FF_INPUT_BUFFER_PADDING_SIZE);
e> 写入数据
pkt.stream_index = m_AVStream->index;
pkt.data = (uint8_t *)buf;
pkt.size = len;//这个数数据长度
av_interleaved_write_frame(m_FormatContext, &pkt);
f>好了这就是一帧数据写完了,如果收到下一帧数据那么就重新从a开始
10到这里就应该是想停止录像了那么需要进行如下操作
写尾部信息
av_write_trailer(m_FormatContext);
释放用过的结构体
if (m_AVStream->codec->extradata != NULL)
{
free(m_AVStream->codec->extradata);
m_AVStream->codec->extradata = NULL;
m_AVStream->codec->extradata_size = 0;
}
关闭输出文件
if (!(m_outFormat->flags & AVFMT_NOFILE))
{
/* close the output file */
avio_close(m_FormatContext->pb);
m_FormatContext->pb = NULL;
}
释放文本中的所有内容
if (NULL != m_FormatContext)
{
avformat_free_context(m_FormatContext);
m_FormatContext = NULL;
}
11结束。
AVI文件的写类下载:
https://pan.baidu.com/s/1C4p5IqzSKS64L1n9jnm38Q
调用方法
1.OpenNewVideo
2.WriteNewFrame 循环调用
3.CloseNewVideo