首先在阅读本代码之前百度一下avi,虽然经过我验证上面有部分错误,但是不影响阅读。因为有些变量的注释我没有写,所以请读者自行搜索吧。下面是c语言文件,编译之后能够直接运行,用来截取开始时间(单位s)后指定长度(单位s)的视频流。最后附上一部分视频文件的二进制,方便对照阅读。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#pragma pack(1)
/*
最开始的4个字节是一个四字符码‘RIFF’,表示这是一个RIFF文件;紧跟着后面用4个字节表示此RIFF文件的大小;
然后又是一个四字符码说明文件的具体类型(比如AVI、WAVE等);最后就是实际的数据。注意文件大小值的计算
方法为:实际数据长度 +4(文件类型域的大小);也就是说,文件大小的值不包括‘RIFF’域和“文件大小”域本身
的大小。
*/
typedef struct RIFF
{
char riff[4];
unsigned int size;
char type[4];
}RIFF;
/*
注意listSize值的计算方法为:实际的列表数据长度 +4(listType域的大小);也就是说listSize值不包括‘LIST’域和listSize域本身的大小。
*/
typedef struct list
{
char fcc[4];
unsigned int size;
char type[4];
}LIST;
/*************avih*********************/
typedef struct _avimainheader
{
char fcc[4];//'avih'
int size;// 本数据结构的大小,不包括最初的8个字节(fcc和cb两个域)
int dwMicroSecPerFrame;//视频帧间隔时间(以微秒为单位)
int dwMaxBytesPerSec;// 这个AVI文件的最大数据率
int dwPaddingGranularity;// 数据填充的粒度
int dwFlags;// AVI文件的全局标记,比如是否含有索引块等
int dwTotalFrames; // 总帧数
int dwInitialFrames; // 为交互格式指定初始帧数(非交互格式应该指定为0)
int dwStreams;// 本文件包含的流的个数
int dwSuggestedBufferSize; // 建议读取本文件的缓存大小(应能容纳最大的块)
int dwWidth;//视频图像的宽(以像素为单位)
int dwHeight;//视频图像的高(以像素为单位)
int dwReserved[4]; // 保留
}AVIMAINHEADER;
/********strh*******/
typedef struct
{
short int left;
short int top;
short int right;
short int bottom;
}RCFRAME;
typedef struct _avistreamheader
{
char fcc[4];// 必须为‘strh’
int size;
char fccType[4];// 流的类型:‘auds’(音频流)、‘vids’(视频流)、
char fccHandler[4];// 指定流的处理者,对于音视频来说就是解码器
int dwFlags;// 标记:是否允许这个流输出?调色板是否变化?
int wPriority;// 流的优先级(当有多个相同类型的流时优先级最高的为默认流)
int wLanguage;
int dwInitialFrames; // 为交互格式指定初始帧数
int dwScale;// 这个流使用的时间尺度
int dwRate;
int dwStart;// 流的开始时间
int dwLength;// 流的长度(单位与dwScale和dwRate的定义有关)
int dwQuality;// 流数据的质量指标(0 ~ 10,000)
int dwSampleSize;// Sample的大小
RCFRAME rcFrame;// 指定这个流(视频流或文字流)在视频主窗口中的显示位置
}AVISTREAMHEADER;
/**************strf_vids********************/
typedef struct tagBITMAPINFOHEADER
{
char fcc[4];
int biSize;
int biWidth;
int biHeight;
short int biPlanes;
short int biBitCount;
int biCompression;
int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
int biClrUsed;
int biClrImportant;
}BITMAPINFOHEADER;
typedef struct tagBITMAPINFO
{
BITMAPINFOHEADER bmiHeader;
int bimColors[1];
}BITMAPINFO;
/***************strf_auds*********************/
//#define WAVEFORMAT __attribute__((packed))
typedef struct
{
char fcc[4];
int size;
short int wFormatTag;
short int nChannels;
int nSamplesPerSec;
int nAvgBytesPerSec;
short int nBlockAlign;
short int wBitsPerSample;
short int biSize;
}WAVEFORMAT;
typedef struct strl_vids
{
LIST list;
AVISTREAMHEADER strh;
BITMAPINFO strf;
}STRL_VIDS;
typedef struct strl_auds
{
LIST list;
AVISTREAMHEADER strh;
WAVEFORMAT strf;
}STRL_AUDS;
typedef struct junk
{
char fcc[4];
int size;
}JUNK;
typedef struct HDRL
{
AVIMAINHEADER avih;
STRL_VIDS strl_vids;
STRL_AUDS strl_auds;
JUNK junk;
}HDRL;
typedef struct movi
{
char id[4];
int length;
}MOVI;
typedef struct data
{
LIST list;
MOVI chunk;
}DATA;
typedef struct aindex
{
char dwChunkId[4];
int dwFlags;
int dwOffset;
int dwSize;
}AINDEX;
typedef struct avi_idxl
{
char fcc[4];
AINDEX aindex[10];
}AVI_IDXL;
void print_avihead(RIFF riff,LIST list,HDRL hdrl)
{
printf("riff.size=%d\n",riff.size);
printf("list.size=%d\n",list.size);
printf("hdrl.avih.fcc=%s\n",hdrl.avih.fcc);
printf("hdrl.avih.dwTotalFrames=%d\n",hdrl.avih.dwTotalFrames);
printf("hdrl.avih.dwMicroSecPerFrame=%dms\n",hdrl.avih.dwMicroSecPerFrame/1000);
printf("hdrl.strl_vids.strh.fcc=%s\n",hdrl.strl_vids.strh.fcc);
printf("hdrl.strl_auds.strh.fcc=%s\n",hdrl.strl_auds.strh.fcc);
printf("hdrl.junk.fcc=%c\n",hdrl.junk.fcc[3]);
printf("hdrl.junk.size=%d\n",hdrl.junk.size);
}
void get_aviidxl(AVI_IDXL*idxl,char*src_buf,int off_set)
{
char*p;
p=src_buf+off_set;
memcpy(idxl,p,sizeof(AVI_IDXL));
printf("idxl.fcc:%s\n",idxl->fcc);
}
long get_file_size(const char *path)
{
unsigned long filesize = -1;
FILE*fp;
fp = fopen(path,"r");
if(fp==NULL)
{
return filesize;
}
fseek(fp,0,SEEK_END);
filesize = ftell(fp);
fclose(fp);
return filesize;
}
int main(int argc ,char*argv[])
{
unsigned long size;
int count=0,i;
int begin_s=60;//开始时间s
int t_s=20;//截取间隔s
int fd_src,fd_dst;
int off_set;
char*buf,*readbuf;
char*src_buf,*tmp;
RIFF riff;
LIST lhdrl;
HDRL hdrl;
LIST lmovi;
MOVI movi;
/*******************打开源文件与目的文件*****************************/
fd_src = open("H264.avi",O_RDONLY);
fd_dst = open("out.avi",O_CREAT|O_RDWR,0777);
/************将源文件内容读入缓存区***********************/
lseek(fd_src,0,SEEK_SET);
read(fd_src,&riff,sizeof(riff));
read(fd_src,&lhdrl,sizeof(lhdrl));
read(fd_src,&hdrl,lhdrl.size+4);
// print_avihead(riff,lhdrl,hdrl);
/*********建立JUNK缓存区存储0************/
buf=malloc(hdrl.junk.size);
memset(buf,0,hdrl.junk.size);
/**********将文件头与JUNK区写入目标文件***********/
lseek(fd_dst,sizeof(riff),SEEK_CUR);
lseek(fd_dst,sizeof(lhdrl),SEEK_CUR);
lseek(fd_dst,sizeof(hdrl),SEEK_CUR);
//write(fd_dst,&riff,sizeof(riff));
//write(fd_dst,&list,sizeof(list));
//write(fd_dst,&hdrl,sizeof(hdrl));
write(fd_dst,buf,hdrl.junk.size);
free(buf);
/***********偏移至movi区获取实际数据***************/
lseek(fd_src,hdrl.junk.size,SEEK_CUR);
read(fd_src,&lmovi,sizeof(lmovi));
write(fd_dst,&lmovi,sizeof(lmovi));
/******写数据*****/
while(1)//for(i =0 ;i < 20;i++)
{
read(fd_src,&movi,sizeof(movi));
if(movi.length%2!=0)
{
movi.length+=1;
}
buf = malloc(movi.length);
read(fd_src,buf,movi.length);
// printf("%d\tmovi.id=%s\n",count,movi.id);
if(((strncmp(movi.id+2,"db",2)==0)||(strncmp(movi.id+2,"dc",2)==0))&&(count<begin_s*25))//25帧是1秒
{
free(buf);
count++;
// printf("%d\tmovi.id=%s\n",count,movi.id);
continue;
}
if(((strncmp(movi.id+2,"pc",2)==0)||(strncmp(movi.id+2,"wb",2)==0))&&((count-1)<begin_s*25))//音频帧不计算在内,时间只用视频帧计算
{
free(buf);
continue;
}
if(((strncmp(movi.id+2,"db",2)==0)||(strncmp(movi.id+2,"dc",2)==0))&&(count>=begin_s*25))
{
count++;
printf("%d\tmovi.id=%s\tmovi.size=%x\n",count,movi.id,movi.length);
}
write(fd_dst,&movi,sizeof(movi));
write(fd_dst,buf,movi.length);
size=size+movi.length+8;
free(buf);
if(count>(begin_s+t_s)*25)
{
break;
}
//free(buf);
//size=size+movi.length+8;
}
riff.size=12+12+lhdrl.size+4+hdrl.junk.size+12+size;//计算数据大小,用来填充报头
hdrl.avih.dwTotalFrames=count;//大小字节
lseek(fd_dst,0,SEEK_SET);
write(fd_dst,&riff,sizeof(riff));
write(fd_dst,&lhdrl,sizeof(lhdrl));
write(fd_dst,&hdrl,sizeof(hdrl));
// off_set=off_set+8+data.list.size;
// get_aviidxl(&idxl,src_buf,off_set);
// write(fd_dst,src_buf,size);
close(fd_src);
close(fd_dst);
}
报头的图片
中间是JUNK的填充
这是一部分数据,都不到一帧,所以最好自己找个avi文件对照的我的代码看。