对于视频⽂件来说,视频由单张图⽚帧所组成,⽐如每秒25帧,但是图⽚帧的像素块之间存在
相似性,因此视频帧图像可以进⾏图像压缩;H264采⽤了16*16的分块⼤⼩对,视频帧图像
进⾏相似⽐较和压缩编码。如下图所示:
H264中的I帧、P帧和B帧
H26使⽤帧内压缩和帧间压缩的⽅式提⾼编码压缩率;H264采⽤了独特的I帧、P帧和B帧策略
来实现,连续帧之间的压缩;
H264除了实现了对视频的压缩处理之外,为了⽅便⽹络传输,提供了对应的视频编码和分⽚
策略;类似于⽹络数据封装成IP帧,在H264中将其称为组(GOP, group of pictures)、⽚
(slice)、宏块(Macroblock)这些⼀起组成了H264的码流分层结构;H264将其组织成为
序列(GOP)、图⽚(pictrue)、⽚(Slice)、宏块(Macroblock)、⼦块(subblock)五个层次
GOP (图像组)主要⽤作形容⼀个IDR帧 到下⼀个IDR帧之间的间隔了多少个帧。
H264将视频分为连续的帧进⾏传输,在连续的帧之间使⽤I帧、P帧和B帧。同时对于帧内⽽
⾔,将图像分块为⽚、宏块和字块进⾏分⽚传输;通过这个过程实现对视频⽂件的压缩包装。
IDR( Instantaneous Decoding Refresh,即时解码刷新 )
⼀个序列的第⼀个图像叫做 IDR 图像(⽴即刷新图像),IDR 图像都是 I 帧图像。
I和IDR帧都使⽤帧内预测。I帧不⽤参考任何帧,但是之后的P帧和B帧是有可能参考这个I帧之
前的帧的。IDR就不允许这样。⽐如(解码的顺序):
IDR1 P4 B2 B3 P7 B5 B6 I10 B8 B9 P13 B11 B12 P16 B14 B15 这⾥的B8可以跨过I10去参考P7
原始图像: IDR1 B2 B3 P4 B5 B6 P7 B8 B9 I10
IDR1 P4 B2 B3 P7 B5 B6 IDR8 P11 B9 B10 P14 B11 B12 这⾥的B9就只能参照IDR8和P11,不可以
参考IDR8前⾯的帧
其核⼼作⽤是,是为了解码的重同步,当解码器解码到 IDR 图像时,⽴即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始⼀个新的序列。这样,如果前⼀
个序列出现重⼤错误,在这⾥可以获得重新同步的机会。IDR图像之后的图像永远不会使⽤
IDR之前的图像的数据来解码。
NALU
这儿我们只关注5,6,7,8点
FFmepg提取H264的格式
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>
static char err_buff[128]={0};
static char* av_get_err(int errnum){
av_strerror(errnum,err_buff,128);
return err_buff;
}
/*
AvCodecContext->extradata[]中为nalu长度
* codec_extradata:
* 1, 64, 0, 1f, ff, e1, [0, 18], 67, 64, 0, 1f, ac, c8, 60, 78, 1b, 7e,
* 78, 40, 0, 0, fa, 40, 0, 3a, 98, 3, c6, c, 66, 80,
* 1, [0, 5],68, e9, 78, bc, b0, 0,
*/
//ffmpeg -i 2018.mp4 -codec copy -bsf:h264_mp4toannexb -f h264 tmp.h264
//ffmpeg 从mp4上提取H264的nalu h
int main(int argc, char **argv)
{
AVFormatContext *ifmt_ctx=NULL;
int videoIndex=-1;
AVPacket *pkt=NULL;
int ret=-1;
int file_end=0;//判斷是都讀取結束
if(argc<3){
printf("error could not alllcate context\n.");
// avformat_close_input(&ifmt_ctx);
return -1;
}
FILE *outfp=fopen(argv[2],"wb");
printf("in:%s out:%s",argv[1],argv[2]);
// 分解解复用器的内存,使用avformat_close_input 释放
ifmt_ctx=avformat_alloc_context();
if(!ifmt_ctx){
printf("error could not allocate context.\n");
return -1;
}
ret=avformat_open_input(&ifmt_ctx,argv[1],NULL,NULL);
if(ret!=0){
printf("error avformat_open_input:%s\n",av_get_err(ret));
return -1;
}
ret=avformat_find_stream_info(ifmt_ctx,NULL);
if(ret<0){
printf("error avformat_find_stream_info :%s\n",av_get_err(ret));
avformat_close_input(&ifmt_ctx);
return -1;
}
//查找出哪個m码流是video/audio/subtitles
videoIndex=-1;
videoIndex=av_find_best_stream(ifmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
if(videoIndex==-1){
printf("Did not find a video stream.\n");
avformat_close_input(&ifmt_ctx);
return -1;
}
//分配数据包
pkt=av_packet_alloc();
//
av_init_packet(pkt);
//获取相应的比特流过滤器
//FLV/MP4/MKV中,h264需要h264_mp4toannexb 处理,添加SPS和PPS等信息
//FLV 封装时,可以把多个NALU放在VIDEO TAG中,结构中4B NALU长度+NALU1+4B
const AVBitStreamFilter *bsFilter=av_bsf_get_by_name("h264_mp4toannexb");
//初始化过滤器上下文
AVBSFContext *bsf_ctx=NULL;
av_bsf_alloc(bsFilter,&bsf_ctx);
// 添加解码器属性
avcodec_parameters_copy(bsf_ctx->par_in,ifmt_ctx->streams[videoIndex]->codecpar);
av_bsf_init(bsf_ctx);
file_end=0;
while(0==file_end){
if(ret=av_read_frame(ifmt_ctx,pkt)<0){
file_end=1;
printf("read file end:ret:%d\n,",ret);
}
if(ret==0&&pkt->stream_index==videoIndex)
{
#if 0
int input_size=pkt->size;
int out_pkt_count=0;
if(av_bsf_send_packet(bsf_ctx,pkt)!=0){
av_packet_unref(pkt);//你不用了酒吧资源释放掉
continue;
}
av_packet_unref(pkt);//释放资源
while(av_bsf_receive_packet(bsf_ctx,pkt)==0){
out_pkt_count++;
//printf
size_t size=fwrite(pkt->data,1,pkt->size,outfp);
if(size!=pkt->size){
printf("fwrite failed->write:%u\n",size,pkt->size);
}
av_packet_unref(pkt);
}
if(out_pkt_count>=2){
printf("cur_pkt(size:%d) only get 1 out pkt:%u",input_size,out_pkt_count);
}
#else
size_t size=fwrite(pkt->data,1,pkt->size,outfp);
if(size!=pkt->size){
printf("fwrite failed->write:%u,pkt->size:%u\n",size,pkt->size);
}
av_packet_unref(pkt);
#endif
}
else{
if(ret==0){
av_packet_unref(pkt);// 释放内存
}
}
}
if(outfp)
fclose(outfp);
if(bsf_ctx){
av_bsf_free(&bsf_ctx);
}
if(pkt)
{
av_packet_free(&pkt);
}
if(ifmt_ctx){
avformat_close_input(&ifmt_ctx);
}
printf("finish\n");
return 0;
}
源码下载地址:
GitHub - weixiaoq20/ffmepg: ffmepg学习 里面ffmepg文件下的07-03-extract-h264这个目录,运行的环境是Qt Creator 4.5.1