diff --git a/trunk/src/kernel/srs_kernel_mp4.cpp b/trunk/src/kernel/srs_kernel_mp4.cpp index e07100e4c..cc13d7774 100644 --- a/trunk/src/kernel/srs_kernel_mp4.cpp +++ b/trunk/src/kernel/srs_kernel_mp4.cpp @@ -409,6 +409,13 @@ 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; + case SrsMp4BoxTypeSTYP: box = new SrsMp4SegmentTypeBox(); break; + case SrsMp4BoxTypeMOOF: box = new SrsMp4MovieFragmentBox(); break; + case SrsMp4BoxTypeMFHD: box = new SrsMp4MovieFragmentHeaderBox(); break; + case SrsMp4BoxTypeTRAF: box = new SrsMp4TrackFragmentBox(); break; + case SrsMp4BoxTypeTFHD: box = new SrsMp4TrackFragmentHeaderBox(); break; + case SrsMp4BoxTypeTFDT: box = new SrsMp4TrackFragmentDecodeTimeBox(); break; + case SrsMp4BoxTypeTRUN: box = new SrsMp4TrackFragmentRunBox(); break; // Skip some unknown boxes. case SrsMp4BoxTypeFREE: case SrsMp4BoxTypeSKIP: case SrsMp4BoxTypePASP: box = new SrsMp4FreeSpaceBox(type); break; @@ -810,6 +817,478 @@ stringstream& SrsMp4FileTypeBox::dumps_detail(stringstream& ss, SrsMp4DumpContex return ss; } +SrsMp4SegmentTypeBox::SrsMp4SegmentTypeBox() +{ + type = SrsMp4BoxTypeSTYP; +} + +SrsMp4SegmentTypeBox::~SrsMp4SegmentTypeBox() +{ +} + +SrsMp4MovieFragmentBox::SrsMp4MovieFragmentBox() +{ + type = SrsMp4BoxTypeMOOF; +} + +SrsMp4MovieFragmentBox::~SrsMp4MovieFragmentBox() +{ +} + +SrsMp4MovieFragmentHeaderBox::SrsMp4MovieFragmentHeaderBox() +{ + type = SrsMp4BoxTypeMFHD; + + sequence_number = 0; +} + +SrsMp4MovieFragmentHeaderBox::~SrsMp4MovieFragmentHeaderBox() +{ +} + +int SrsMp4MovieFragmentHeaderBox::nb_header() +{ + return SrsMp4FullBox::nb_header() + 4; +} + +int SrsMp4MovieFragmentHeaderBox::encode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + buf->write_4bytes(sequence_number); + + return ret; +} + +int SrsMp4MovieFragmentHeaderBox::decode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + sequence_number = buf->read_4bytes(); + + return ret; +} + +stringstream& SrsMp4MovieFragmentHeaderBox::dumps_detail(stringstream& ss, SrsMp4DumpContext dc) +{ + SrsMp4FullBox::dumps_detail(ss, dc); + + ss << ", sequence=" << sequence_number; + return ss; +} + +SrsMp4TrackFragmentBox::SrsMp4TrackFragmentBox() +{ + type = SrsMp4BoxTypeTRAF; +} + +SrsMp4TrackFragmentBox::~SrsMp4TrackFragmentBox() +{ +} + +SrsMp4TrackFragmentHeaderBox::SrsMp4TrackFragmentHeaderBox() +{ + type = SrsMp4BoxTypeTFHD; + + flags = 0; + base_data_offset = 0; + track_id = sample_description_index = 0; + default_sample_duration = default_sample_size = 0; + default_sample_flags = 0; +} + +SrsMp4TrackFragmentHeaderBox::~SrsMp4TrackFragmentHeaderBox() +{ +} + +int SrsMp4TrackFragmentHeaderBox::nb_header() +{ + int size = SrsMp4FullBox::nb_header() + 4; + + if ((flags&SrsMp4TfhdFlagsBaseDataOffset) == SrsMp4TfhdFlagsBaseDataOffset) { + size += 8; + } + if ((flags&SrsMp4TfhdFlagsSampleDescriptionIndex) == SrsMp4TfhdFlagsSampleDescriptionIndex) { + size += 4; + } + if ((flags&SrsMp4TfhdFlagsDefaultSampleDuration) == SrsMp4TfhdFlagsDefaultSampleDuration) { + size += 4; + } + if ((flags&SrsMp4TfhdFlagsDefautlSampleSize) == SrsMp4TfhdFlagsDefautlSampleSize) { + size += 4; + } + if ((flags&SrsMp4TfhdFlagsDefaultSampleFlags) == SrsMp4TfhdFlagsDefaultSampleFlags) { + size += 4; + } + + return size; +} + +int SrsMp4TrackFragmentHeaderBox::encode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + buf->write_4bytes(track_id); + + if ((flags&SrsMp4TfhdFlagsBaseDataOffset) == SrsMp4TfhdFlagsBaseDataOffset) { + buf->write_8bytes(base_data_offset); + } + if ((flags&SrsMp4TfhdFlagsSampleDescriptionIndex) == SrsMp4TfhdFlagsSampleDescriptionIndex) { + buf->write_4bytes(sample_description_index); + } + if ((flags&SrsMp4TfhdFlagsDefaultSampleDuration) == SrsMp4TfhdFlagsDefaultSampleDuration) { + buf->write_4bytes(default_sample_duration); + } + if ((flags&SrsMp4TfhdFlagsDefautlSampleSize) == SrsMp4TfhdFlagsDefautlSampleSize) { + buf->write_4bytes(default_sample_size); + } + if ((flags&SrsMp4TfhdFlagsDefaultSampleFlags) == SrsMp4TfhdFlagsDefaultSampleFlags) { + buf->write_4bytes(default_sample_flags); + } + + return ret; +} + +int SrsMp4TrackFragmentHeaderBox::decode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + track_id = buf->read_4bytes(); + + if ((flags&SrsMp4TfhdFlagsBaseDataOffset) == SrsMp4TfhdFlagsBaseDataOffset) { + base_data_offset = buf->read_8bytes(); + } + if ((flags&SrsMp4TfhdFlagsSampleDescriptionIndex) == SrsMp4TfhdFlagsSampleDescriptionIndex) { + sample_description_index = buf->read_4bytes(); + } + if ((flags&SrsMp4TfhdFlagsDefaultSampleDuration) == SrsMp4TfhdFlagsDefaultSampleDuration) { + default_sample_duration = buf->read_4bytes(); + } + if ((flags&SrsMp4TfhdFlagsDefautlSampleSize) == SrsMp4TfhdFlagsDefautlSampleSize) { + default_sample_size = buf->read_4bytes(); + } + if ((flags&SrsMp4TfhdFlagsDefaultSampleFlags) == SrsMp4TfhdFlagsDefaultSampleFlags) { + default_sample_flags = buf->read_4bytes(); + } + + return ret; +} + +stringstream& SrsMp4TrackFragmentHeaderBox::dumps_detail(stringstream& ss, SrsMp4DumpContext dc) +{ + SrsMp4FullBox::dumps_detail(ss, dc); + + ss << ", track=" << track_id; + + if ((flags&SrsMp4TfhdFlagsBaseDataOffset) == SrsMp4TfhdFlagsBaseDataOffset) { + ss << ", bdo=" << base_data_offset; + } + if ((flags&SrsMp4TfhdFlagsSampleDescriptionIndex) == SrsMp4TfhdFlagsSampleDescriptionIndex) { + ss << ", sdi=" << sample_description_index; + } + if ((flags&SrsMp4TfhdFlagsDefaultSampleDuration) == SrsMp4TfhdFlagsDefaultSampleDuration) { + ss << ", dsu=" << default_sample_duration; + } + if ((flags&SrsMp4TfhdFlagsDefautlSampleSize) == SrsMp4TfhdFlagsDefautlSampleSize) { + ss << ", dss=" << default_sample_size; + } + if ((flags&SrsMp4TfhdFlagsDefaultSampleFlags) == SrsMp4TfhdFlagsDefaultSampleFlags) { + ss << ", dsf=" << default_sample_flags; + } + + return ss; +} + +SrsMp4TrackFragmentDecodeTimeBox::SrsMp4TrackFragmentDecodeTimeBox() +{ + type = SrsMp4BoxTypeTFDT; + base_media_decode_time = 0; +} + +SrsMp4TrackFragmentDecodeTimeBox::~SrsMp4TrackFragmentDecodeTimeBox() +{ +} + +int SrsMp4TrackFragmentDecodeTimeBox::nb_header() +{ + return SrsMp4FullBox::nb_header() + (version? 8:4); +} + +int SrsMp4TrackFragmentDecodeTimeBox::encode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + if (version) { + buf->write_8bytes(base_media_decode_time); + } else { + buf->write_4bytes((uint32_t)base_media_decode_time); + } + + return ret; +} + +int SrsMp4TrackFragmentDecodeTimeBox::decode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + if (version) { + base_media_decode_time = buf->read_8bytes(); + } else { + base_media_decode_time = buf->read_4bytes(); + } + + return ret; +} + +stringstream& SrsMp4TrackFragmentDecodeTimeBox::dumps_detail(stringstream& ss, SrsMp4DumpContext dc) +{ + SrsMp4FullBox::dumps_detail(ss, dc); + + ss << ", bmdt=" << base_media_decode_time; + + return ss; +} + +SrsMp4TrunEntry::SrsMp4TrunEntry(uint8_t v, uint32_t f) +{ + flags = f; + version = v; + sample_duration = sample_size = sample_flags = 0; + sample_composition_time_offset = 0; +} + +SrsMp4TrunEntry::~SrsMp4TrunEntry() +{ +} + +int SrsMp4TrunEntry::nb_header() +{ + int size = 0; + + if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { + size += 4; + } + if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { + size += 4; + } + if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { + size += 4; + } + if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { + size += 4; + } + + return size; +} + +int SrsMp4TrunEntry::encode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { + buf->write_4bytes(sample_duration); + } + if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { + buf->write_4bytes(sample_size); + } + if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { + buf->write_4bytes(sample_flags); + } + if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { + if (!version) { + uint32_t v = (uint32_t)sample_composition_time_offset; + buf->write_4bytes(v); + } else { + int32_t v = (int32_t)sample_composition_time_offset; + buf->write_4bytes(v); + } + } + + return ret; +} + +int SrsMp4TrunEntry::decode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { + sample_duration = buf->read_4bytes(); + } + if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { + sample_size = buf->read_4bytes(); + } + if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { + sample_flags = buf->read_4bytes(); + } + if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { + if (!version) { + uint32_t v = buf->read_4bytes(); + sample_composition_time_offset = v; + } else { + int32_t v = buf->read_4bytes(); + sample_composition_time_offset = v; + } + } + + return ret; +} + +stringstream& SrsMp4TrunEntry::dumps_detail(stringstream& ss, SrsMp4DumpContext dc) +{ + if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { + ss << "duration=" << sample_duration; + } + if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { + ss << ", size=" << sample_size; + } + if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { + ss << ", flags=" << sample_flags; + } + if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { + ss << ", cts-offset=" << sample_composition_time_offset; + } + return ss; +} + +SrsMp4TrackFragmentRunBox::SrsMp4TrackFragmentRunBox() +{ + type = SrsMp4BoxTypeTRUN; + sample_count = first_sample_flags = 0; + data_offset = 0; +} + +SrsMp4TrackFragmentRunBox::~SrsMp4TrackFragmentRunBox() +{ + vector::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + SrsMp4TrunEntry* entry = *it; + srs_freep(entry); + } +} + +int SrsMp4TrackFragmentRunBox::nb_header() +{ + int size = SrsMp4FullBox::nb_header() + 4; + + if ((flags&SrsMp4TrunFlagsDataOffset) == SrsMp4TrunFlagsDataOffset) { + size += 4; + } + if ((flags&SrsMp4TrunFlagsFirstSample) == SrsMp4TrunFlagsFirstSample) { + size += 4; + } + + vector::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + SrsMp4TrunEntry* entry = *it; + size += entry->nb_header(); + } + + return size; +} + +int SrsMp4TrackFragmentRunBox::encode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + buf->write_4bytes(sample_count); + + if ((flags&SrsMp4TrunFlagsDataOffset) == SrsMp4TrunFlagsDataOffset) { + buf->write_4bytes(data_offset); + } + if ((flags&SrsMp4TrunFlagsFirstSample) == SrsMp4TrunFlagsFirstSample) { + buf->write_4bytes(first_sample_flags); + } + + vector::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + SrsMp4TrunEntry* entry = *it; + if ((ret = entry->encode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + } + + return ret; +} + +int SrsMp4TrackFragmentRunBox::decode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + sample_count = buf->read_4bytes(); + + if ((flags&SrsMp4TrunFlagsDataOffset) == SrsMp4TrunFlagsDataOffset) { + data_offset = buf->read_4bytes(); + } + if ((flags&SrsMp4TrunFlagsFirstSample) == SrsMp4TrunFlagsFirstSample) { + first_sample_flags = buf->read_4bytes(); + } + + for (int i = 0; i < sample_count; i++) { + SrsMp4TrunEntry* entry = new SrsMp4TrunEntry(version, flags); + entries.push_back(entry); + + if ((ret = entry->decode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + } + + return ret; +} + +stringstream& SrsMp4TrackFragmentRunBox::dumps_detail(stringstream& ss, SrsMp4DumpContext dc) +{ + SrsMp4FullBox::dumps_detail(ss, dc); + + ss << ", samples=" << sample_count; + + if ((flags&SrsMp4TrunFlagsDataOffset) == SrsMp4TrunFlagsDataOffset) { + ss << ", do" << data_offset; + } + if ((flags&SrsMp4TrunFlagsFirstSample) == SrsMp4TrunFlagsFirstSample) { + ss << ", fsf=" << first_sample_flags; + } + + if (sample_count > 0) { + ss << endl; + srs_padding(ss, dc.indent()); + srs_dumps_array(entries, ss, dc.indent(), srs_pfn_pdetail, srs_delimiter_newline); + } + + return ss; +} + SrsMp4MediaDataBox::SrsMp4MediaDataBox() { type = SrsMp4BoxTypeMDAT; diff --git a/trunk/src/kernel/srs_kernel_mp4.hpp b/trunk/src/kernel/srs_kernel_mp4.hpp index d70374cdf..69efeb971 100644 --- a/trunk/src/kernel/srs_kernel_mp4.hpp +++ b/trunk/src/kernel/srs_kernel_mp4.hpp @@ -111,8 +111,14 @@ enum SrsMp4BoxType SrsMp4BoxTypeUDTA = 0x75647461, // 'udta' SrsMp4BoxTypeMVEX = 0x6d766578, // 'mvex' SrsMp4BoxTypeTREX = 0x74726578, // 'trex' - SrsMp4BoxTypePASP = 0x70617370, // 'pasp' + SrsMp4BoxTypeSTYP = 0x73747970, // 'styp' + SrsMp4BoxTypeMOOF = 0x6d6f6f66, // 'moof' + SrsMp4BoxTypeMFHD = 0x6d666864, // 'mfhd' + SrsMp4BoxTypeTRAF = 0x74726166, // 'traf' + SrsMp4BoxTypeTFHD = 0x74666864, // 'tfhd' + SrsMp4BoxTypeTFDT = 0x74666474, // 'tfdt' + SrsMp4BoxTypeTRUN = 0x7472756e, // 'trun' }; /** @@ -290,6 +296,245 @@ public: virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); }; +/** + * 8.16.2 Segment Type Box (styp) + * ISO_IEC_14496-12-base-format-2012.pdf, page 105 + * If segments are stored in separate files (e.g. on a standard HTTP server) it is recommended that these + * 'segment files' contain a segment-type box, which must be first if present, to enable identification of those files, + * and declaration of the specifications with which they are compliant. + */ +class SrsMp4SegmentTypeBox : public SrsMp4FileTypeBox +{ +public: + SrsMp4SegmentTypeBox(); + virtual ~SrsMp4SegmentTypeBox(); +}; + +/** + * 8.8.4 Movie Fragment Box (moof) + * ISO_IEC_14496-12-base-format-2012.pdf, page 66 + * The movie fragments extend the presentation in time. They provide the information that would previously have + * been in the Movie Box. The actual samples are in Media Data Boxes, as usual, if they are in the same file. + * The data reference index is in the sample description, so it is possible to build incremental presentations + * where the media data is in files other than the file containing the Movie Box. + */ +class SrsMp4MovieFragmentBox : public SrsMp4Box +{ +public: + SrsMp4MovieFragmentBox(); + virtual ~SrsMp4MovieFragmentBox(); +}; + +/** + * 8.8.5 Movie Fragment Header Box (mfhd) + * ISO_IEC_14496-12-base-format-2012.pdf, page 67 + * The movie fragment header contains a sequence number, as a safety check. The sequence number usually + * starts at 1 and must increase for each movie fragment in the file, in the order in which they occur. This allows + * readers to verify integrity of the sequence; it is an error to construct a file where the fragments are out of + * sequence. + */ +class SrsMp4MovieFragmentHeaderBox : public SrsMp4FullBox +{ +public: + // the ordinal number of this fragment, in increasing order + uint32_t sequence_number; +public: + SrsMp4MovieFragmentHeaderBox(); + virtual ~SrsMp4MovieFragmentHeaderBox(); +protected: + virtual int nb_header(); + virtual int encode_header(SrsBuffer* buf); + virtual int decode_header(SrsBuffer* buf); +public: + virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); +}; + +/** + * 8.8.6 Track Fragment Box (traf) + * ISO_IEC_14496-12-base-format-2012.pdf, page 67 + * Within the movie fragment there is a set of track fragments, zero or more per track. The track fragments in + * turn contain zero or more track runs, each of which document a contiguous run of samples for that track. + * Within these structures, many fields are optional and can be defaulted. + */ +class SrsMp4TrackFragmentBox : public SrsMp4Box +{ +public: + SrsMp4TrackFragmentBox(); + virtual ~SrsMp4TrackFragmentBox(); +}; + +/** + * The tf_flags of tfhd. + * ISO_IEC_14496-12-base-format-2012.pdf, page 68 + */ +enum SrsMp4TfhdFlags +{ + /** + * indicates the presence of the base-data-offset field. This provides + * an explicit anchor for the data offsets in each track run (see below). If not provided, the base-data- + * offset for the first track in the movie fragment is the position of the first byte of the enclosing Movie + * Fragment Box, and for second and subsequent track fragments, the default is the end of the data + * defined by the preceding fragment. Fragments 'inheriting' their offset in this way must all use + * the same data-reference (i.e., the data for these tracks must be in the same file). + */ + SrsMp4TfhdFlagsBaseDataOffset = 0x000001, + /** + * indicates the presence of this field, which over-rides, in this + * fragment, the default set up in the Track Extends Box. + */ + SrsMp4TfhdFlagsSampleDescriptionIndex = 0x000002, + SrsMp4TfhdFlagsDefaultSampleDuration = 0x000008, + SrsMp4TfhdFlagsDefautlSampleSize = 0x000010, + SrsMp4TfhdFlagsDefaultSampleFlags = 0x000020, + /** + * this indicates that the duration provided in either default-sample-duration, + * or by the default-duration in the Track Extends Box, is empty, i.e. that there are no samples for this + * time interval. It is an error to make a presentation that has both edit lists in the Movie Box, and empty- + * duration fragments. + */ + SrsMp4TfhdFlagsDurationIsEmpty = 0x010000, + /** + * if base-data-offset-present is zero, this indicates that the base-data- + * offset for this track fragment is the position of the first byte of the enclosing Movie Fragment Box. + * Support for the default-base-is-moof flag is required under the ‘iso5’ brand, and it shall not be used in + * brands or compatible brands earlier than iso5. + */ + SrsMp4TfhdFlagsDefaultBaseIsMoof = 0x020000, +}; + +/** + * 8.8.7 Track Fragment Header Box (tfhd) + * ISO_IEC_14496-12-base-format-2012.pdf, page 68 + * Each movie fragment can add zero or more fragments to each track; and a track fragment can add zero or + * more contiguous runs of samples. The track fragment header sets up information and defaults used for those + * runs of samples. + */ +class SrsMp4TrackFragmentHeaderBox : public SrsMp4FullBox +{ +public: + uint32_t track_id; + // all the following are optional fields +public: + // the base offset to use when calculating data offsets + uint64_t base_data_offset; + uint32_t sample_description_index; + uint32_t default_sample_duration; + uint32_t default_sample_size; + uint32_t default_sample_flags; +public: + SrsMp4TrackFragmentHeaderBox(); + virtual ~SrsMp4TrackFragmentHeaderBox(); +protected: + virtual int nb_header(); + virtual int encode_header(SrsBuffer* buf); + virtual int decode_header(SrsBuffer* buf); +public: + virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); +}; + +/** + * 8.8.12 Track fragment decode time (tfdt) + * ISO_IEC_14496-12-base-format-2012.pdf, page 72 + * The Track Fragment Base Media Decode Time Box provides the absolute decode time, measured on + * the media timeline, of the first sample in decode order in the track fragment. This can be useful, for example, + * when performing random access in a file; it is not necessary to sum the sample durations of all preceding + * samples in previous fragments to find this value (where the sample durations are the deltas in the Decoding + * Time to Sample Box and the sample_durations in the preceding track runs). + */ +class SrsMp4TrackFragmentDecodeTimeBox : public SrsMp4FullBox +{ +public: + uint64_t base_media_decode_time; +public: + SrsMp4TrackFragmentDecodeTimeBox(); + virtual ~SrsMp4TrackFragmentDecodeTimeBox(); +protected: + virtual int nb_header(); + virtual int encode_header(SrsBuffer* buf); + virtual int decode_header(SrsBuffer* buf); +public: + virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); +}; + +/** + * The tr_flags for trun + * ISO_IEC_14496-12-base-format-2012.pdf, page 69 + */ +enum SrsMp4TrunFlags +{ + // data-offset-present. + SrsMp4TrunFlagsDataOffset = 0x000001, + // this over-rides the default flags for the first sample only. This + // makes it possible to record a group of frames where the first is a key and the rest are difference + // frames, without supplying explicit flags for every sample. If this flag and field are used, sample-flags + // shall not be present. + SrsMp4TrunFlagsFirstSample = 0x000004, + // indicates that each sample has its own duration, otherwise the default is used. + SrsMp4TrunFlagsSampleDuration = 0x000100, + // each sample has its own size, otherwise the default is used. + SrsMp4TrunFlagsSampleSize = 0x000200, + // each sample has its own flags, otherwise the default is used. + SrsMp4TrunFlagsSampleFlag = 0x000400, + // each sample has a composition time offset (e.g. as used for I/P/B video in MPEG). + SrsMp4TrunFlagsSampleCtsOffset = 0x000800, +}; + +/** + * Entry for trun. + * ISO_IEC_14496-12-base-format-2012.pdf, page 69 + */ +struct SrsMp4TrunEntry +{ + uint8_t version; + uint32_t flags; + + uint32_t sample_duration; + uint32_t sample_size; + uint32_t sample_flags; + // if version == 0, unsigned int(32); otherwise, signed int(32). + int64_t sample_composition_time_offset; + + SrsMp4TrunEntry(uint8_t v, uint32_t f); + virtual ~SrsMp4TrunEntry(); + + virtual int nb_header(); + virtual int encode_header(SrsBuffer* buf); + virtual int decode_header(SrsBuffer* buf); + virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); +}; + +/** + * 8.8.8 Track Fragment Run Box (trun) + * ISO_IEC_14496-12-base-format-2012.pdf, page 69 + * Within the Track Fragment Box, there are zero or more Track Run Boxes. If the duration-is-empty flag is set in + * the tf_flags, there are no track runs. A track run documents a contiguous set of samples for a track. + */ +class SrsMp4TrackFragmentRunBox : public SrsMp4FullBox +{ +public: + // the number of samples being added in this run; also the number of rows in the following + // table (the rows can be empty) + uint32_t sample_count; +// the following are optional fields +public: + // added to the implicit or explicit data_offset established in the track fragment header. + int32_t data_offset; + // provides a set of flags for the first sample only of this run. + uint32_t first_sample_flags; +// all fields in the following array are optional +public: + std::vector entries; +public: + SrsMp4TrackFragmentRunBox(); + virtual ~SrsMp4TrackFragmentRunBox(); +protected: + virtual int nb_header(); + virtual int encode_header(SrsBuffer* buf); + virtual int decode_header(SrsBuffer* buf); +public: + virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); +}; + /** * 8.1.1 Media Data Box (mdat) * ISO_IEC_14496-12-base-format-2012.pdf, page 29