MOV/3GP/MP4文件解析
2012-04-23 14:51:41| 分类: MPEG-2解码器编程 |字号 订阅
#include
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed __int64 int64_t;
#define FOURCC(a,b,c,d) ((a << 24) | (b << 16) | (c << 8) | d)
#define LE16(a) (((a)[0] << 8) | (a)[1])
#define LE32(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3])
#define LE64(a) ((LE32((a)) << 32) | LE32(((a) + 4)) )
#define BE16(a) (((a)[1] << 8) | (a)[0])
#define BE32(a) (((a)[3] << 24) | ((a)[2] << 16) | ((a)[1] << 8) | (a)[0])
#define BE64(a) ((BE32((a) + 4) << 32) | BE32(((a))) )
#define MKINT16 LE16
#define MKINT32 LE32
#define MKINT64 LE64
#define FOURCC_ftyp FOURCC('f','t','y','p')
#define FOURCC_moov FOURCC('m','o','o','v')
#define FOURCC_mvhd FOURCC('m','v','h','d')
#define FOURCC_trak FOURCC('t','r','a','k')
#define FOURCC_tkhd FOURCC('t','k','h','d')
#define FOURCC_edts FOURCC('e','d','t','s')
#define FOURCC_mdia FOURCC('m','d','i','a')
#define FOURCC_mdhd FOURCC('m','d','h','d')
#define FOURCC_hdlr FOURCC('h','d','l','r')
#define FOURCC_minf FOURCC('m','i','n','f')
#define FOURCC_vmhd FOURCC('v','m','h','d')
#define FOURCC_smhd FOURCC('s','m','h','d')
#define FOURCC_dinf FOURCC('d','i','n','f')
#define FOURCC_dref FOURCC('d','r','e','f')
#define FOURCC_stbl FOURCC('s','t','b','l')
#define FOURCC_stsd FOURCC('s','t','s','d')
#define FOURCC_stts FOURCC('s','t','t','s')
#define FOURCC_ctts FOURCC('c','t','t','s')
#define FOURCC_stsc FOURCC('s','t','s','c')
#define FOURCC_stsz FOURCC('s','t','s','z')
#define FOURCC_stco FOURCC('s','t','c','o')
#define FOURCC_stss FOURCC('s','t','s','s')
#define FOURCC_stsz FOURCC('s','t','s','z')
#define FOURCC_esds FOURCC('e','s','d','s')
#define FOURCC_mp4a FOURCC('m','p','4','a')
#define FOURCC_mp4v FOURCC('m','p','4','v')
#define FOURCC_avc1 FOURCC('a','v','c','1')
#define FOURCC_avcC FOURCC('a','v','c','C')
#define FOURCC_wave FOURCC('w','a','v','e')
#define FOURCC_frma FOURCC('f','r','m','a')
typedef struct
{
uint64_t creation_time;
uint64_t modification_time;
uint32_t timescale;
uint64_t duration;
}mvhd_t;
typedef struct
{
uint32_t sample_count;
uint32_t sample_delta;
}stts_t;
typedef struct
{
uint32_t first_chunk;
uint32_t first_sample;
uint32_t samples_per_chunk;
uint32_t samples_desc_index;
}stsc_t;
typedef struct
{
uint32_t chunk_offset;
}stco_t;
typedef struct
{
uint32_t sample_number;
}stss_t;
typedef struct
{
uint32_t sample_size;
}stsz_t;
typedef struct
{
uint64_t creation_time;
uint64_t modification_time;
uint32_t language;
uint32_t handler_type;
uint32_t track_id;
uint32_t timescale;
uint64_t duration;
uint32_t codec;
uint32_t width;
uint32_t height;
uint32_t channels;
uint32_t samplesize;
uint32_t samplerate;
uint8_t* extra_data;
uint32_t extra_data_size;
uint32_t nal_size;
stts_t* stts;
uint32_t stts_count;
stsc_t* stsc;
uint32_t stsc_count;
stco_t* stco;
uint32_t stco_count;
stss_t* stss;
uint32_t stss_count;
stsz_t* stsz;
uint32_t stsz_count;
uint32_t sample_idx;
}track_t;
typedef struct
{
uint32_t qtmux;
mvhd_t mvhd;
FILE* fp;
track_t tracks[16];
uint32_t num_tracks;
track_t* current;
track_t* video;
track_t* audio;
}MP4DMX;
#ifdef __cplusplus
extern "C"
{
#endif
void* mp4dmxCreate(char* filename);
int mp4dmxGetVideoPacket(void* dmx,char* buf,uint32_t* pts);
int mp4dmxGetAudioPacket(void* dmx,char* buf,uint32_t* pts);
int mp4dmxDestroy(void* dmx);
#ifdef __cplusplus
}
#endif
#ifndef __MP4DMX_C__
#define __MP4DMX_C__
#include "mp4dmx.h"
typedef struct
{
int64_t size;
uint32_t type;
uint8_t uid[16];
uint32_t version;
uint32_t flags;
}ATOM;
static int mp4dmxParseAtom(MP4DMX* dmx,ATOM* atom);
static int mp4dmxReadVersionFlags(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[4];
fread(buf,1,4,dmx->fp);
atom->version = buf[0];
atom->flags = MKINT32(buf) & 0x00ffffff;
return 0;
}
static int mp4dmxReadAtom(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[8];
if(8 == fread(buf,1,8,dmx->fp))
{
atom->size = MKINT32(buf);
atom->type = MKINT32(buf + 4);
if(atom->size == 0)
{
return -1;
}
else if(atom->size == 1)
{
if(8 == fread(buf,1,8,dmx->fp))
{
atom->size = MKINT64(buf) - 16;
}
else
{
return -1;
}
}
else
{
atom->size -= 8;
}
if(atom->type == FOURCC('u','u','i','d'))
{
if(16 != fread(atom->uid,1,16,dmx->fp))
{
return -1;
}
atom->size -= 16;
}
return 0;
}
return -1;
}
static int mp4dmxParseSkip(MP4DMX* dmx,ATOM* atom)
{
fseek(dmx->fp,atom->size,SEEK_CUR);
return 0;
}
static int mp4dmxParseNone(MP4DMX* dmx,ATOM* atom)
{
return 0;
}
static int mp4dmxParseFTYP(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[4];
fread(buf,1,4,dmx->fp);
if(MKINT32(buf) == 0x71742020)
{
dmx->qtmux = 1;
}
else
{
dmx->qtmux = 0;
}
fseek(dmx->fp,atom->size - 4,SEEK_CUR);
return 0;
}
static int mp4dmxParseMVHD(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[28];
mp4dmxReadVersionFlags(dmx,atom);
if(atom->version)
{
fread(buf,1,28,dmx->fp);
dmx->mvhd.creation_time = MKINT64(buf);
dmx->mvhd.modification_time = MKINT64(buf + 8);
dmx->mvhd.timescale = MKINT32(buf + 16);
dmx->mvhd.duration = MKINT64(buf + 20);
}
else
{
fread(buf,1,16,dmx->fp);
dmx->mvhd.creation_time = MKINT32(buf);
dmx->mvhd.modification_time = MKINT32(buf + 4);
dmx->mvhd.timescale = MKINT32(buf + 8);
dmx->mvhd.duration = MKINT32(buf + 12);
}
fseek(dmx->fp,80,SEEK_CUR);
return 0;
}
static int mp4dmxParseTKHD(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[32];
track_t* trk = &dmx->tracks[dmx->num_tracks++];
dmx->current = trk;
mp4dmxReadVersionFlags(dmx,atom);
if(atom->version)
{
fread(buf,1,32,dmx->fp);
trk->creation_time = MKINT64(buf);
trk->modification_time = MKINT64(buf + 8);
trk->track_id = MKINT32(buf + 16);
trk->duration = MKINT64(buf + 20);
}
else
{
fread(buf,1,20,dmx->fp);
trk->creation_time = MKINT32(buf);
trk->modification_time = MKINT32(buf + 4);
trk->track_id = MKINT32(buf + 8);
trk->duration = MKINT32(buf + 12);
}
fseek(dmx->fp,52,SEEK_CUR);
fread(buf,1,8,dmx->fp);
trk->width = LE16(buf);
trk->height = LE16(buf + 4);
return 0;
}
static int mp4dmxParseMDHD(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[32];
track_t* trk = dmx->current;
mp4dmxReadVersionFlags(dmx,atom);
if(atom->version)
{
fread(buf,1,32,dmx->fp);
trk->creation_time = MKINT64(buf);
trk->modification_time = MKINT64(buf + 8);
trk->timescale = MKINT32(buf + 16);
trk->duration = MKINT64(buf + 20);
}
else
{
fread(buf,1,16,dmx->fp);
trk->creation_time = MKINT32(buf);
trk->modification_time = MKINT32(buf + 4);
trk->timescale = MKINT32(buf + 8);
trk->duration = MKINT32(buf + 12);
}
fread(buf,1,4,dmx->fp);
trk->language = LE16(buf) & 0x07ff;
return 0;
}
static int mp4dmxParseHDLR(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[24];
uint32_t type;
track_t* trk = dmx->current;
mp4dmxReadVersionFlags(dmx,atom);
fread(buf,1,20,dmx->fp);
type = MKINT32(buf + 4);
if((type == FOURCC('s','o','u','n')) || (type == FOURCC('v','i','d','e')) )
{
trk->handler_type = type;
}
fseek(dmx->fp,atom->size - 24,SEEK_CUR);
return 0;
}
static int mp4dmxParseMP4A(MP4DMX* dmx,ATOM* atom)
{
/*mp4a section formats:
size(32) mp4a(32) reserved(32) reserved(16) dref_id(16) version(16) revision(16) vendor(32)
channels(16) bits_per_sample(16) packet_size(16) sample_rate(32) [16 bytes reserved in version 1,36 bytes reserved in version 2,no reserved byte in version 0]
*/
uint32_t version;
uint8_t buf[12];
track_t* trk = dmx->current;
if(atom->size > 16)
{
fseek(dmx->fp,8,SEEK_CUR);
fread(buf,1,2,dmx->fp);
version = MKINT16(buf);
fseek(dmx->fp,6,SEEK_CUR);
fread(buf,1,12,dmx->fp);
trk->channels = MKINT16(buf);
trk->samplesize = MKINT16(buf + 2);
trk->samplerate = MKINT16(buf + 8);
if(version == 1)
{
fseek(dmx->fp,16,SEEK_CUR);
}
else if(version == 2)
{
fseek(dmx->fp,36,SEEK_CUR);
}
}
else
{
fseek(dmx->fp,atom->size,SEEK_CUR);
}
return 0;
}
static int mp4dmxParseAVCC(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[8];
int i;
int size = atom->size;
uint8_t* extra;
uint32_t num_sps,num_pps;
uint16_t len;
track_t* trk = dmx->current;
fread(buf,1,6,dmx->fp);
trk->nal_size = (buf[4] & 0x03) + 1;
num_sps = buf[5] & 0x1f;
extra = trk->extra_data = (uint8_t*)malloc(64 * 1024);
trk->extra_data_size = 0;
size -= 6;
for(i = 0; i < num_sps; i ++)
{
fread(buf,1,2,dmx->fp);
len = LE16(buf);
memcpy(extra,"\0\0\0\1",4);
fread(extra + 4,1,len,dmx->fp);
trk->extra_data_size += len + 4;
extra += len + 4;
size -= 2 + len;
}
fread(buf,1,1,dmx->fp);
num_pps = buf[0];
size --;
for(i = 0; i < num_pps; i ++)
{
fread(buf,1,2,dmx->fp);
len = LE16(buf);
memcpy(extra,"\0\0\0\1",4);
fread(extra + 4,1,len,dmx->fp);
trk->extra_data_size += len + 4;
extra += len + 4;
size -= 2 + len;
}
fseek(dmx->fp,size,SEEK_CUR);
return 0;
}
static int mp4dmxParseAVC1(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[36];
int size;
track_t* trk = dmx->current;
fseek(dmx->fp,24,SEEK_CUR);
fread(buf,1,18,dmx->fp);
trk->width = LE16(buf);
trk->height = LE16(buf + 2);
fread(buf,1,36,dmx->fp);
size = atom->size - 78;
while(size > 8)
{
mp4dmxReadAtom(dmx,atom);
mp4dmxParseAtom(dmx,atom);
size -= atom->size + 8;
}
fseek(dmx->fp,size,SEEK_CUR);
return 0;
}
static int mp4dmxParseSTSC(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[12];
stsc_t* stsc;
int i;
int num_chunk,first_sample = 1;
track_t* trk = dmx->current;
mp4dmxReadVersionFlags(dmx,atom);
fread(buf,1,4,dmx->fp);
trk->stsc_count = MKINT32(buf);
stsc = trk->stsc = (stsc_t*)malloc(sizeof(stsc_t) * trk->stsc_count);
if(trk->stsc)
{
for(i = 0; i < trk->stsc_count; i ++)
{
fread(buf,1,12,dmx->fp);
stsc->first_sample = 1;
stsc->first_chunk = MKINT32(buf);
stsc->samples_per_chunk = MKINT32(buf + 4);
stsc->samples_desc_index = MKINT32(buf + 8);
stsc ++;
}
stsc = trk->stsc;
for(i = 0; i < trk->stsc_count - 1; i ++,stsc++)
{
num_chunk = (stsc + 1)->first_chunk - stsc->first_chunk;
stsc->first_sample = first_sample;
first_sample += stsc->samples_per_chunk * num_chunk;
}
}
else
{
fseek(dmx->fp,atom->size - 8,SEEK_CUR);
}
return 0;
}
static int mp4dmxParseSTCO(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[4];
stco_t* stco;
int i;
track_t* trk = dmx->current;
mp4dmxReadVersionFlags(dmx,atom);
fread(buf,1,4,dmx->fp);
trk->stco_count = MKINT32(buf);
stco = trk->stco = (stco_t*)malloc(sizeof(stco_t) * trk->stco_count);
if(trk->stco)
{
for(i = 0; i < trk->stco_count; i ++)
{
fread(buf,1,4,dmx->fp);
stco->chunk_offset = MKINT32(buf);
stco ++;
}
}
else
{
fseek(dmx->fp,atom->size - 8,SEEK_CUR);
}
return 0;
}
static int mp4dmxParseSTSS(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[4];
stss_t* stss;
int i;
track_t* trk = dmx->current;
mp4dmxReadVersionFlags(dmx,atom);
fread(buf,1,4,dmx->fp);
trk->stss_count = MKINT32(buf);
stss = trk->stss = (stss_t*)malloc(sizeof(stss_t) * trk->stss_count);
if(trk->stss)
{
for(i = 0; i < trk->stss_count; i ++)
{
fread(buf,1,4,dmx->fp);
stss->sample_number = MKINT32(buf);
stss ++;
}
}
else
{
fseek(dmx->fp,atom->size - 8,SEEK_CUR);
}
return 0;
}
static int mp4dmxParseESDS(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[4];
int size = atom->size;
int pos = ftell(dmx->fp);
track_t* trk = dmx->current;
mp4dmxReadVersionFlags(dmx,atom);
while(size > 2)
{
fread(buf,1,1,dmx->fp);
switch(buf[0])
{
case 0x03:/*ESDescriptor_tag(8) + len(8) + esid(16) + priorty(8)*/
fseek(dmx->fp,4,SEEK_CUR);
size -= 5;
break;
case 0x04:/*DecoderConfigDescrTag(8) + len(8) + ObjectType(8) + streamType(8) + buffersize(24) + maxbitrate(32) + avgbitrage(32)*/
fseek(dmx->fp,14,SEEK_CUR);
size -= 15;
break;
case 0x05:/*DecodeSpecificInfoTag(8) + len(8) + infos(len * 8)*/
fread(buf,1,1,dmx->fp);
trk->extra_data = (uint8_t*)malloc(buf[0]);
trk->extra_data_size = buf[0];
fread(trk->extra_data,1,trk->extra_data_size,dmx->fp);
size -= 2 + buf[0];
break;
case 0x06:/*SLConfigDescriptorTag(8) + len(8) + infos(len * 8)*/
default:
size = 0;
break;
}
}
fseek(dmx->fp,pos,SEEK_SET);
fseek(dmx->fp,atom->size,SEEK_CUR);
return 0;
}
static int mp4dmxParseSTSZ(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[8];
stsz_t* stsz;
int i;
uint32_t sample_size,sample_count;
track_t* trk = dmx->current;
mp4dmxReadVersionFlags(dmx,atom);
fread(buf,1,8,dmx->fp);
sample_size = MKINT32(buf);
sample_count = MKINT32(buf + 4);
trk->stsz_count = sample_count;
stsz = trk->stsz = (stsz_t*)malloc(sizeof(stsz_t) * trk->stsz_count);
if(trk->stsz)
{
if(!sample_size)
{
for(i = 0; i < trk->stsz_count; i ++)
{
fread(buf,1,4,dmx->fp);
stsz->sample_size = MKINT32(buf);
stsz ++;
}
}
else
{
for(i = 0; i < trk->stsz_count; i ++)
{
stsz->sample_size = sample_size;
stsz ++;
}
}
}
else
{
fseek(dmx->fp,atom->size - 12,SEEK_CUR);
}
return 0;
}
static int mp4dmxParseSTTS(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[8];
stts_t* stts;
int i;
track_t* trk = dmx->current;
mp4dmxReadVersionFlags(dmx,atom);
fread(buf,1,4,dmx->fp);
trk->stts_count = MKINT32(buf);
stts = trk->stts = (stts_t*)malloc(sizeof(stts_t) * trk->stts_count);
if(trk->stts)
{
for(i = 0; i < trk->stts_count; i ++)
{
fread(buf,1,8,dmx->fp);
stts->sample_count = MKINT32(buf);
stts->sample_delta = MKINT32(buf + 4);
stts ++;
}
}
else
{
fseek(dmx->fp,atom->size - 8,SEEK_CUR);
}
return 0;
}
static int mp4dmxParseSTSD(MP4DMX* dmx,ATOM* atom)
{
uint8_t buf[24];
int i;
track_t* trk = dmx->current;
ATOM fmt;
mp4dmxReadVersionFlags(dmx,atom);
fread(buf,1,4,dmx->fp);
i = LE32(buf);
while(i--)
{
switch(trk->handler_type)
{
case FOURCC('s','o','u','n'):
if(!dmx->audio)
{
dmx->audio = trk;
}
mp4dmxReadAtom(dmx,&fmt);
trk->codec = fmt.type;/*codec type should detects by esds,if it exists,other wise it is the default!*/
return mp4dmxParseAtom(dmx,&fmt);
break;
case FOURCC('v','i','d','e'):
if(!dmx->video)
{
dmx->video = trk;
}
mp4dmxReadAtom(dmx,&fmt);
trk->codec = fmt.type;/*codec type should detects by esds,if it exists,other wise it is the default!*/
return mp4dmxParseAtom(dmx,&fmt);
default:
fseek(dmx->fp,atom->size - 8,SEEK_CUR);
return 0;
}
}
fseek(dmx->fp,atom->size,SEEK_CUR);
return 0;
}
static int mp4dmxParseAtom(MP4DMX* dmx,ATOM* atom)
{
typedef int (*ATOMPARSER)(MP4DMX* dmx,ATOM* atom);
typedef struct
{
uint32_t type;
ATOMPARSER parse;
}ATOMCALLBACK;
ATOMCALLBACK callbacks[] =
{
{FOURCC_ftyp,mp4dmxParseFTYP},
{FOURCC_moov,mp4dmxParseNone},
{FOURCC_mvhd,mp4dmxParseMVHD},
{FOURCC_trak,mp4dmxParseNone},
{FOURCC_tkhd,mp4dmxParseTKHD},
{FOURCC_edts,mp4dmxParseNone},
{FOURCC_mdia,mp4dmxParseNone},
{FOURCC_mdhd,mp4dmxParseMDHD},
{FOURCC_hdlr,mp4dmxParseHDLR},
{FOURCC_minf,mp4dmxParseNone},
{FOURCC_dinf,mp4dmxParseNone},
{FOURCC_stbl,mp4dmxParseNone},
{FOURCC_stsd,mp4dmxParseSTSD},
{FOURCC_mp4a,mp4dmxParseMP4A},
{FOURCC_avc1,mp4dmxParseAVC1},
{FOURCC_avcC,mp4dmxParseAVCC},
{FOURCC_stts,mp4dmxParseSTTS},
{FOURCC_stsc,mp4dmxParseSTSC},
{FOURCC_stco,mp4dmxParseSTCO},
{FOURCC_stss,mp4dmxParseSTSS},
{FOURCC_stsz,mp4dmxParseSTSZ},
{FOURCC_wave,mp4dmxParseNone},
{FOURCC_frma,mp4dmxParseSkip},
{FOURCC_esds,mp4dmxParseESDS},
{FOURCC_mp4v,mp4dmxParseAVC1},
};
int i;
printf("ATOM:type = %c%c%c%c,size = %I64d\n",atom->type >> 24,atom->type >> 16,atom->type >> 8,atom->type & 0xff,atom->size);
for(i = 0; i < sizeof(callbacks) / sizeof(ATOMCALLBACK); i ++)
{
if(atom->type == callbacks[i].type)
{
return callbacks[i].parse(dmx,atom);
}
}
return mp4dmxParseSkip(dmx,atom);
}
static int mp4dmxParseHeader(MP4DMX* dmx)
{
ATOM atom;
while(0 == mp4dmxReadAtom(dmx,&atom))
{
mp4dmxParseAtom(dmx,&atom);
}
return 0;
}
void* mp4dmxCreate(char* filename)
{
int i;
MP4DMX* dmx = (MP4DMX*)malloc(sizeof(MP4DMX));
if(dmx)
{
memset(dmx,0,sizeof(MP4DMX));
dmx->fp = fopen(filename,"rb");
if(!dmx->fp)
{
free(dmx);
return NULL;
}
mp4dmxParseHeader(dmx);
}
return dmx;
}
static int mp4dmxQueryNextSampleInfo(track_t* trk,uint32_t* offset,uint32_t* size,uint32_t* pts)
{
stsc_t* stsc;
stts_t* stts;
uint32_t chunk;
uint32_t num_chunk;
uint32_t sample_last;
int i,j;
if(trk->sample_idx > trk->stsz_count)
{
return -1;
}
if(trk->stsc_count < 2)
{
uint32_t first_sample = 1;
int idx = trk->stsc->samples_desc_index;
trk->stsc_count = trk->stsz_count;
trk->stsc = (stsc_t*)realloc(trk->stsc,sizeof(stsc_t) * trk->stsz_count);
stsc = trk->stsc;
for(i = 0; i < trk->stsc_count - 1; i ++,stsc++)
{
stsc->first_sample = first_sample;
stsc->first_chunk = i + 1;
stsc->samples_per_chunk = 1;
stsc->samples_desc_index = idx;
first_sample += stsc->samples_per_chunk;
}
}
*size = trk->stsz[trk->sample_idx - 1].sample_size;
*pts = 0;
sample_last = 1;
for(stts = trk->stts,i = 0; i < trk->stts_count; i ++,stts++)
{
for(j = 0; j < stts->sample_count; j ++,sample_last++)
{
if(sample_last == trk->sample_idx)
{
i = trk->stts_count;
break;
}
(*pts) += stts->sample_delta;
}
}
(*pts) = (*pts) / (double)trk->timescale * 1000.0f;
*offset = 0;
for(stsc = trk->stsc,i = 0; i < trk->stsc_count; i ++,stsc++)
{
if(i < trk->stsc_count - 1)
{
num_chunk = (stsc + 1)->first_chunk - stsc->first_chunk;
}
else
{
num_chunk = 1;
}
sample_last = stsc->first_sample + num_chunk * stsc->samples_per_chunk;
if(trk->sample_idx >= stsc->first_sample && trk->sample_idx < sample_last)
{
chunk = stsc->first_chunk + (trk->sample_idx - stsc->first_sample) / stsc->samples_per_chunk;
for(i = stsc->first_sample + (chunk - stsc->first_chunk) * stsc->samples_per_chunk; i < trk->sample_idx; i ++)
{
(*offset) += trk->stsz[i - 1].sample_size;
}
(*offset) += trk->stco[chunk - 1].chunk_offset;
return 0;
}
}
return -1;
}
static int mp4dmxBuildADTSHeader(unsigned char* buf,int samplerate,int num_channels,int size)
{
int idx;
size += 7;
if(samplerate <= 8000)
{
idx = 0x0b;
}
else if(samplerate <= 11025)
{
idx = 0x0a;
}
else if(samplerate <= 12000)
{
idx = 0x09;
}
else if(samplerate <= 16000)
{
idx = 0x08;
}
else if(samplerate <= 22050)
{
idx = 0x07;
}
else if(samplerate <= 24000)
{
idx = 0x06;
}
else if(samplerate <= 32000)
{
idx = 0x05;
}
else if(samplerate <= 44100)
{
idx = 0x04;
}
else if(samplerate <= 48000)
{
idx = 0x03;
}
else if(samplerate <= 64000)
{
idx = 0x02;
}
else if(samplerate <= 88200)
{
idx = 0x01;
}
else
{
idx = 0x00;
}
buf[0] = 0xff;
buf[1] = 0xf9;/*MPEG2-AAC*/
buf[2] = (0x01 << 6) | (idx << 2) | (num_channels >> 2); /*D7 - D6:profile 01,D5 - D2:idx,D1:1,D0:channels >> 2*/
buf[3] = ((num_channels & 0x03) << 6) | ((size >> 11) & 0x03);/*D7 - D6:channels & 0x03,D5 - D4:0 0 ,D3 - D2: 0 0,D1 - D0:size >> 11*/
buf[4] = ((size >> 3) & 0xff);/*(size >> 3) & 0xff*/
buf[5] = ((size & 0x07) << 5) | 0x0f;/*D7 - D5:size & 0x07,D4 - D0:bufsize >> 6*/
buf[6] = (0x3f << 2);/*D7 - D2:buszie & 0x3f, D1 - D0:00*/
return 7;
}
static int mp4dmxQueryNextSample(MP4DMX* dmx,track_t* trk,uint8_t* buf,uint32_t offset,int32_t size)
{
uint8_t sbuf[4];
int len;
int result = size;
fseek(dmx->fp,offset,SEEK_SET);
switch(trk->codec)
{
case FOURCC_avc1:
while(size > 0)
{
fread(sbuf,1,trk->nal_size,dmx->fp);
switch(trk->nal_size)
{
case 1:
len = sbuf[0];
break;
case 2:
len = LE16(sbuf);
break;
default:
len = LE32(sbuf);
break;
}
memcpy(buf,"\0\0\0\1",4);
fread(buf + 4,1,len,dmx->fp);
buf += 4 + len;
size -= len + trk->nal_size;
}
break;
case FOURCC_mp4a:
mp4dmxBuildADTSHeader(buf,trk->samplerate,trk->channels,size);
buf += 7;
result += 7;
default:
fread(buf,1,size,dmx->fp);
break;
}
return result;
}
int mp4dmxGetVideoPacket(void* hdl,char* buf,uint32_t* pts)
{
uint32_t offset,size;
MP4DMX* dmx = (MP4DMX*)hdl;
if(dmx->video)
{
if(dmx->video->extra_data)
{
if(dmx->video->sample_idx == 0)
{
*pts = 0;
++dmx->video->sample_idx;
memcpy(buf,dmx->video->extra_data,dmx->video->extra_data_size);
return dmx->video->extra_data_size;
}
}
else
{
if(dmx->video->sample_idx == 0)
{
++dmx->video->sample_idx;
}
}
if(0 == mp4dmxQueryNextSampleInfo(dmx->video,&offset,&size,pts))
{
++dmx->video->sample_idx;
return mp4dmxQueryNextSample(dmx,dmx->video,buf,offset,size);
}
}
return -1;
}
int mp4dmxGetAudioPacket(void* hdl,char* buf,uint32_t* pts)
{
uint32_t offset,size;
MP4DMX* dmx = (MP4DMX*)hdl;
if(dmx->audio)
{
*pts = 0;
if(dmx->audio->extra_data)
{
if(dmx->audio->sample_idx == 0)
{
++dmx->audio->sample_idx;
memcpy(buf,dmx->audio->extra_data,dmx->audio->extra_data_size);
return dmx->audio->extra_data_size;
}
}
else
{
if(dmx->audio->sample_idx == 0)
{
++dmx->audio->sample_idx;
}
}
if(0 == mp4dmxQueryNextSampleInfo(dmx->audio,&offset,&size,pts))
{
++dmx->audio->sample_idx;
return mp4dmxQueryNextSample(dmx,dmx->audio,buf,offset,size);
}
}
return -1;
}
int mp4dmxDestroy(void* hdl)
{
int i;
MP4DMX* dmx = (MP4DMX*)hdl;
for(i = 0; i < sizeof(dmx->tracks) / sizeof(track_t); i ++)
{
if(dmx->tracks[i].extra_data)
{
free(dmx->tracks[i].extra_data);
}
if(dmx->tracks[i].stts)
{
free(dmx->tracks[i].stts);
}
if(dmx->tracks[i].stsc)
{
free(dmx->tracks[i].stsc);
}
if(dmx->tracks[i].stco)
{
free(dmx->tracks[i].stco);
}
if(dmx->tracks[i].stss)
{
free(dmx->tracks[i].stss);
}
if(dmx->tracks[i].stsz)
{
free(dmx->tracks[i].stsz);
}
}
fclose(dmx->fp);
free(dmx);
return 0;
}
static char buf[1024 * 1024 * 2];
int main()
{
int i;
int size;
uint32_t pts;
int noVideo = 0,noAudio = 0;
int first_video_pts = -1,last_video_pts;
int first_audio_pts = -1,last_audio_pts;
int video_frames_count = 0;
int audio_frames_count = 0;
FILE* vo = fopen("c:\\test.video","wb");
FILE* ao = fopen("c:\\test.audio","wb");
MP4DMX* dmx = mp4dmxCreate("c:\\panda.mov");
printf("Extract Video & Audio Streams now,please wait...\n");
while(dmx && (!noAudio || !noVideo))
{
size = mp4dmxGetVideoPacket(dmx,buf,&last_video_pts);
if(size > 0)
{
if(first_video_pts == -1)
{
first_video_pts = last_video_pts;
}
noVideo = 0;
fwrite(buf,1,size,vo);
video_frames_count ++;
}
else
{
noVideo = 1;
}
size = mp4dmxGetAudioPacket(dmx,buf,&last_audio_pts);
if(size > 0)
{
if(first_audio_pts == -1)
{
first_audio_pts = last_audio_pts;
}
noAudio = 0;
fwrite(buf,1,size,ao);
audio_frames_count ++;
}
else
{
noAudio = 1;
}
}
fclose(vo);
fclose(ao);
mp4dmxDestroy(dmx);
printf("Video %d frames extracted,duration = %8.2f seconds!\r\nAudio %d frames extracted,duration = %8.2f seconds!\n",video_frames_count,(last_video_pts - first_video_pts ) / 1000.0f,audio_frames_count,(last_audio_pts - first_audio_pts ) / 1000.0f);
return 0;
}
#endif