Skip to content

Commit

Permalink
Refine mp4 codec for mdat.
Browse files Browse the repository at this point in the history
  • Loading branch information
winlinvip committed Apr 16, 2017
1 parent 18d9f6c commit c9bed5a
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 39 deletions.
1 change: 1 addition & 0 deletions trunk/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/doc/frozen*.flv
/doc/kungfupanda*.flv
/doc/time*.flv
/doc/source*.mp4
/html
/ide/srs_xcode/srs_xcode.xcodeproj/project.xcworkspace/xcshareddata/
/ide/srs_xcode/srs_xcode.xcodeproj/project.xcworkspace/xcuserdata/
Expand Down
83 changes: 57 additions & 26 deletions trunk/src/kernel/srs_kernel_mp4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ uint64_t SrsMp4Box::sz()
return smallsize == SRS_MP4_USE_LARGE_SIZE? largesize:smallsize;
}

int SrsMp4Box::sz_header()
{
return nb_header();
}

int SrsMp4Box::left_space(SrsBuffer* buf)
{
return (int)sz() - (buf->pos() - start_pos);
Expand Down Expand Up @@ -193,7 +198,6 @@ int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox)
switch(type) {
case SrsMp4BoxTypeFTYP: box = new SrsMp4FileTypeBox(); break;
case SrsMp4BoxTypeMDAT: box = new SrsMp4MediaDataBox(); break;
case SrsMp4BoxTypeFREE: case SrsMp4BoxTypeSKIP: box = new SrsMp4FreeSpaceBox(); break;
case SrsMp4BoxTypeMOOV: box = new SrsMp4MovieBox(); break;
case SrsMp4BoxTypeMVHD: box = new SrsMp4MovieHeaderBox(); break;
case SrsMp4BoxTypeTRAK: box = new SrsMp4TrackBox(); break;
Expand Down Expand Up @@ -226,6 +230,9 @@ int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox)
case SrsMp4BoxTypeUDTA: box = new SrsMp4UserDataBox(); break;
case SrsMp4BoxTypeMVEX: box = new SrsMp4MovieExtendsBox(); break;
case SrsMp4BoxTypeTREX: box = new SrsMp4TrackExtendsBox(); break;
// Skip some unknown boxes.
case SrsMp4BoxTypeFREE: case SrsMp4BoxTypeSKIP: case SrsMp4BoxTypePASP:
box = new SrsMp4FreeSpaceBox(); break;
default:
ret = ERROR_MP4_BOX_ILLEGAL_TYPE;
srs_error("MP4 illegal box type=%d. ret=%d", type, ret);
Expand Down Expand Up @@ -259,7 +266,7 @@ int SrsMp4Box::encode(SrsBuffer* buf)
{
int ret = ERROR_SUCCESS;

uint64_t size = encode_actual_size();
uint64_t size = nb_bytes();
if (size > 0xffffffff) {
largesize = size;
} else {
Expand Down Expand Up @@ -407,7 +414,7 @@ int SrsMp4Box::decode_header(SrsBuffer* buf)
type = (SrsMp4BoxType)buf->read_4bytes();

if (smallsize == SRS_MP4_EOF_SIZE) {
srs_warn("MP4 box EOF.");
srs_trace("MP4 box EOF.");
return ret;
}

Expand Down Expand Up @@ -448,11 +455,6 @@ int SrsMp4Box::decode_header(SrsBuffer* buf)
return ret;
}

uint64_t SrsMp4Box::encode_actual_size()
{
return nb_bytes();
}

SrsMp4FullBox::SrsMp4FullBox()
{
version = 0;
Expand Down Expand Up @@ -589,20 +591,29 @@ int SrsMp4FileTypeBox::decode_header(SrsBuffer* buf)
SrsMp4MediaDataBox::SrsMp4MediaDataBox()
{
type = SrsMp4BoxTypeMDAT;
data = NULL;
nb_data = 0;
}

SrsMp4MediaDataBox::~SrsMp4MediaDataBox()
{
srs_freepa(data);
}

uint64_t SrsMp4MediaDataBox::encode_actual_size()
int SrsMp4MediaDataBox::nb_bytes()
{
return SrsMp4Box::nb_header() + nb_data;
}

int SrsMp4MediaDataBox::encode(SrsBuffer* buf)
{
int ret = ERROR_SUCCESS;

if ((ret = SrsMp4Box::encode(buf)) != ERROR_SUCCESS) {
return ret;
}

return ret;
}

int SrsMp4MediaDataBox::decode(SrsBuffer* buf)
{
int ret = ERROR_SUCCESS;
Expand All @@ -611,11 +622,23 @@ int SrsMp4MediaDataBox::decode(SrsBuffer* buf)
return ret;
}

nb_data = left_space(buf);
nb_data = (int)(sz() - nb_header());

// Because the

return ret;
}

int SrsMp4MediaDataBox::encode_boxes(SrsBuffer* buf)
{
return ERROR_SUCCESS;
}

int SrsMp4MediaDataBox::decode_boxes(SrsBuffer* buf)
{
return ERROR_SUCCESS;
}

SrsMp4FreeSpaceBox::SrsMp4FreeSpaceBox()
{
type = SrsMp4BoxTypeFREE; // 'free' or 'skip'
Expand All @@ -627,7 +650,7 @@ SrsMp4FreeSpaceBox::~SrsMp4FreeSpaceBox()

int SrsMp4FreeSpaceBox::nb_header()
{
return SrsMp4Box::nb_header() + data.size();
return SrsMp4Box::nb_header() + (int)data.size();
}

int SrsMp4FreeSpaceBox::encode_header(SrsBuffer* buf)
Expand All @@ -639,7 +662,7 @@ int SrsMp4FreeSpaceBox::encode_header(SrsBuffer* buf)
}

if (!data.empty()) {
buf->write_bytes(&data[0], data.size());
buf->write_bytes(&data[0], (int)data.size());
}

return ret;
Expand Down Expand Up @@ -4184,7 +4207,13 @@ int SrsMp4Decoder::do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_typ

SrsMp4Box* box = NULL;
while (true) {
// For the first time to read the box, maybe it's a basic box which is only 4bytes header.
// When we disconvery the real box, we know the size of the whole box, then read again and decode it.
uint64_t required = box? box->sz():4;
// For mdat box, we only requires to decode the header.
if (box && box->is_mdat()) {
required = box->sz_header();
}
while (stream->length() < (int)required) {
ssize_t nread;
if ((ret = rsio->read(buf, SRS_MP4_BUF_SIZE, &nread)) != ERROR_SUCCESS) {
Expand All @@ -4208,15 +4237,17 @@ int SrsMp4Decoder::do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_typ
return ret;
}

// Util we can demux the whole box.
// When box is discoveried, check whether we can demux the whole box.
// For mdat, only the header is required.
if (!box->is_mdat()) {
if (!buffer->require((int)box->sz())) {
continue;
}
required = (box->is_mdat()? box->sz_header():box->sz());
if (!buffer->require((int)required)) {
continue;
}

// Decode the matched box or any box is matched.
// Decode the box:
// 1. Any box, when no box type is required.
// 2. Matched box, when box type match the required type.
// 3. Mdat box, always decode the mdat because we only decode the header of it.
if (!required_box_type || box->type == required_box_type || box->is_mdat()) {
ret = box->decode(buffer);
}
Expand Down Expand Up @@ -4316,7 +4347,7 @@ int SrsMp4Encoder::initialize(ISrsWriteSeeker* ws)
return ret;
}

int nb_data = mdat->nb_bytes();
int nb_data = mdat->sz_header();
uint8_t* data = new uint8_t[nb_data];
SrsAutoFreeA(uint8_t, data);
if ((ret = buffer->initialize((char*)data, nb_data)) != ERROR_SUCCESS) {
Expand Down Expand Up @@ -4573,13 +4604,13 @@ int SrsMp4Encoder::flush()
return ret;
}

// Write empty mdat box,
// its payload will be writen by samples,
// Write mdat box with size of data,
// its payload already writen by samples,
// and we will update its header(size) when flush.
SrsMp4MediaDataBox* mdat = new SrsMp4MediaDataBox();
SrsAutoFree(SrsMp4MediaDataBox, mdat);

int nb_data = mdat->nb_bytes();
int nb_data = mdat->sz_header();
uint8_t* data = new uint8_t[nb_data];
SrsAutoFreeA(uint8_t, data);
if ((ret = buffer->initialize((char*)data, nb_data)) != ERROR_SUCCESS) {
Expand All @@ -4606,7 +4637,7 @@ int SrsMp4Encoder::copy_sequence_header(bool vsh, uint8_t* sample, uint32_t nb_s
int ret = ERROR_SUCCESS;

if (vsh && !pavcc.empty()) {
if (nb_sample == pavcc.size() && srs_bytes_equals(sample, &pavcc[0], pavcc.size())) {
if (nb_sample == pavcc.size() && srs_bytes_equals(sample, &pavcc[0], (int)pavcc.size())) {
return ret;
}

Expand All @@ -4616,7 +4647,7 @@ int SrsMp4Encoder::copy_sequence_header(bool vsh, uint8_t* sample, uint32_t nb_s
}

if (!vsh && !pasc.empty()) {
if (nb_sample == pasc.size() && srs_bytes_equals(sample, &pasc[0], pasc.size())) {
if (nb_sample == pasc.size() && srs_bytes_equals(sample, &pasc[0], (int)pasc.size())) {
return ret;
}

Expand Down
73 changes: 60 additions & 13 deletions trunk/src/kernel/srs_kernel_mp4.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ enum SrsMp4BoxType
SrsMp4BoxTypeUDTA = 0x75647461, // 'udta'
SrsMp4BoxTypeMVEX = 0x6d766578, // 'mvex'
SrsMp4BoxTypeTREX = 0x74726578, // 'trex'

SrsMp4BoxTypePASP = 0x70617370, // 'pasp'
};

/**
Expand Down Expand Up @@ -172,7 +174,11 @@ class SrsMp4Box : public ISrsCodec
virtual ~SrsMp4Box();
public:
// Get the size of box, whatever small or large size.
// @remark For general box(except mdat), we use this sz() to create the buffer to codec it.
virtual uint64_t sz();
// Get the size of header, without contained boxes.
// @remark For mdat box, we must codec its header, use this instead of sz().
virtual int sz_header();
// Get the left space of box, for decoder.
virtual int left_space(SrsBuffer* buf);
// Box type helper.
Expand All @@ -198,7 +204,8 @@ class SrsMp4Box : public ISrsCodec
protected:
virtual int encode_boxes(SrsBuffer* buf);
virtual int decode_boxes(SrsBuffer* buf);
// Sub classes can override these functions for special codec.
// Sub classes can override these functions for special codec.
// @remark For mdat box, we use completely different codec.
protected:
// The size of header, not including the contained boxes.
virtual int nb_header();
Expand All @@ -208,13 +215,6 @@ class SrsMp4Box : public ISrsCodec
// It's not necessary to check the buffer, unless the box is not only determined by the verson.
// Generally, it's not necessary, that is, all boxes is determinated by version.
virtual int decode_header(SrsBuffer* buf);
protected:
// The actual size of this box, generally it must equal to nb_bytes,
// but for some special boxes, for instance mdat, the box encode actual size maybe large than
// the nb_bytes to write, because the data is written directly.
// That is, the actual size is used to encode the box size in header,
// while the nb_bytes is the bytes encoded the box.
virtual uint64_t encode_actual_size();
};

/**
Expand Down Expand Up @@ -273,22 +273,69 @@ class SrsMp4FileTypeBox : public SrsMp4Box
* A presentation may contain zero or more Media Data Boxes. The actual media data follows the type field;
* its structure is described by the metadata (see particularly the sample table, subclause 8.5, and the
* item location box, subclause 8.11.3).
*
* @remark The mdat box only decode and encode the header,
* so user must read and write the data by yourself.
* To encode mdat:
* SrsMp4MediaDataBox* mdat = new SrsMp4MediaDataBox();
* mdat->nb_data = 1024000;
*
* char* buffer = new char[mdat->sz_header()];
* SrsBuffer* buf = new SrsBuffer(buffer);
* mdat->encode(buf);
*
* file->write(buffer, mdat->sz_header()); // Write the mdat box header.
* file->write(data, size); // Write the mdat box data.
*
* To decode mdat:
* SrsMp4MediaDataBox* mdat = new SrsMp4MediaDataBox();
* char* buffer = new char[mdat->sz_header()];
* SrsBuffer* buf = ...; // Read mdat->sz_header() data from io.
*
* mdat->decode(buf); // The buf should be empty now.
* file->lseek(mdat->nb_data, SEEK_CUR); // Skip the mdat data in file.
*
* To discovery any box from file:
* SrsSimpleStream* stream = new SrsSimpleStream();
* SrsBuffer* buf = new SrsBuffer(stream...); // Create read buffer from stream.
*
* // We don't know what's the next box, so try to read 4bytes and discovery it.
* append(file, stream, 4); // Append 4bytes from file to stream.
*
* SrsMp4Box* box = NULL;
* SrsMp4Box::discovery(buf, &box);
*
* required = (box->is_mdat()? box->sz_header():box->sz()); // Now we know how many bytes we needed.
* append(file, stream, required);
* box->decode(buf);
*
* if (box->is_mdat()) {
* file->lseek(mdat->nb_data, SEEK_CUR); // Skip the mdat data in file.
* }
*/
class SrsMp4MediaDataBox : public SrsMp4Box
{
public:
// the contained media data
// The contained media data, which we never directly read/write it.
// TODO: FIXME: Support 64bits size.
int nb_data;
// @remark User must alloc the data and codec it.
uint8_t* data;
public:
SrsMp4MediaDataBox();
virtual ~SrsMp4MediaDataBox();
protected:
virtual uint64_t encode_actual_size();
// Interface ISrsCodec
public:
// The total size of bytes, including the sz_header() and nb_data,
// which used to write the smallsize or largesize of mp4.
virtual int nb_bytes();
// To encode the mdat box, the buf should only contains the sz_header(),
// because the mdata only encode the header.
virtual int encode(SrsBuffer* buf);
// To decode the mdat box, the buf should only contains the sz_header(),
// because the mdat only decode the header.
virtual int decode(SrsBuffer* buf);
protected:
virtual int encode_boxes(SrsBuffer* buf);
virtual int decode_boxes(SrsBuffer* buf);
};

/**
Expand Down

0 comments on commit c9bed5a

Please sign in to comment.