diff --git a/trunk/src/app/srs_app_dash.cpp b/trunk/src/app/srs_app_dash.cpp index 311a2a780..a87720529 100644 --- a/trunk/src/app/srs_app_dash.cpp +++ b/trunk/src/app/srs_app_dash.cpp @@ -31,6 +31,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include #include using namespace std; @@ -119,15 +121,17 @@ int SrsMpdWriter::write(SrsFormat* format) ss << " " << endl << "" << endl; - SrsFileWriter fw; + SrsFileWriter* fw = new SrsFileWriter(); + SrsAutoFree(SrsFileWriter, fw); + string full_path_tmp = full_path + ".tmp"; - if ((ret = fw.open(full_path_tmp)) != ERROR_SUCCESS) { + if ((ret = fw->open(full_path_tmp)) != ERROR_SUCCESS) { srs_error("DASH: Open MPD file=%s failed, ret=%d", full_path_tmp.c_str(), ret); return ret; } string content = ss.str(); - if ((ret = fw.write((void*)content.data(), content.length(), NULL)) != ERROR_SUCCESS) { + if ((ret = fw->write((void*)content.data(), content.length(), NULL)) != ERROR_SUCCESS) { srs_error("DASH: Write MPD file=%s failed, ret=%d", full_path.c_str(), ret); return ret; } @@ -138,13 +142,16 @@ int SrsMpdWriter::write(SrsFormat* format) return ret; } - srs_trace("DASH: Refresh MPD successed, size=%dB, file=%s", content.length(), full_path.c_str()); + srs_trace("DASH: Refresh MPD success, size=%dB, file=%s", content.length(), full_path.c_str()); return ret; } SrsDashController::SrsDashController() { + req = NULL; + video_tack_id = 2; + audio_track_id = 1; mpd = new SrsMpdWriter(); } @@ -157,6 +164,9 @@ int SrsDashController::initialize(SrsRequest* r) { int ret = ERROR_SUCCESS; + req = r; + home = _srs_config->get_dash_path(r->vhost); + if ((ret = mpd->initialize(r)) != ERROR_SUCCESS) { return ret; } @@ -168,6 +178,10 @@ int SrsDashController::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* fo { int ret = ERROR_SUCCESS; + if (format->is_aac_sequence_header()) { + return refresh_init_mp4(shared_audio, format); + } + if ((ret = refresh_mpd(format)) != ERROR_SUCCESS) { srs_error("DASH: Refresh the MPD failed. ret=%d", ret); return ret; @@ -180,6 +194,10 @@ int SrsDashController::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* fo { int ret = ERROR_SUCCESS; + if (format->is_avc_sequence_header()) { + return refresh_init_mp4(shared_video, format); + } + if ((ret = refresh_mpd(format)) != ERROR_SUCCESS) { srs_error("DASH: Refresh the MPD failed. ret=%d", ret); return ret; @@ -204,6 +222,278 @@ int SrsDashController::refresh_mpd(SrsFormat* format) return ret; } +int SrsDashController::refresh_init_mp4(SrsSharedPtrMessage* msg, SrsFormat* format) +{ + int ret = ERROR_SUCCESS; + + if (msg->size <= 0 || (msg->is_video() && !format->vcodec->avc_extra_size) + || (msg->is_audio() && !format->acodec->aac_extra_size)) { + srs_warn("DASH: Ignore empty sequence header."); + return ret; + } + + string full_home = home + "/" + req->app + "/" + req->stream; + if ((ret = srs_create_dir_recursively(full_home)) != ERROR_SUCCESS) { + srs_error("DASH: Create media home failed, home=%s, ret=%d", full_home.c_str(), ret); + return ret; + } + + std::string path = full_home; + if (msg->is_video()) { + path += "/video-init.mp4"; + } else { + path += "/audio-init.mp4"; + } + + SrsFileWriter* fw = new SrsFileWriter(); + SrsAutoFree(SrsFileWriter, fw); + + string path_tmp = path + ".tmp"; + if ((ret = fw->open(path_tmp)) != ERROR_SUCCESS) { + srs_error("DASH: Open media failed, path=%s, ret=%d", path_tmp.c_str(), ret); + return ret; + } + + // Write ftyp box. + SrsMp4FileTypeBox* ftyp = new SrsMp4FileTypeBox(); + SrsAutoFree(SrsMp4FileTypeBox, ftyp); + if (true) { + ftyp->major_brand = SrsMp4BoxBrandISO5; + ftyp->minor_version = 0; + ftyp->set_compatible_brands(SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO5, SrsMp4BoxBrandDASH, SrsMp4BoxBrandMP42); + } + + // Write moov. + SrsMp4MovieBox* moov = new SrsMp4MovieBox(); + SrsAutoFree(SrsMp4MovieBox, moov); + if (true) { + SrsMp4MovieHeaderBox* mvhd = new SrsMp4MovieHeaderBox(); + moov->set_mvhd(mvhd); + + mvhd->timescale = 1000; // Use tbn ms. + mvhd->duration_in_tbn = 0; + mvhd->next_track_ID = (msg->is_video()? video_tack_id : audio_track_id); + + if (msg->is_video()) { + SrsMp4TrackBox* trak = new SrsMp4TrackBox(); + moov->add_trak(trak); + + SrsMp4TrackHeaderBox* tkhd = new SrsMp4TrackHeaderBox(); + trak->set_tkhd(tkhd); + + tkhd->track_ID = mvhd->next_track_ID++; + tkhd->duration = 0; + tkhd->width = (format->vcodec->width << 16); + tkhd->height = (format->vcodec->height << 16); + + SrsMp4MediaBox* mdia = new SrsMp4MediaBox(); + trak->set_mdia(mdia); + + SrsMp4MediaHeaderBox* mdhd = new SrsMp4MediaHeaderBox(); + mdia->set_mdhd(mdhd); + + mdhd->timescale = 1000; + mdhd->duration = 0; + mdhd->set_language0('u'); + mdhd->set_language1('n'); + mdhd->set_language2('d'); + + SrsMp4HandlerReferenceBox* hdlr = new SrsMp4HandlerReferenceBox(); + mdia->set_hdlr(hdlr); + + hdlr->handler_type = SrsMp4HandlerTypeVIDE; + hdlr->name = "VideoHandler"; + + SrsMp4MediaInformationBox* minf = new SrsMp4MediaInformationBox(); + mdia->set_minf(minf); + + SrsMp4VideoMeidaHeaderBox* vmhd = new SrsMp4VideoMeidaHeaderBox(); + minf->set_vmhd(vmhd); + + SrsMp4DataInformationBox* dinf = new SrsMp4DataInformationBox(); + minf->set_dinf(dinf); + + SrsMp4DataReferenceBox* dref = new SrsMp4DataReferenceBox(); + dinf->set_dref(dref); + + SrsMp4DataEntryBox* url = new SrsMp4DataEntryUrlBox(); + dref->append(url); + + SrsMp4SampleTableBox* stbl = new SrsMp4SampleTableBox(); + minf->set_stbl(stbl); + + SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox(); + stbl->set_stsd(stsd); + + SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry(); + stsd->append(avc1); + + avc1->width = format->vcodec->width; + avc1->height = format->vcodec->height; + + SrsMp4AvccBox* avcC = new SrsMp4AvccBox(); + avc1->set_avcC(avcC); + + avcC->nb_config = format->vcodec->avc_extra_size; + avcC->avc_config = new uint8_t[format->vcodec->avc_extra_size]; + memcpy(avcC->avc_config, format->vcodec->avc_extra_data, format->vcodec->avc_extra_size); + + SrsMp4DecodingTime2SampleBox* stts = new SrsMp4DecodingTime2SampleBox(); + stbl->set_stts(stts); + + SrsMp4Sample2ChunkBox* stsc = new SrsMp4Sample2ChunkBox(); + stbl->set_stsc(stsc); + + SrsMp4SampleSizeBox* stsz = new SrsMp4SampleSizeBox(); + stbl->set_stsz(stsz); + + SrsMp4ChunkOffsetBox* stco = new SrsMp4ChunkOffsetBox(); + stbl->set_stco(stco); + + SrsMp4MovieExtendsBox* mvex = new SrsMp4MovieExtendsBox(); + moov->set_mvex(mvex); + + SrsMp4TrackExtendsBox* trex = new SrsMp4TrackExtendsBox(); + mvex->set_trex(trex); + + trex->track_ID = video_tack_id; + trex->default_sample_description_index = 1; + } else { + SrsMp4TrackBox* trak = new SrsMp4TrackBox(); + moov->add_trak(trak); + + SrsMp4TrackHeaderBox* tkhd = new SrsMp4TrackHeaderBox(); + tkhd->volume = 0x0100; + trak->set_tkhd(tkhd); + + tkhd->track_ID = mvhd->next_track_ID++; + tkhd->duration = 0; + + SrsMp4MediaBox* mdia = new SrsMp4MediaBox(); + trak->set_mdia(mdia); + + SrsMp4MediaHeaderBox* mdhd = new SrsMp4MediaHeaderBox(); + mdia->set_mdhd(mdhd); + + mdhd->timescale = 1000; + mdhd->duration = 0; + mdhd->set_language0('u'); + mdhd->set_language1('n'); + mdhd->set_language2('d'); + + SrsMp4HandlerReferenceBox* hdlr = new SrsMp4HandlerReferenceBox(); + mdia->set_hdlr(hdlr); + + hdlr->handler_type = SrsMp4HandlerTypeSOUN; + hdlr->name = "SoundHandler"; + + SrsMp4MediaInformationBox* minf = new SrsMp4MediaInformationBox(); + mdia->set_minf(minf); + + SrsMp4SoundMeidaHeaderBox* smhd = new SrsMp4SoundMeidaHeaderBox(); + minf->set_smhd(smhd); + + SrsMp4DataInformationBox* dinf = new SrsMp4DataInformationBox(); + minf->set_dinf(dinf); + + SrsMp4DataReferenceBox* dref = new SrsMp4DataReferenceBox(); + dinf->set_dref(dref); + + SrsMp4DataEntryBox* url = new SrsMp4DataEntryUrlBox(); + dref->append(url); + + SrsMp4SampleTableBox* stbl = new SrsMp4SampleTableBox(); + minf->set_stbl(stbl); + + SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox(); + stbl->set_stsd(stsd); + + SrsMp4AudioSampleEntry* mp4a = new SrsMp4AudioSampleEntry(); + mp4a->samplerate = uint32_t(srs_flv_srates[format->acodec->sound_rate]) << 16; + if (format->acodec->sound_size == SrsAudioSampleBits16bit) { + mp4a->samplesize = 16; + } else { + mp4a->samplesize = 8; + } + if (format->acodec->sound_type == SrsAudioChannelsStereo) { + mp4a->channelcount = 2; + } else { + mp4a->channelcount = 1; + } + stsd->append(mp4a); + + SrsMp4EsdsBox* esds = new SrsMp4EsdsBox(); + mp4a->set_esds(esds); + + SrsMp4ES_Descriptor* es = esds->es; + es->ES_ID = 0x02; + + SrsMp4DecoderConfigDescriptor& desc = es->decConfigDescr; + desc.objectTypeIndication = SrsMp4ObjectTypeAac; + desc.streamType = SrsMp4StreamTypeAudioStream; + srs_freep(desc.decSpecificInfo); + + SrsMp4DecoderSpecificInfo* asc = new SrsMp4DecoderSpecificInfo(); + desc.decSpecificInfo = asc; + asc->nb_asc = format->acodec->aac_extra_size; + asc->asc = new uint8_t[format->acodec->aac_extra_size]; + memcpy(asc->asc, format->acodec->aac_extra_data, format->acodec->aac_extra_size); + + SrsMp4DecodingTime2SampleBox* stts = new SrsMp4DecodingTime2SampleBox(); + stbl->set_stts(stts); + + SrsMp4Sample2ChunkBox* stsc = new SrsMp4Sample2ChunkBox(); + stbl->set_stsc(stsc); + + SrsMp4SampleSizeBox* stsz = new SrsMp4SampleSizeBox(); + stbl->set_stsz(stsz); + + SrsMp4ChunkOffsetBox* stco = new SrsMp4ChunkOffsetBox(); + stbl->set_stco(stco); + + SrsMp4MovieExtendsBox* mvex = new SrsMp4MovieExtendsBox(); + moov->set_mvex(mvex); + + SrsMp4TrackExtendsBox* trex = new SrsMp4TrackExtendsBox(); + mvex->set_trex(trex); + + trex->track_ID = audio_track_id; + trex->default_sample_description_index = 1; + } + } + + int nb_data = ftyp->nb_bytes() + moov->nb_bytes(); + uint8_t* data = new uint8_t[nb_data]; + SrsAutoFreeA(uint8_t, data); + + SrsBuffer* buffer = new SrsBuffer(); + SrsAutoFree(SrsBuffer, buffer); + if ((ret = buffer->initialize((char*)data, nb_data)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = ftyp->encode(buffer)) != ERROR_SUCCESS) { + return ret; + } + if ((ret = moov->encode(buffer)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = fw->write(data, nb_data, NULL)) != ERROR_SUCCESS) { + return ret; + } + + if (::rename(path_tmp.c_str(), path.c_str()) < 0) { + ret = ERROR_DASH_WRITE_FAILED; + srs_error("DASH: Rename %s to %s failed, ret=%d", path_tmp.c_str(), path.c_str(), ret); + return ret; + } + + srs_trace("DASH: Refresh media success, file=%s", path.c_str()); + + return ret; +} + SrsDash::SrsDash() { hub = NULL; diff --git a/trunk/src/app/srs_app_dash.hpp b/trunk/src/app/srs_app_dash.hpp index 1e87a8baf..d76879722 100644 --- a/trunk/src/app/srs_app_dash.hpp +++ b/trunk/src/app/srs_app_dash.hpp @@ -80,7 +80,12 @@ public: class SrsDashController { private: + SrsRequest* req; SrsMpdWriter* mpd; +private: + std::string home; + int video_tack_id; + int audio_track_id; public: SrsDashController(); virtual ~SrsDashController(); @@ -90,6 +95,7 @@ public: virtual int on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format); private: virtual int refresh_mpd(SrsFormat* format); + virtual int refresh_init_mp4(SrsSharedPtrMessage* msg, SrsFormat* format); }; /** diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index f5aba41ff..d7bd6a530 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -560,7 +560,8 @@ int SrsFormat::on_audio(int64_t timestamp, char* data, int size) } // @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76 - SrsAudioCodecId codec = (SrsAudioCodecId)((buffer->read_1bytes() >> 4) & 0x0f); + uint8_t v = buffer->read_1bytes(); + SrsAudioCodecId codec = (SrsAudioCodecId)((v >> 4) & 0x0f); if (codec != SrsAudioCodecIdMP3 && codec != SrsAudioCodecIdAAC) { return ret; @@ -1361,6 +1362,21 @@ int SrsFormat::audio_mp3_demux(SrsBuffer* stream, int64_t timestamp) audio->cts = 0; audio->dts = timestamp; + // @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76 + int8_t sound_format = stream->read_1bytes(); + + int8_t sound_type = sound_format & 0x01; + int8_t sound_size = (sound_format >> 1) & 0x01; + int8_t sound_rate = (sound_format >> 2) & 0x03; + sound_format = (sound_format >> 4) & 0x0f; + + SrsAudioCodecId codec_id = (SrsAudioCodecId)sound_format; + acodec->id = codec_id; + + acodec->sound_type = (SrsAudioChannels)sound_type; + acodec->sound_rate = (SrsAudioSampleRate)sound_rate; + acodec->sound_size = (SrsAudioSampleBits)sound_size; + // we always decode aac then mp3. srs_assert(acodec->id == SrsAudioCodecIdMP3); diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index 22c7564d4..c1156bb97 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -515,14 +515,17 @@ public: */ class SrsAudioCodecConfig : public SrsCodecConfig { +// In FLV specification. public: // audio specified SrsAudioCodecId id; // audio aac specified. SrsAudioSampleRate sound_rate; SrsAudioSampleBits sound_size; + // TODO: FIXME: Rename to sound_channels. SrsAudioChannels sound_type; int audio_data_rate; // in bps +// In AAC specification. public: /** * audio specified @@ -539,6 +542,7 @@ public: * channelConfiguration */ uint8_t aac_channels; +// Sequence header payload. public: /** * the aac extra data, the AAC sequence header, diff --git a/trunk/src/kernel/srs_kernel_mp4.cpp b/trunk/src/kernel/srs_kernel_mp4.cpp index 0b16bc479..99cda1cfe 100644 --- a/trunk/src/kernel/srs_kernel_mp4.cpp +++ b/trunk/src/kernel/srs_kernel_mp4.cpp @@ -226,6 +226,8 @@ int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox) case SrsMp4BoxTypeMP4A: box = new SrsMp4AudioSampleEntry(); break; case SrsMp4BoxTypeESDS: box = new SrsMp4EsdsBox(); break; case SrsMp4BoxTypeUDTA: box = new SrsMp4UserDataBox(); break; + case SrsMp4BoxTypeMVEX: box = new SrsMp4MovieExtendsBox(); break; + case SrsMp4BoxTypeTREX: box = new SrsMp4TrackExtendsBox(); break; default: ret = ERROR_MP4_BOX_ILLEGAL_TYPE; srs_error("MP4 illegal box type=%d. ret=%d", type, ret); @@ -690,7 +692,19 @@ SrsMp4MovieHeaderBox* SrsMp4MovieBox::mvhd() void SrsMp4MovieBox::set_mvhd(SrsMp4MovieHeaderBox* v) { remove(SrsMp4BoxTypeMVHD); - boxes.insert(boxes.begin(), v); + boxes.push_back(v); +} + +SrsMp4MovieExtendsBox* SrsMp4MovieBox::mvex() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeMVEX); + return dynamic_cast(box); +} + +void SrsMp4MovieBox::set_mvex(SrsMp4MovieExtendsBox* v) +{ + remove(SrsMp4BoxTypeMVEX); + boxes.push_back(v); } SrsMp4TrackBox* SrsMp4MovieBox::video() @@ -884,6 +898,77 @@ int SrsMp4MovieHeaderBox::decode_header(SrsBuffer* buf) return ret; } +SrsMp4MovieExtendsBox::SrsMp4MovieExtendsBox() +{ + type = SrsMp4BoxTypeMVEX; +} + +SrsMp4MovieExtendsBox::~SrsMp4MovieExtendsBox() +{ +} + +SrsMp4TrackExtendsBox* SrsMp4MovieExtendsBox::trex() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeTREX); + return dynamic_cast(box); +} + +void SrsMp4MovieExtendsBox::set_trex(SrsMp4TrackExtendsBox* v) +{ + remove(SrsMp4BoxTypeTREX); + boxes.push_back(v); +} + +SrsMp4TrackExtendsBox::SrsMp4TrackExtendsBox() +{ + type = SrsMp4BoxTypeTREX; + track_ID = default_sample_size = default_sample_flags = 0; + default_sample_size = default_sample_duration = default_sample_description_index = 0; +} + +SrsMp4TrackExtendsBox::~SrsMp4TrackExtendsBox() +{ +} + +int SrsMp4TrackExtendsBox::nb_header() +{ + return SrsMp4FullBox::nb_header() + 4*5; +} + +int SrsMp4TrackExtendsBox::encode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + buf->write_4bytes(track_ID); + buf->write_4bytes(default_sample_description_index); + buf->write_4bytes(default_sample_duration); + buf->write_4bytes(default_sample_size); + buf->write_4bytes(default_sample_flags); + + return ret; +} + +int SrsMp4TrackExtendsBox::decode_header(SrsBuffer* buf) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) { + return ret; + } + + track_ID = buf->read_4bytes(); + default_sample_description_index = buf->read_4bytes(); + default_sample_duration = buf->read_4bytes(); + default_sample_size = buf->read_4bytes(); + default_sample_flags = buf->read_4bytes(); + + return ret; +} + SrsMp4TrackBox::SrsMp4TrackBox() { type = SrsMp4BoxTypeTRAK; @@ -4010,7 +4095,8 @@ int SrsMp4Decoder::parse_ftyp(SrsMp4FileTypeBox* ftyp) // File Type Box (ftyp) bool legal_brand = false; static SrsMp4BoxBrand legal_brands[] = { - SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO2, SrsMp4BoxBrandAVC1, SrsMp4BoxBrandMP41 + SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO2, SrsMp4BoxBrandAVC1, SrsMp4BoxBrandMP41, + SrsMp4BoxBrandISO5 }; for (int i = 0; i < sizeof(legal_brands)/sizeof(SrsMp4BoxBrand); i++) { if (ftyp->major_brand == legal_brands[i]) { @@ -4375,7 +4461,7 @@ int SrsMp4Encoder::flush() mvhd->timescale = 1000; // Use tbn ms. mvhd->duration_in_tbn = srs_max(vduration, aduration); - mvhd->next_track_ID++; + mvhd->next_track_ID = 1; // Starts from 1, increase when use it. if (nb_videos) { SrsMp4TrackBox* trak = new SrsMp4TrackBox(); diff --git a/trunk/src/kernel/srs_kernel_mp4.hpp b/trunk/src/kernel/srs_kernel_mp4.hpp index 04a4b69f2..c43a501bc 100644 --- a/trunk/src/kernel/srs_kernel_mp4.hpp +++ b/trunk/src/kernel/srs_kernel_mp4.hpp @@ -64,6 +64,8 @@ class SrsMp4VideoMeidaHeaderBox; class SrsMp4DataInformationBox; class SrsMp4DataReferenceBox; class SrsMp4SoundMeidaHeaderBox; +class SrsMp4MovieExtendsBox; +class SrsMp4TrackExtendsBox; /** * 4.2 Object Structure @@ -109,6 +111,8 @@ enum SrsMp4BoxType SrsMp4BoxTypeMP4A = 0x6d703461, // 'mp4a' SrsMp4BoxTypeESDS = 0x65736473, // 'esds' SrsMp4BoxTypeUDTA = 0x75647461, // 'udta' + SrsMp4BoxTypeMVEX = 0x6d766578, // 'mvex' + SrsMp4BoxTypeTREX = 0x74726578, // 'trex' }; /** @@ -134,6 +138,9 @@ enum SrsMp4BoxBrand SrsMp4BoxBrandISO2 = 0x69736f32, // 'iso2' SrsMp4BoxBrandAVC1 = 0x61766331, // 'avc1' SrsMp4BoxBrandMP41 = 0x6d703431, // 'mp41' + SrsMp4BoxBrandISO5 = 0x69736f35, // 'iso5' + SrsMp4BoxBrandMP42 = 0x6d703432, // 'mp42' + SrsMp4BoxBrandDASH = 0x64617368, // 'dash' }; /** @@ -321,6 +328,9 @@ public: // Get the header of moov. virtual SrsMp4MovieHeaderBox* mvhd(); virtual void set_mvhd(SrsMp4MovieHeaderBox* v); + // Get the movie extends header. + virtual SrsMp4MovieExtendsBox* mvex(); + virtual void set_mvex(SrsMp4MovieExtendsBox* v); // Get the first video track. virtual SrsMp4TrackBox* video(); // Get the first audio track. @@ -395,6 +405,47 @@ enum SrsMp4TrackType SrsMp4TrackTypeVideo = 0x02, }; +/** + * 8.8.1 Movie Extends Box (mvex) + * ISO_IEC_14496-12-base-format-2012.pdf, page 64 + * This box warns readers that there might be Movie Fragment Boxes in this file. To know of all samples in the + * tracks, these Movie Fragment Boxes must be found and scanned in order, and their information logically + * added to that found in the Movie Box. + */ +class SrsMp4MovieExtendsBox : public SrsMp4Box +{ +public: + SrsMp4MovieExtendsBox(); + virtual ~SrsMp4MovieExtendsBox(); +public: + // Get the track extends box. + virtual SrsMp4TrackExtendsBox* trex(); + virtual void set_trex(SrsMp4TrackExtendsBox* v); +}; + +/** + * 8.8.3 Track Extends Box(trex) + * ISO_IEC_14496-12-base-format-2012.pdf, page 65 + */ +class SrsMp4TrackExtendsBox : public SrsMp4FullBox +{ +public: + // identifies the track; this shall be the track ID of a track in the Movie Box + uint32_t track_ID; + // these fields set up defaults used in the track fragments. + uint32_t default_sample_description_index; + uint32_t default_sample_duration; + uint32_t default_sample_size; + uint32_t default_sample_flags; +public: + SrsMp4TrackExtendsBox(); + virtual ~SrsMp4TrackExtendsBox(); +protected: + virtual int nb_header(); + virtual int encode_header(SrsBuffer* buf); + virtual int decode_header(SrsBuffer* buf); +}; + /** * 8.3.1 Track Box (trak) * ISO_IEC_14496-12-base-format-2012.pdf, page 32