/*******************************************************************************
mp4_reader.c - A library for reading MPEG4.
Copyright (C) 2007-2009 CodeShop B.V.
http://www.code-shop.com
For licensing see the LICENSE file
******************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef __cplusplus
#define __STDC_FORMAT_MACROS // C++ should define this for PRIu64
#define __STDC_LIMIT_MACROS // C++ should define this for UINT64_MAX
#endif
#include "mp4_reader.h"
#include "mp4_io.h"
#include <stdlib.h>
#include <string.h>
struct atom_t
{
uint32_t type_;
uint32_t short_size_;
uint64_t size_;
unsigned char* start_;
unsigned char* end_;
};
typedef struct atom_t atom_t;
static void atom_print(mp4_context_t const* mp4_context, atom_t const* atom)
{
MP4_INFO("Atom(%c%c%c%c,%"PRIu64")\n",
atom->type_ >> 24,
atom->type_ >> 16,
atom->type_ >> 8,
atom->type_,
atom->size_);
}
static unsigned char*
atom_read_header(mp4_context_t const* mp4_context, unsigned char* buffer,
atom_t* atom)
{
atom->start_ = buffer;
atom->short_size_ = read_32(buffer);
atom->type_ = read_32(buffer + 4);
if(atom->short_size_ == 1)
atom->size_ = read_64(buffer + 8);
else
atom->size_ = atom->short_size_;
atom->end_ = atom->start_ + atom->size_;
atom_print(mp4_context, atom);
if(atom->size_ < ATOM_PREAMBLE_SIZE)
{
MP4_ERROR("%s", "Error: invalid atom size\n");
return 0;
}
return buffer + ATOM_PREAMBLE_SIZE + (atom->short_size_ == 1 ? 8 : 0);
}
static struct unknown_atom_t* unknown_atom_add_atom(struct unknown_atom_t* parent, void* atom)
{
size_t size = read_32((const unsigned char*)atom);
unknown_atom_t* unknown = unknown_atom_init();
unknown->atom_ = malloc(size);
memcpy(unknown->atom_, atom, size);
{
unknown_atom_t** adder = &parent;
while(*adder != NULL)
{
adder = &(*adder)->next_;
}
*adder = unknown;
}
return parent;
}
extern int atom_reader(struct mp4_context_t const* mp4_context,
struct atom_read_list_t* atom_read_list,
unsigned int atom_read_list_size,
void* parent,
unsigned char* buffer, uint64_t size)
{
atom_t leaf_atom;
unsigned char* buffer_start = buffer;
while(buffer < buffer_start + size)
{
unsigned int i;
buffer = atom_read_header(mp4_context, buffer, &leaf_atom);
if(buffer == NULL)
{
return 0;
}
for(i = 0; i != atom_read_list_size; ++i)
{
if(leaf_atom.type_ == atom_read_list[i].type_)
{
break;
}
}
if(i == atom_read_list_size)
{
// add to unkown chunks
(*(unknown_atom_t**)parent) =
unknown_atom_add_atom(*(unknown_atom_t**)(parent), buffer - ATOM_PREAMBLE_SIZE);
}
else
{
void* child =
atom_read_list[i].reader_(mp4_context, parent, buffer,
leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
if(!child)
break;
if(!atom_read_list[i].destination_(mp4_context, parent, child))
break;
}
buffer = leaf_atom.end_;
}
if(buffer < buffer_start + size)
{
return 0;
}
return 1;
}
static void* ctts_read(mp4_context_t const* UNUSED(mp4_context),
void* UNUSED(parent),
unsigned char* buffer, uint64_t size)
{
unsigned int i;
ctts_t* atom;
if(size < 8)
return 0;
atom = ctts_init();
atom->version_ = read_8(buffer + 0);
atom->flags_ = read_24(buffer + 1);
atom->entries_ = read_32(buffer + 4);
if(size < 8 + atom->entries_ * sizeof(ctts_table_t))
return 0;
buffer += 8;
atom->table_ = (ctts_table_t*)(malloc(atom->entries_ * sizeof(ctts_table_t)));
for(i = 0; i != atom->entries_; ++i)
{
atom->table_[i].sample_count_ = read_32(buffer + 0);
atom->table_[i].sample_offset_ = read_32(buffer + 4);
buffer += 8;
}
return atom;
}
static void* stco_read(mp4_context_t const* UNUSED(mp4_context),
void* UNUSED(parent),
unsigned char* buffer, uint64_t size)
{
unsigned int i;
stco_t* atom;
if(size < 8)
return 0;
atom = stco_init();
atom->version_ = read_8(buffer + 0);
atom->flags_ = read_24(buffer + 1);
atom->entries_ = read_32(buffer + 4);
buffer += 8;
if(size < 8 + atom->entries_ * sizeof(uint32_t))
return 0;
atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t));
for(i = 0; i != atom->entries_; ++i)
{
atom->chunk_offsets_[i] = read_32(buffer);
buffer += 4;
}
return atom;
}
static void* co64_read(mp4_context_t const* UNUSED(mp4_context),
void* UNUSED(parent),
unsigned char* buffer, uint64_t size)
{
unsigned int i;
stco_t* atom;
if(size < 8)
return 0;
atom = stco_init();
atom->version_ = read_8(buffer + 0);
atom->flags_ = read_24(buffer + 1);
atom->entries_ = read_32(buffer + 4);
buffer += 8;
if(size < 8 + atom->entries_ * sizeof(uint64_t))
return 0;
atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t));
for(i = 0; i != atom->entries_; ++i)
{
atom->chunk_offsets_[i] = read_64(buffer);
buffer += 8;
}
return atom;
}
static void* stsz_read(mp4_context_t const* mp4_context,
void* UNUSED(parent),
unsigned char* buffer, uint64_t size)
{
unsigned int i;
stsz_t* atom;
if(size < 12)
{
MP4_ERROR("%s", "Error: not enough bytes for stsz atom\n");
return 0;
}
atom = stsz_init();
atom->version_ = read_8(buffer + 0);
atom->flags_ = read_24(buffer + 1);
atom->sample_size_ = read_32(buffer + 4);
atom->entries_ = read_32(buffer + 8);
buffer += 12;
// fix for clayton.mp4, it mistakenly says there is 1 entry
// if(atom->sample_size_ && atom->entries_)
// atom->entries_ = 0;
if(!atom->sample_size_)
{
if(size < 12 + atom->entries_ * sizeof(uint32_t))
{
MP4_ERROR("%s", "Error: stsz.entries don't match with size\n");
stsz_exit(atom);
return 0;
}
atom->sample_sizes_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t));
for(i = 0; i != atom->entries_; ++i)
{
atom->sample_sizes_[i] = read_32(buffer);
buffer += 4;
}
}
return atom;
}
static void* stsc_read(mp4_context_t const* UNUSED(mp4_context),
void* UNUSED(parent),
unsigned char* buffer, uint64_t size)
{
unsigned int i;
stsc_t* atom;
if(size < 8)
return 0;
atom = stsc_init();
atom->version_ = read_8(buffer + 0);
atom->flags_ = read_24(buffer + 1);
atom->entries_ = read_32(buffer + 4);
if(size < 8 + atom->entries_ * sizeof(stsc_table_t))
return 0;
buffer += 8;
// reserve space for one extra entry as when splitting the video we may have to
// split the first entry
atom->table_ = (stsc_table_t*)(malloc((atom->entries_ + 1) * sizeof(stsc_table_t)));
for(i = 0; i != atom->entries_; ++i)
{
atom->table_[i].chunk_ = read_32(buffer + 0) - 1; // Note: we use zero based
atom->table_[i].samples_ = read_32(buffer + 4);
atom->table_[i].id_ = read_32(buffer + 8);
buffer += 12;
}
return atom;
}
static void* stss_read(mp4_context_t const* UNUSED(mp4_context),
void* UNUSED(parent),
unsigned char* buffer, uint64_t size)
{
unsigned int i;
stss_t* atom;
if(size < 8)
return 0;
atom = stss_init();
atom->version_ = read_8(buffer + 0);
atom->flags_ = read_24(buffer + 1);
atom->entries_ = read_32(buffer + 4);
if(size < 8 + atom->entries_ * sizeof(uint32_t))
return 0;
buffer += 8;
atom->sample_numbers_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t));
for(i = 0; i != atom->entries_; ++i)
{
atom->sample_numbers_[i] = r
- 1
- 2
- 3
前往页