diff --git a/trunk/.gitignore b/trunk/.gitignore index 8d1f8e4ec0..df86e17d29 100644 --- a/trunk/.gitignore +++ b/trunk/.gitignore @@ -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/ diff --git a/trunk/src/kernel/srs_kernel_mp4.cpp b/trunk/src/kernel/srs_kernel_mp4.cpp index 5fe6366ed9..7293f057b6 100644 --- a/trunk/src/kernel/srs_kernel_mp4.cpp +++ b/trunk/src/kernel/srs_kernel_mp4.cpp @@ -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); @@ -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; @@ -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); @@ -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 { @@ -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; } @@ -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; @@ -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; @@ -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' @@ -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) @@ -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; @@ -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) { @@ -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); } @@ -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) { @@ -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) { @@ -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; } @@ -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; } diff --git a/trunk/src/kernel/srs_kernel_mp4.hpp b/trunk/src/kernel/srs_kernel_mp4.hpp index 2b830613eb..51dc1dda8b 100644 --- a/trunk/src/kernel/srs_kernel_mp4.hpp +++ b/trunk/src/kernel/srs_kernel_mp4.hpp @@ -110,6 +110,8 @@ enum SrsMp4BoxType SrsMp4BoxTypeUDTA = 0x75647461, // 'udta' SrsMp4BoxTypeMVEX = 0x6d766578, // 'mvex' SrsMp4BoxTypeTREX = 0x74726578, // 'trex' + + SrsMp4BoxTypePASP = 0x70617370, // 'pasp' }; /** @@ -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. @@ -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(); @@ -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(); }; /** @@ -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); }; /**