From 0e9e1792fefbe65f6c5d6f0d9c1ef35f43a6e7f2 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 4 Jun 2017 15:10:35 +0800 Subject: [PATCH] For #299, write fMP4 for DASH. --- trunk/conf/full.conf | 4 +- trunk/src/app/srs_app_config.cpp | 2 +- trunk/src/app/srs_app_dash.cpp | 292 +++---------- trunk/src/app/srs_app_dash.hpp | 13 +- trunk/src/app/srs_app_dvr.cpp | 114 ++--- trunk/src/app/srs_app_dvr.hpp | 31 +- trunk/src/app/srs_app_source.cpp | 31 +- trunk/src/kernel/srs_kernel_codec.cpp | 18 +- trunk/src/kernel/srs_kernel_codec.hpp | 5 + trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/kernel/srs_kernel_mp4.cpp | 605 ++++++++++++++++++++++++-- trunk/src/kernel/srs_kernel_mp4.hpp | 99 ++++- 12 files changed, 834 insertions(+), 381 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 69273f8cfe..492253f515 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -951,8 +951,8 @@ vhost dash.srs.com { # Default: off enabled on; # The duration of segment in seconds. - # Default: 10 - dash_fragment 10; + # Default: 3 + dash_fragment 3; # The period to update the MPD in seconds. # Default: 30 dash_update_period 30; diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 0d7d773ee1..b2b767dc5c 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -6013,7 +6013,7 @@ bool SrsConfig::get_dash_enabled(string vhost) int SrsConfig::get_dash_fragment(string vhost) { - static int DEFAULT = 10 * 1000; + static int DEFAULT = 3 * 1000; SrsConfDirective* conf = get_dash(vhost); if (!conf) { diff --git a/trunk/src/app/srs_app_dash.cpp b/trunk/src/app/srs_app_dash.cpp index 927b533b2b..a4b533f3b2 100644 --- a/trunk/src/app/srs_app_dash.cpp +++ b/trunk/src/app/srs_app_dash.cpp @@ -40,10 +40,12 @@ using namespace std; SrsInitMp4::SrsInitMp4() { fw = new SrsFileWriter(); + init = new SrsMp4M2tsInitEncoder(); } SrsInitMp4::~SrsInitMp4() { + srs_freep(init); srs_freep(fw); } @@ -57,228 +59,11 @@ int SrsInitMp4::write(SrsFormat* format, bool video, int tid) 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 = tid; - - if (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->avc_config = format->vcodec->avc_extra_data; - - 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 = tid; - 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->asc = format->acodec->aac_extra_data; - - 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 = tid; - 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) { + if ((ret = init->initialize(fw)) != 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) { + if ((ret = init->write(format, video, tid)) != ERROR_SUCCESS) { return ret; } @@ -288,21 +73,24 @@ int SrsInitMp4::write(SrsFormat* format, bool video, int tid) SrsFragmentedMp4::SrsFragmentedMp4() { fw = new SrsFileWriter(); + enc = new SrsMp4M2tsSegmentEncoder(); } SrsFragmentedMp4::~SrsFragmentedMp4() { + srs_freep(enc); srs_freep(fw); } -int SrsFragmentedMp4::initialize(SrsRequest* r, bool video, SrsMpdWriter* mpd) +int SrsFragmentedMp4::initialize(SrsRequest* r, bool video, SrsMpdWriter* mpd, uint32_t tid) { int ret = ERROR_SUCCESS; string file_home; string file_name; - - if ((ret = mpd->get_fragment(video, file_home, file_name)) != ERROR_SUCCESS) { + int64_t sequence_number; + uint64_t basetime; + if ((ret = mpd->get_fragment(video, file_home, file_name, sequence_number, basetime)) != ERROR_SUCCESS) { return ret; } @@ -319,6 +107,10 @@ int SrsFragmentedMp4::initialize(SrsRequest* r, bool video, SrsMpdWriter* mpd) return ret; } + if ((ret = enc->initialize(fw, (uint32_t)sequence_number, basetime, tid)) != ERROR_SUCCESS) { + return ret; + } + return ret; } @@ -326,15 +118,40 @@ int SrsFragmentedMp4::write(SrsSharedPtrMessage* shared_msg, SrsFormat* format) { int ret = ERROR_SUCCESS; + if (shared_msg->is_audio()) { + uint8_t* sample = (uint8_t*)format->raw; + uint32_t nb_sample = (uint32_t)format->nb_raw; + + uint32_t dts = (uint32_t)shared_msg->timestamp; + ret = enc->write_sample(SrsMp4HandlerTypeSOUN, 0x00, dts, dts, sample, nb_sample); + } else if (shared_msg->is_video()) { + SrsVideoAvcFrameType frame_type = format->video->frame_type; + uint32_t cts = (uint32_t)format->video->cts; + + uint32_t dts = (uint32_t)shared_msg->timestamp; + uint32_t pts = dts + cts; + + uint8_t* sample = (uint8_t*)format->raw; + uint32_t nb_sample = (uint32_t)format->nb_raw; + ret = enc->write_sample(SrsMp4HandlerTypeVIDE, frame_type, dts, pts, sample, nb_sample); + } else { + return ret; + } + append(shared_msg->timestamp); return ret; } -int SrsFragmentedMp4::reap() +int SrsFragmentedMp4::reap(uint64_t& dts) { int ret = ERROR_SUCCESS; + if ((ret = enc->flush(dts)) != ERROR_SUCCESS) { + srs_error("DASH: Flush encoder failed, ret=%d", ret); + return ret; + } + srs_freep(fw); if ((ret = rename()) != ERROR_SUCCESS) { @@ -369,6 +186,8 @@ int SrsMpdWriter::initialize(SrsRequest* r) string mpd_path = srs_path_build_stream(mpd_file, req->vhost, req->app, req->stream); fragment_home = srs_path_dirname(mpd_path) + "/" + req->stream; + srs_trace("DASH: Config fragment=%d, period=%d", fragment, update_period); + return ret; } @@ -451,17 +270,19 @@ int SrsMpdWriter::write(SrsFormat* format) return ret; } -int SrsMpdWriter::get_fragment(bool video, std::string& home, std::string& file_name) +int SrsMpdWriter::get_fragment(bool video, std::string& home, std::string& file_name, int64_t& sn, uint64_t& basetime) { int ret = ERROR_SUCCESS; home = fragment_home; - int64_t sequence_number = srs_update_system_time_ms() / fragment; + sn = srs_update_system_time_ms() / fragment; + basetime = sn * fragment; + if (video) { - file_name = "video-" + srs_int2str(sequence_number) + ".m4s"; + file_name = "video-" + srs_int2str(sn) + ".m4s"; } else { - file_name = "audio-" + srs_int2str(sequence_number) + ".m4s"; + file_name = "audio-" + srs_int2str(sn) + ".m4s"; } return ret; @@ -476,6 +297,7 @@ SrsDashController::SrsDashController() vcurrent = acurrent = NULL; vfragments = new SrsFragmentWindow(); afragments = new SrsFragmentWindow(); + audio_dts = video_dts = 0; } SrsDashController::~SrsDashController() @@ -503,14 +325,14 @@ int SrsDashController::initialize(SrsRequest* r) srs_freep(vcurrent); vcurrent = new SrsFragmentedMp4(); - if ((ret = vcurrent->initialize(req, true, mpd)) != ERROR_SUCCESS) { + if ((ret = vcurrent->initialize(req, true, mpd, video_tack_id)) != ERROR_SUCCESS) { srs_error("DASH: Initialize the video fragment failed, ret=%d", ret); return ret; } srs_freep(acurrent); acurrent = new SrsFragmentedMp4(); - if ((ret = acurrent->initialize(req, false, mpd)) != ERROR_SUCCESS) { + if ((ret = acurrent->initialize(req, false, mpd, audio_track_id)) != ERROR_SUCCESS) { srs_error("DASH: Initialize the audio fragment failed, ret=%d", ret); return ret; } @@ -527,14 +349,14 @@ int SrsDashController::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* fo } if (acurrent->duration() >= fragment) { - if ((ret = acurrent->reap()) != ERROR_SUCCESS) { + if ((ret = acurrent->reap(audio_dts)) != ERROR_SUCCESS) { return ret; } afragments->append(acurrent); acurrent = new SrsFragmentedMp4(); - if ((ret = acurrent->initialize(req, false, mpd)) != ERROR_SUCCESS) { + if ((ret = acurrent->initialize(req, false, mpd, audio_track_id)) != ERROR_SUCCESS) { srs_error("DASH: Initialize the audio fragment failed, ret=%d", ret); return ret; } @@ -564,14 +386,14 @@ int SrsDashController::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* fo bool reopen = format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame && vcurrent->duration() >= fragment; if (reopen) { - if ((ret = vcurrent->reap()) != ERROR_SUCCESS) { + if ((ret = vcurrent->reap(video_dts)) != ERROR_SUCCESS) { return ret; } vfragments->append(vcurrent); vcurrent = new SrsFragmentedMp4(); - if ((ret = vcurrent->initialize(req, true, mpd)) != ERROR_SUCCESS) { + if ((ret = vcurrent->initialize(req, true, mpd, video_tack_id)) != ERROR_SUCCESS) { srs_error("DASH: Initialize the video fragment failed, ret=%d", ret); return ret; } @@ -610,8 +432,8 @@ int SrsDashController::refresh_init_mp4(SrsSharedPtrMessage* msg, SrsFormat* for { int ret = ERROR_SUCCESS; - if (msg->size <= 0 || (msg->is_video() && format->vcodec->is_avc_codec_ok()) - || (msg->is_audio() && format->acodec->is_aac_codec_ok())) { + if (msg->size <= 0 || (msg->is_video() && !format->vcodec->is_avc_codec_ok()) + || (msg->is_audio() && !format->acodec->is_aac_codec_ok())) { srs_warn("DASH: Ignore empty sequence header."); return ret; } diff --git a/trunk/src/app/srs_app_dash.hpp b/trunk/src/app/srs_app_dash.hpp index 3a6775cb11..6872033507 100644 --- a/trunk/src/app/srs_app_dash.hpp +++ b/trunk/src/app/srs_app_dash.hpp @@ -37,6 +37,8 @@ class SrsSharedPtrMessage; class SrsFormat; class SrsFileWriter; class SrsMpdWriter; +class SrsMp4M2tsInitEncoder; +class SrsMp4M2tsSegmentEncoder; /** * The init mp4 for FMP4. @@ -45,6 +47,7 @@ class SrsInitMp4 : public SrsFragment { private: SrsFileWriter* fw; + SrsMp4M2tsInitEncoder* init; public: SrsInitMp4(); virtual ~SrsInitMp4(); @@ -60,16 +63,17 @@ class SrsFragmentedMp4 : public SrsFragment { private: SrsFileWriter* fw; + SrsMp4M2tsSegmentEncoder* enc; public: SrsFragmentedMp4(); virtual ~SrsFragmentedMp4(); public: // Initialize the fragment, create the home dir, open the file. - virtual int initialize(SrsRequest* r, bool video, SrsMpdWriter* mpd); + virtual int initialize(SrsRequest* r, bool video, SrsMpdWriter* mpd, uint32_t tid); // Write media message to fragment. virtual int write(SrsSharedPtrMessage* shared_msg, SrsFormat* format); // Reap the fragment, close the fd and rename tmp to official file. - virtual int reap(); + virtual int reap(uint64_t& dts); }; /** @@ -103,7 +107,8 @@ class SrsMpdWriter virtual int write(SrsFormat* format); public: // Get the fragment relative home and filename. - virtual int get_fragment(bool video, std::string& home, std::string& filename); + // The basetime is the absolute time in ms, while the sn(sequence number) is basetime/fragment. + virtual int get_fragment(bool video, std::string& home, std::string& filename, int64_t& sn, uint64_t& basetime); }; /** @@ -119,6 +124,8 @@ class SrsDashController SrsFragmentWindow* vfragments; SrsFragmentedMp4* acurrent; SrsFragmentWindow* afragments; + uint64_t audio_dts; + uint64_t video_dts; private: // The fragment duration in ms to reap it. int fragment; diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 2471798d01..cce3699076 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -132,7 +132,7 @@ int SrsDvrSegmenter::write_metadata(SrsSharedPtrMessage* metadata) return encode_metadata(metadata); } -int SrsDvrSegmenter::write_audio(SrsSharedPtrMessage* shared_audio) +int SrsDvrSegmenter::write_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format) { int ret = ERROR_SUCCESS; @@ -147,14 +147,14 @@ int SrsDvrSegmenter::write_audio(SrsSharedPtrMessage* shared_audio) return ret; } - if ((ret = encode_audio(audio)) != ERROR_SUCCESS) { + if ((ret = encode_audio(audio, format)) != ERROR_SUCCESS) { return ret; } return ret; } -int SrsDvrSegmenter::write_video(SrsSharedPtrMessage* shared_video) +int SrsDvrSegmenter::write_video(SrsSharedPtrMessage* shared_video, SrsFormat* format) { int ret = ERROR_SUCCESS; @@ -165,7 +165,7 @@ int SrsDvrSegmenter::write_video(SrsSharedPtrMessage* shared_video) return ret; } - if ((ret = encode_video(video)) != ERROR_SUCCESS) { + if ((ret = encode_video(video, format)) != ERROR_SUCCESS) { return ret; } @@ -409,7 +409,7 @@ int SrsDvrFlvSegmenter::encode_metadata(SrsSharedPtrMessage* metadata) return ret; } -int SrsDvrFlvSegmenter::encode_audio(SrsSharedPtrMessage* audio) +int SrsDvrFlvSegmenter::encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format) { int ret = ERROR_SUCCESS; @@ -422,15 +422,14 @@ int SrsDvrFlvSegmenter::encode_audio(SrsSharedPtrMessage* audio) return ret; } -int SrsDvrFlvSegmenter::encode_video(SrsSharedPtrMessage* video) +int SrsDvrFlvSegmenter::encode_video(SrsSharedPtrMessage* video, SrsFormat* format) { int ret = ERROR_SUCCESS; char* payload = video->payload; int size = video->size; - bool sh = SrsFlvVideo::sh(payload, size); - bool keyframe = SrsFlvVideo::h264(payload, size) - && SrsFlvVideo::keyframe(payload, size) && !sh; + bool sh = (format->video->avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader); + bool keyframe = (!sh && format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame); if (keyframe) { has_keyframe = true; @@ -460,13 +459,11 @@ int SrsDvrFlvSegmenter::close_encoder() SrsDvrMp4Segmenter::SrsDvrMp4Segmenter() { enc = new SrsMp4Encoder(); - buffer = new SrsBuffer(); } SrsDvrMp4Segmenter::~SrsDvrMp4Segmenter() { srs_freep(enc); - srs_freep(buffer); } int SrsDvrMp4Segmenter::refresh_metadata() @@ -493,37 +490,14 @@ int SrsDvrMp4Segmenter::encode_metadata(SrsSharedPtrMessage* /*metadata*/) return ERROR_SUCCESS; } -int SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio) +int SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format) { - int ret = ERROR_SUCCESS; - - if ((ret = buffer->initialize(audio->payload, audio->size)) != ERROR_SUCCESS) { - return ret; - } - - // E.4.2.1 AUDIODATA, flv_v10_1.pdf, page 3 - if (!buffer->require(1)) { - ret = ERROR_FLV_REQUIRE_SPACE; - srs_error("DVR require flva 1 byte space. ret=%d", ret); - return ret; - } - uint8_t v = buffer->read_1bytes(); - SrsAudioCodecId sound_format = (SrsAudioCodecId)((v >> 4) & 0x0f); - SrsAudioSampleRate sound_rate = (SrsAudioSampleRate)((v >> 2) & 0x03); - SrsAudioSampleBits sound_size = (SrsAudioSampleBits)((v >> 1) & 0x01); - SrsAudioChannels channels = (SrsAudioChannels)(v&0x01); - - uint16_t ct = 0x00; - if (sound_format == SrsAudioCodecIdAAC) { - if (!buffer->require(1)) { - ret = ERROR_FLV_REQUIRE_SPACE; - srs_error("DVR require flva 1 byte space, format=%d. ret=%d", sound_format, ret); - return ret; - } - v = buffer->read_1bytes(); - ct = (v == 0? SrsAudioAacFrameTraitSequenceHeader:SrsAudioAacFrameTraitRawData); - } + SrsAudioCodecId sound_format = format->acodec->id; + SrsAudioSampleRate sound_rate = format->acodec->sound_rate; + SrsAudioSampleBits sound_size = format->acodec->sound_size; + SrsAudioChannels channels = format->acodec->sound_type; + SrsAudioAacFrameTrait ct = format->audio->aac_packet_type; if (ct == SrsAudioAacFrameTraitSequenceHeader) { enc->acodec = sound_format; enc->sample_rate = sound_rate; @@ -531,38 +505,20 @@ int SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio) enc->channels = channels; } - uint8_t* sample = (uint8_t*)(buffer->data() + buffer->pos()); - uint32_t nb_sample = (uint32_t)(buffer->size() - buffer->pos()); + uint8_t* sample = (uint8_t*)format->raw; + uint32_t nb_sample = (uint32_t)format->nb_raw; uint32_t dts = (uint32_t)audio->timestamp; return enc->write_sample(SrsMp4HandlerTypeSOUN, 0x00, ct, dts, dts, sample, nb_sample); } -int SrsDvrMp4Segmenter::encode_video(SrsSharedPtrMessage* video) +int SrsDvrMp4Segmenter::encode_video(SrsSharedPtrMessage* video, SrsFormat* format) { - int ret = ERROR_SUCCESS; - - if ((ret = buffer->initialize(video->payload, video->size)) != ERROR_SUCCESS) { - return ret; - } + SrsVideoAvcFrameType frame_type = format->video->frame_type; + SrsVideoCodecId codec_id = format->vcodec->id; - // E.4.3.1 VIDEODATA, flv_v10_1.pdf, page 5 - if (!buffer->require(1)) { - ret = ERROR_FLV_REQUIRE_SPACE; - srs_error("DVR require flvv 1 byte space. ret=%d", ret); - return ret; - } - uint8_t v = buffer->read_1bytes(); - SrsVideoAvcFrameType frame_type = (SrsVideoAvcFrameType)((v>>4)&0x0f); - SrsVideoCodecId codec_id = (SrsVideoCodecId)(v&0x0f); - - if (!buffer->require(4)) { - ret = ERROR_FLV_REQUIRE_SPACE; - srs_error("DVR require flvv 4 bytes space, codec=%d. ret=%d", codec_id, ret); - return ret; - } - SrsVideoAvcFrameTrait ct = (SrsVideoAvcFrameTrait)buffer->read_1bytes(); - uint32_t cts = (uint32_t)buffer->read_3bytes(); + SrsVideoAvcFrameTrait ct = format->video->avc_packet_type; + uint32_t cts = (uint32_t)format->video->cts; if (ct == SrsVideoAvcFrameTraitSequenceHeader) { enc->vcodec = codec_id; @@ -571,8 +527,8 @@ int SrsDvrMp4Segmenter::encode_video(SrsSharedPtrMessage* video) uint32_t dts = (uint32_t)video->timestamp; uint32_t pts = dts + cts; - uint8_t* sample = (uint8_t*)(buffer->data() + buffer->pos()); - uint32_t nb_sample = (uint32_t)(buffer->size() - buffer->pos()); + uint8_t* sample = (uint8_t*)format->raw; + uint32_t nb_sample = (uint32_t)format->nb_raw; return enc->write_sample(SrsMp4HandlerTypeVIDE, frame_type, ct, dts, pts, sample, nb_sample); } @@ -686,7 +642,7 @@ int SrsDvrPlan::on_meta_data(SrsSharedPtrMessage* shared_metadata) return segment->write_metadata(shared_metadata); } -int SrsDvrPlan::on_audio(SrsSharedPtrMessage* shared_audio) +int SrsDvrPlan::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format) { int ret = ERROR_SUCCESS; @@ -694,14 +650,14 @@ int SrsDvrPlan::on_audio(SrsSharedPtrMessage* shared_audio) return ret; } - if ((ret = segment->write_audio(shared_audio)) != ERROR_SUCCESS) { + if ((ret = segment->write_audio(shared_audio, format)) != ERROR_SUCCESS) { return ret; } return ret; } -int SrsDvrPlan::on_video(SrsSharedPtrMessage* shared_video) +int SrsDvrPlan::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format) { int ret = ERROR_SUCCESS; @@ -709,7 +665,7 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* shared_video) return ret; } - if ((ret = segment->write_video(shared_video)) != ERROR_SUCCESS) { + if ((ret = segment->write_video(shared_video, format)) != ERROR_SUCCESS) { return ret; } @@ -857,7 +813,7 @@ void SrsDvrSegmentPlan::on_unpublish() { } -int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* shared_audio) +int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format) { int ret = ERROR_SUCCESS; @@ -865,14 +821,14 @@ int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* shared_audio) return ret; } - if ((ret = SrsDvrPlan::on_audio(shared_audio)) != ERROR_SUCCESS) { + if ((ret = SrsDvrPlan::on_audio(shared_audio, format)) != ERROR_SUCCESS) { return ret; } return ret; } -int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* shared_video) +int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format) { int ret = ERROR_SUCCESS; @@ -880,7 +836,7 @@ int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* shared_video) return ret; } - if ((ret = SrsDvrPlan::on_video(shared_video)) != ERROR_SUCCESS) { + if ((ret = SrsDvrPlan::on_video(shared_video, format)) != ERROR_SUCCESS) { return ret; } @@ -1035,24 +991,24 @@ int SrsDvr::on_meta_data(SrsSharedPtrMessage* metadata) return ret; } -int SrsDvr::on_audio(SrsSharedPtrMessage* shared_audio) +int SrsDvr::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format) { // the dvr for this stream is not actived. if (!actived) { return ERROR_SUCCESS; } - return plan->on_audio(shared_audio); + return plan->on_audio(shared_audio, format); } -int SrsDvr::on_video(SrsSharedPtrMessage* shared_video) +int SrsDvr::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format) { // the dvr for this stream is not actived. if (!actived) { return ERROR_SUCCESS; } - return plan->on_video(shared_video); + return plan->on_video(shared_video, format); } int SrsDvr::on_reload_vhost_dvr_apply(string vhost) diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index e6e3f5b001..c08b5235eb 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -43,6 +43,7 @@ class SrsJsonObject; class SrsThread; class SrsMp4Encoder; class SrsFragment; +class SrsFormat; #include #include @@ -82,10 +83,10 @@ class SrsDvrSegmenter : public ISrsReloadHandler virtual int write_metadata(SrsSharedPtrMessage* metadata); // Write audio packet. // @param shared_audio, directly ptr, copy it if need to save it. - virtual int write_audio(SrsSharedPtrMessage* shared_audio); + virtual int write_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format); // Write video packet. // @param shared_video, directly ptr, copy it if need to save it. - virtual int write_video(SrsSharedPtrMessage* shared_video); + virtual int write_video(SrsSharedPtrMessage* shared_video, SrsFormat* format); // Refresh the metadata. For example, there is duration in flv metadata, // when DVR in append mode, the duration must be update every some seconds. // @remark Maybe ignored by concreate segmenter. @@ -96,8 +97,8 @@ class SrsDvrSegmenter : public ISrsReloadHandler protected: virtual int open_encoder() = 0; virtual int encode_metadata(SrsSharedPtrMessage* metadata) = 0; - virtual int encode_audio(SrsSharedPtrMessage* audio) = 0; - virtual int encode_video(SrsSharedPtrMessage* video) = 0; + virtual int encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format) = 0; + virtual int encode_video(SrsSharedPtrMessage* video, SrsFormat* format) = 0; virtual int close_encoder() = 0; private: // Generate the flv segment path. @@ -134,8 +135,8 @@ class SrsDvrFlvSegmenter : public SrsDvrSegmenter protected: virtual int open_encoder(); virtual int encode_metadata(SrsSharedPtrMessage* metadata); - virtual int encode_audio(SrsSharedPtrMessage* audio); - virtual int encode_video(SrsSharedPtrMessage* video); + virtual int encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format); + virtual int encode_video(SrsSharedPtrMessage* video, SrsFormat* format); virtual int close_encoder(); }; @@ -147,8 +148,6 @@ class SrsDvrMp4Segmenter : public SrsDvrSegmenter private: // The MP4 encoder, for MP4 target. SrsMp4Encoder* enc; - // The buffer to demux the packet to mp4 sample. - SrsBuffer* buffer; public: SrsDvrMp4Segmenter(); virtual ~SrsDvrMp4Segmenter(); @@ -157,8 +156,8 @@ class SrsDvrMp4Segmenter : public SrsDvrSegmenter protected: virtual int open_encoder(); virtual int encode_metadata(SrsSharedPtrMessage* metadata); - virtual int encode_audio(SrsSharedPtrMessage* audio); - virtual int encode_video(SrsSharedPtrMessage* video); + virtual int encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format); + virtual int encode_video(SrsSharedPtrMessage* video, SrsFormat* format); virtual int close_encoder(); }; @@ -199,8 +198,8 @@ class SrsDvrPlan : public ISrsReloadHandler virtual int on_publish() = 0; virtual void on_unpublish() = 0; virtual int on_meta_data(SrsSharedPtrMessage* shared_metadata); - virtual int on_audio(SrsSharedPtrMessage* shared_audio); - virtual int on_video(SrsSharedPtrMessage* shared_video); + virtual int on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format); + virtual int on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format); // Internal interface for segmenter. public: // When segmenter close a segment. @@ -238,8 +237,8 @@ class SrsDvrSegmentPlan : public SrsDvrPlan virtual int initialize(SrsOriginHub* h, SrsDvrSegmenter* s, SrsRequest* r); virtual int on_publish(); virtual void on_unpublish(); - virtual int on_audio(SrsSharedPtrMessage* shared_audio); - virtual int on_video(SrsSharedPtrMessage* shared_video); + virtual int on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format); + virtual int on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format); private: virtual int update_duration(SrsSharedPtrMessage* msg); // interface ISrsReloadHandler @@ -290,12 +289,12 @@ class SrsDvr : public ISrsReloadHandler * mux the audio packets to dvr. * @param shared_audio, directly ptr, copy it if need to save it. */ - virtual int on_audio(SrsSharedPtrMessage* shared_audio); + virtual int on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* foramt); /** * mux the video packets to dvr. * @param shared_video, directly ptr, copy it if need to save it. */ - virtual int on_video(SrsSharedPtrMessage* shared_video); + virtual int on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format); // interface ISrsReloadHandler public: virtual int on_reload_vhost_dvr_apply(std::string vhost); diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index a695c0af84..a695763776 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -1024,7 +1024,7 @@ int SrsOriginHub::on_audio(SrsSharedPtrMessage* shared_audio) ret = ERROR_SUCCESS; } - if ((ret = dvr->on_audio(msg)) != ERROR_SUCCESS) { + if ((ret = dvr->on_audio(msg, format)) != ERROR_SUCCESS) { srs_warn("dvr process audio message failed, ignore and disable dvr. ret=%d", ret); // unpublish, ignore ret. @@ -1126,7 +1126,7 @@ int SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_sequence_h ret = ERROR_SUCCESS; } - if ((ret = dvr->on_video(msg)) != ERROR_SUCCESS) { + if ((ret = dvr->on_video(msg, format)) != ERROR_SUCCESS) { srs_warn("dvr process video message failed, ignore and disable dvr. ret=%d", ret); // unpublish, ignore ret. @@ -1278,13 +1278,28 @@ int SrsOriginHub::on_dvr_request_sh() return ret; } - if (cache_sh_video && (ret = dvr->on_video(cache_sh_video)) != ERROR_SUCCESS) { - srs_error("dvr process video sequence header message failed. ret=%d", ret); - return ret; + if (cache_sh_video) { + // TODO: Use cached format for sh. + if ((ret = format->on_video(cache_sh_video)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = dvr->on_video(cache_sh_video, format)) != ERROR_SUCCESS) { + srs_error("dvr process video sequence header message failed. ret=%d", ret); + return ret; + } } - if (cache_sh_audio && (ret = dvr->on_audio(cache_sh_audio)) != ERROR_SUCCESS) { - srs_error("dvr process audio sequence header message failed. ret=%d", ret); - return ret; + + if (cache_sh_audio) { + // TODO: Use cached format for sh. + if ((ret = format->on_audio(cache_sh_audio)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = dvr->on_audio(cache_sh_audio, format)) != ERROR_SUCCESS) { + srs_error("dvr process audio sequence header message failed. ret=%d", ret); + return ret; + } } return ret; diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index ce2af256ae..a237ec29d4 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -511,6 +511,8 @@ SrsFormat::SrsFormat() video = NULL; buffer = new SrsBuffer(); avc_parse_sps = true; + raw = NULL; + nb_raw = 0; } SrsFormat::~SrsFormat() @@ -689,6 +691,10 @@ int SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) video->cts = composition_time; video->avc_packet_type = (SrsVideoAvcFrameTrait)avc_packet_type; + // Update the RAW AVC data. + raw = stream->data() + stream->pos(); + nb_raw = stream->size() - stream->pos(); + if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) { if ((ret = avc_demux_sps_pps(stream)) != ERROR_SUCCESS) { return ret; @@ -819,7 +825,9 @@ int SrsFormat::avc_demux_sps() } SrsBuffer stream; - if ((ret = stream.initialize(&vcodec->sequenceParameterSetNALUnit[0], vcodec->sequenceParameterSetNALUnit.size())) != ERROR_SUCCESS) { + char* sps = &vcodec->sequenceParameterSetNALUnit[0]; + int nbsps = (int)vcodec->sequenceParameterSetNALUnit.size(); + if ((ret = stream.initialize(sps, nbsps)) != ERROR_SUCCESS) { return ret; } @@ -1280,6 +1288,10 @@ int SrsFormat::audio_aac_demux(SrsBuffer* stream, int64_t timestamp) SrsAudioAacFrameTrait aac_packet_type = (SrsAudioAacFrameTrait)stream->read_1bytes(); audio->aac_packet_type = (SrsAudioAacFrameTrait)aac_packet_type; + // Update the RAW AAC data. + raw = stream->data() + stream->pos(); + nb_raw = stream->size() - stream->pos(); + if (aac_packet_type == SrsAudioAacFrameTraitSequenceHeader) { // AudioSpecificConfig // 1.6.2.1 AudioSpecificConfig, in ISO_IEC_14496-3-AAC-2001.pdf, page 33. @@ -1363,6 +1375,10 @@ int SrsFormat::audio_mp3_demux(SrsBuffer* stream, int64_t timestamp) // we always decode aac then mp3. srs_assert(acodec->id == SrsAudioCodecIdMP3); + // Update the RAW MP3 data. + raw = stream->data() + stream->pos(); + nb_raw = stream->size() - stream->pos(); + stream->skip(1); if (stream->empty()) { return ret; diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index 54747e04c9..8ad4bb3161 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -235,6 +235,7 @@ class SrsFlvVideo /** * check codec h264, keyframe, sequence header */ + // TODO: FIXME: Remove it, use SrsFormat instead. static bool sh(char* data, int size); /** * check codec h264. @@ -678,6 +679,10 @@ class SrsFormat SrsAudioCodecConfig* acodec; SrsVideoFrame* video; SrsVideoCodecConfig* vcodec; +public: + char* raw; + int nb_raw; +private: SrsBuffer* buffer; public: // for sequence header, whether parse the h.264 sps. diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 1be054fe3d..c3fb41f4ca 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -265,6 +265,7 @@ #define ERROR_MP4_ASC_CHANGE 3086 #define ERROR_DASH_WRITE_FAILED 3087 #define ERROR_TS_CONTEXT_NOT_READY 3088 +#define ERROR_MP4_ILLEGAL_MOOF 3089 /////////////////////////////////////////////////////// // HTTP/StreamCaster/KAFKA protocol error. diff --git a/trunk/src/kernel/srs_kernel_mp4.cpp b/trunk/src/kernel/srs_kernel_mp4.cpp index cc13d77748..7d46eee773 100644 --- a/trunk/src/kernel/srs_kernel_mp4.cpp +++ b/trunk/src/kernel/srs_kernel_mp4.cpp @@ -743,6 +743,13 @@ SrsMp4FileTypeBox::~SrsMp4FileTypeBox() { } +void SrsMp4FileTypeBox::set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1) +{ + compatible_brands.resize(2); + compatible_brands[0] = b0; + compatible_brands[1] = b1; +} + void SrsMp4FileTypeBox::set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1, SrsMp4BoxBrand b2, SrsMp4BoxBrand b3) { compatible_brands.resize(4); @@ -835,6 +842,30 @@ SrsMp4MovieFragmentBox::~SrsMp4MovieFragmentBox() { } +SrsMp4MovieFragmentHeaderBox* SrsMp4MovieFragmentBox::mfhd() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeMFHD); + return dynamic_cast(box); +} + +void SrsMp4MovieFragmentBox::set_mfhd(SrsMp4MovieFragmentHeaderBox* v) +{ + remove(SrsMp4BoxTypeMFHD); + boxes.push_back(v); +} + +SrsMp4TrackFragmentBox* SrsMp4MovieFragmentBox::traf() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeTRAF); + return dynamic_cast(box); +} + +void SrsMp4MovieFragmentBox::set_traf(SrsMp4TrackFragmentBox* v) +{ + remove(SrsMp4BoxTypeTRAF); + boxes.push_back(v); +} + SrsMp4MovieFragmentHeaderBox::SrsMp4MovieFragmentHeaderBox() { type = SrsMp4BoxTypeMFHD; @@ -894,6 +925,42 @@ SrsMp4TrackFragmentBox::~SrsMp4TrackFragmentBox() { } +SrsMp4TrackFragmentHeaderBox* SrsMp4TrackFragmentBox::tfhd() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeTFHD); + return dynamic_cast(box); +} + +void SrsMp4TrackFragmentBox::set_tfhd(SrsMp4TrackFragmentHeaderBox* v) +{ + remove(SrsMp4BoxTypeTFHD); + boxes.push_back(v); +} + +SrsMp4TrackFragmentDecodeTimeBox* SrsMp4TrackFragmentBox::tfdt() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeTFDT); + return dynamic_cast(box); +} + +void SrsMp4TrackFragmentBox::set_tfdt(SrsMp4TrackFragmentDecodeTimeBox* v) +{ + remove(SrsMp4BoxTypeTFDT); + boxes.push_back(v); +} + +SrsMp4TrackFragmentRunBox* SrsMp4TrackFragmentBox::trun() +{ + SrsMp4Box* box = get(SrsMp4BoxTypeTRUN); + return dynamic_cast(box); +} + +void SrsMp4TrackFragmentBox::set_trun(SrsMp4TrackFragmentRunBox* v) +{ + remove(SrsMp4BoxTypeTRUN); + boxes.push_back(v); +} + SrsMp4TrackFragmentHeaderBox::SrsMp4TrackFragmentHeaderBox() { type = SrsMp4BoxTypeTFHD; @@ -1012,6 +1079,13 @@ stringstream& SrsMp4TrackFragmentHeaderBox::dumps_detail(stringstream& ss, SrsMp ss << ", dsf=" << default_sample_flags; } + if ((flags&SrsMp4TfhdFlagsDurationIsEmpty) == SrsMp4TfhdFlagsDurationIsEmpty) { + ss << ", empty-duration"; + } + if ((flags&SrsMp4TfhdFlagsDefaultBaseIsMoof) == SrsMp4TfhdFlagsDefaultBaseIsMoof) { + ss << ", moof-base"; + } + return ss; } @@ -1073,10 +1147,9 @@ stringstream& SrsMp4TrackFragmentDecodeTimeBox::dumps_detail(stringstream& ss, S return ss; } -SrsMp4TrunEntry::SrsMp4TrunEntry(uint8_t v, uint32_t f) +SrsMp4TrunEntry::SrsMp4TrunEntry(SrsMp4FullBox* o) { - flags = f; - version = v; + owner = o; sample_duration = sample_size = sample_flags = 0; sample_composition_time_offset = 0; } @@ -1089,16 +1162,16 @@ int SrsMp4TrunEntry::nb_header() { int size = 0; - if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { + if ((owner->flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { size += 4; } - if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { + if ((owner->flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { size += 4; } - if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { + if ((owner->flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { size += 4; } - if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { + if ((owner->flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { size += 4; } @@ -1109,17 +1182,17 @@ int SrsMp4TrunEntry::encode_header(SrsBuffer* buf) { int ret = ERROR_SUCCESS; - if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { + if ((owner->flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { buf->write_4bytes(sample_duration); } - if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { + if ((owner->flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { buf->write_4bytes(sample_size); } - if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { + if ((owner->flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { buf->write_4bytes(sample_flags); } - if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { - if (!version) { + if ((owner->flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { + if (!owner->version) { uint32_t v = (uint32_t)sample_composition_time_offset; buf->write_4bytes(v); } else { @@ -1135,17 +1208,17 @@ int SrsMp4TrunEntry::decode_header(SrsBuffer* buf) { int ret = ERROR_SUCCESS; - if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { + if ((owner->flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { sample_duration = buf->read_4bytes(); } - if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { + if ((owner->flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { sample_size = buf->read_4bytes(); } - if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { + if ((owner->flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { sample_flags = buf->read_4bytes(); } - if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { - if (!version) { + if ((owner->flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { + if (!owner->version) { uint32_t v = buf->read_4bytes(); sample_composition_time_offset = v; } else { @@ -1159,17 +1232,17 @@ int SrsMp4TrunEntry::decode_header(SrsBuffer* buf) stringstream& SrsMp4TrunEntry::dumps_detail(stringstream& ss, SrsMp4DumpContext dc) { - if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { + if ((owner->flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) { ss << "duration=" << sample_duration; } - if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { + if ((owner->flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) { ss << ", size=" << sample_size; } - if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { + if ((owner->flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) { ss << ", flags=" << sample_flags; } - if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { - ss << ", cts-offset=" << sample_composition_time_offset; + if ((owner->flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) { + ss << ", cts=" << sample_composition_time_offset; } return ss; } @@ -1256,7 +1329,7 @@ int SrsMp4TrackFragmentRunBox::decode_header(SrsBuffer* buf) } for (int i = 0; i < sample_count; i++) { - SrsMp4TrunEntry* entry = new SrsMp4TrunEntry(version, flags); + SrsMp4TrunEntry* entry = new SrsMp4TrunEntry(this); entries.push_back(entry); if ((ret = entry->decode_header(buf)) != ERROR_SUCCESS) { @@ -1274,10 +1347,10 @@ stringstream& SrsMp4TrackFragmentRunBox::dumps_detail(stringstream& ss, SrsMp4Du ss << ", samples=" << sample_count; if ((flags&SrsMp4TrunFlagsDataOffset) == SrsMp4TrunFlagsDataOffset) { - ss << ", do" << data_offset; + ss << ", data-offset=" << data_offset; } if ((flags&SrsMp4TrunFlagsFirstSample) == SrsMp4TrunFlagsFirstSample) { - ss << ", fsf=" << first_sample_flags; + ss << ", first-sample=" << first_sample_flags; } if (sample_count > 0) { @@ -1325,8 +1398,6 @@ int SrsMp4MediaDataBox::decode(SrsBuffer* buf) nb_data = (int)(sz() - nb_header()); - // Because the - return ret; } @@ -4721,6 +4792,49 @@ int SrsMp4SampleManager::write(SrsMp4MovieBox* moov) return ret; } +int SrsMp4SampleManager::write(SrsMp4MovieFragmentBox* moof, uint64_t& dts) +{ + int ret = ERROR_SUCCESS; + + SrsMp4TrackFragmentBox* traf = moof->traf(); + SrsMp4TrackFragmentRunBox* trun = traf->trun(); + + trun->flags = SrsMp4TrunFlagsDataOffset | SrsMp4TrunFlagsSampleDuration + | SrsMp4TrunFlagsSampleSize | SrsMp4TrunFlagsSampleFlag | SrsMp4TrunFlagsSampleCtsOffset; + trun->sample_count = (uint32_t)samples.size(); + + SrsMp4Sample* previous = NULL; + + vector::iterator it; + for (it = samples.begin(); it != samples.end(); ++it) { + SrsMp4Sample* sample = *it; + SrsMp4TrunEntry* entry = new SrsMp4TrunEntry(trun); + + if (!previous) { + previous = sample; + entry->sample_flags = 0x02000000; + } else { + entry->sample_flags = 0x01000000; + } + + entry->sample_duration = (uint32_t)srs_min(100, sample->dts - dts); + if (entry->sample_duration == 0) { + entry->sample_duration = 40; + } + dts = sample->dts; + + entry->sample_size = sample->nb_data; + entry->sample_composition_time_offset = (int64_t)(sample->pts - sample->dts); + if (entry->sample_composition_time_offset < 0) { + trun->version = 1; + } + + trun->entries.push_back(entry); + } + + return ret; +} + int SrsMp4SampleManager::write_track(SrsFrameType track, SrsMp4DecodingTime2SampleBox* stts, SrsMp4SyncSampleBox* stss, SrsMp4CompositionTime2SampleBox* ctts, SrsMp4Sample2ChunkBox* stsc, SrsMp4SampleSizeBox* stsz, SrsMp4ChunkOffsetBox* stco) @@ -5546,7 +5660,7 @@ int SrsMp4Encoder::flush() if (!nb_audios && !nb_videos) { ret = ERROR_MP4_ILLEGAL_MOOV; - srs_error("MP4 missing audio and video track. ret=%d", ret); + srs_error("MP4: Missing audio and video track. ret=%d", ret); return ret; } @@ -5821,3 +5935,438 @@ int SrsMp4Encoder::do_write_sample(SrsMp4Sample* ps, uint8_t* sample, uint32_t n return ret; } +SrsMp4M2tsInitEncoder::SrsMp4M2tsInitEncoder() +{ + writer = NULL; +} + +SrsMp4M2tsInitEncoder::~SrsMp4M2tsInitEncoder() +{ +} + +int SrsMp4M2tsInitEncoder::initialize(ISrsWriter* w) +{ + writer = w; + return ERROR_SUCCESS; +} + +int SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid) +{ + int ret = ERROR_SUCCESS; + + // 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 = tid; + + if (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->avc_config = format->vcodec->avc_extra_data; + + 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 = tid; + 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->asc = format->acodec->aac_extra_data; + + 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 = tid; + 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 = writer->write(data, nb_data, NULL)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +SrsMp4M2tsSegmentEncoder::SrsMp4M2tsSegmentEncoder() +{ + writer = NULL; + nb_audios = nb_videos = 0; + samples = new SrsMp4SampleManager(); + buffer = new SrsBuffer(); + sequence_number = 0; + decode_basetime = 0; + data_offset = 0; + mdat_bytes = 0; +} + +SrsMp4M2tsSegmentEncoder::~SrsMp4M2tsSegmentEncoder() +{ + srs_freep(samples); + srs_freep(buffer); +} + +int SrsMp4M2tsSegmentEncoder::initialize(ISrsWriter* w, uint32_t sequence, uint64_t basetime, uint32_t tid) +{ + int ret = ERROR_SUCCESS; + + writer = w; + track_id = tid; + sequence_number = sequence; + decode_basetime = basetime; + + // Write styp box. + if (true) { + SrsMp4SegmentTypeBox* styp = new SrsMp4SegmentTypeBox(); + SrsAutoFree(SrsMp4SegmentTypeBox, styp); + + styp->major_brand = SrsMp4BoxBrandMSDH; + styp->minor_version = 0; + styp->set_compatible_brands(SrsMp4BoxBrandMSDH, SrsMp4BoxBrandDASH); + + int nb_data = styp->nb_bytes(); + std::vector data(nb_data); + if ((ret = buffer->initialize(&data[0], nb_data)) != ERROR_SUCCESS) { + return ret; + } + if ((ret = styp->encode(buffer)) != ERROR_SUCCESS) { + return ret; + } + + // TODO: FIXME: Ensure write ok. + if ((ret = writer->write(&data[0], nb_data, NULL)) != ERROR_SUCCESS) { + return ret; + } + + data_offset = nb_data; + } + return ret; +} + +int SrsMp4M2tsSegmentEncoder::write_sample(SrsMp4HandlerType ht, + uint16_t ft, uint32_t dts, uint32_t pts, uint8_t* sample, uint32_t nb_sample +) { + int ret = ERROR_SUCCESS; + + SrsMp4Sample* ps = new SrsMp4Sample(); + + if (ht == SrsMp4HandlerTypeVIDE) { + ps->type = SrsFrameTypeVideo; + ps->frame_type = (SrsVideoAvcFrameType)ft; + ps->index = nb_videos++; + } else if (ht == SrsMp4HandlerTypeSOUN) { + ps->type = SrsFrameTypeAudio; + ps->index = nb_audios++; + } else { + srs_freep(ps); + return ret; + } + + ps->tbn = 1000; + ps->dts = dts; + ps->pts = pts; + ps->data = sample; + ps->nb_data = nb_sample; + + // Append to manager to build the moof. + samples->append(ps); + + mdat_bytes += nb_sample; + + return ret; +} + +int SrsMp4M2tsSegmentEncoder::flush(uint64_t& dts) +{ + int ret = ERROR_SUCCESS; + + if (!nb_audios && !nb_videos) { + ret = ERROR_MP4_ILLEGAL_MOOF; + srs_error("fMP4: Missing audio and video track. ret=%d", ret); + return ret; + } + + // Create a mdat box. + // its payload will be writen by samples, + // and we will update its header(size) when flush. + SrsMp4MediaDataBox* mdat = new SrsMp4MediaDataBox(); + SrsAutoFree(SrsMp4MediaDataBox, mdat); + + // Write moof. + if (true) { + SrsMp4MovieFragmentBox* moof = new SrsMp4MovieFragmentBox(); + SrsAutoFree(SrsMp4MovieFragmentBox, moof); + + SrsMp4MovieFragmentHeaderBox* mfhd = new SrsMp4MovieFragmentHeaderBox(); + moof->set_mfhd(mfhd); + + mfhd->sequence_number = sequence_number; + + SrsMp4TrackFragmentBox* traf = new SrsMp4TrackFragmentBox(); + moof->set_traf(traf); + + SrsMp4TrackFragmentHeaderBox* tfhd = new SrsMp4TrackFragmentHeaderBox(); + traf->set_tfhd(tfhd); + + tfhd->track_id = track_id; + tfhd->flags = SrsMp4TfhdFlagsDefaultBaseIsMoof; + + SrsMp4TrackFragmentDecodeTimeBox* tfdt = new SrsMp4TrackFragmentDecodeTimeBox(); + traf->set_tfdt(tfdt); + + tfdt->version = 1; + tfdt->base_media_decode_time = decode_basetime; + + SrsMp4TrackFragmentRunBox* trun = new SrsMp4TrackFragmentRunBox(); + traf->set_trun(trun); + + if ((ret = samples->write(moof, dts)) != ERROR_SUCCESS) { + return ret; + } + + int nb_data = moof->nb_bytes(); + trun->data_offset = (int32_t)(data_offset + 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) { + return ret; + } + + if ((ret = moof->encode(buffer)) != ERROR_SUCCESS) { + return ret; + } + + // TODO: FIXME: Ensure all bytes are writen. + if ((ret = writer->write(data, nb_data, NULL)) != ERROR_SUCCESS) { + return ret; + } + } + + // Write mdat. + if (true) { + mdat->nb_data = (int)mdat_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) { + return ret; + } + if ((ret = mdat->encode(buffer)) != ERROR_SUCCESS) { + return ret; + } + + // TODO: FIXME: Ensure all bytes are writen. + if ((ret = writer->write(data, nb_data, NULL)) != ERROR_SUCCESS) { + return ret; + } + + vector::iterator it; + for (it = samples->samples.begin(); it != samples->samples.end(); ++it) { + SrsMp4Sample* sample = *it; + + // TODO: FIXME: Ensure all bytes are writen. + if ((ret = writer->write(sample->data, sample->nb_data, NULL)) != ERROR_SUCCESS) { + return ret; + } + } + } + + return ret; +} + diff --git a/trunk/src/kernel/srs_kernel_mp4.hpp b/trunk/src/kernel/srs_kernel_mp4.hpp index 69efeb9715..234e479453 100644 --- a/trunk/src/kernel/srs_kernel_mp4.hpp +++ b/trunk/src/kernel/srs_kernel_mp4.hpp @@ -34,6 +34,7 @@ #include #include +class ISrsWriter; class ISrsWriteSeeker; class ISrsReadSeeker; class SrsMp4TrackBox; @@ -64,6 +65,11 @@ class SrsMp4DataReferenceBox; class SrsMp4SoundMeidaHeaderBox; class SrsMp4MovieExtendsBox; class SrsMp4TrackExtendsBox; +class SrsMp4MovieFragmentHeaderBox; +class SrsMp4TrackFragmentBox; +class SrsMp4TrackFragmentHeaderBox; +class SrsMp4TrackFragmentDecodeTimeBox; +class SrsMp4TrackFragmentRunBox; /** * 4.2 Object Structure @@ -147,6 +153,7 @@ enum SrsMp4BoxBrand SrsMp4BoxBrandISO5 = 0x69736f35, // 'iso5' SrsMp4BoxBrandMP42 = 0x6d703432, // 'mp42' SrsMp4BoxBrandDASH = 0x64617368, // 'dash' + SrsMp4BoxBrandMSDH = 0x6d736468, // 'msdh' }; /** @@ -287,6 +294,7 @@ class SrsMp4FileTypeBox : public SrsMp4Box SrsMp4FileTypeBox(); virtual ~SrsMp4FileTypeBox(); public: + virtual void set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1); virtual void set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1, SrsMp4BoxBrand b2, SrsMp4BoxBrand b3); protected: virtual int nb_header(); @@ -323,6 +331,13 @@ class SrsMp4MovieFragmentBox : public SrsMp4Box public: SrsMp4MovieFragmentBox(); virtual ~SrsMp4MovieFragmentBox(); +public: + // Get the header of moof. + virtual SrsMp4MovieFragmentHeaderBox* mfhd(); + virtual void set_mfhd(SrsMp4MovieFragmentHeaderBox* v); + // Get the traf. + virtual SrsMp4TrackFragmentBox* traf(); + virtual void set_traf(SrsMp4TrackFragmentBox* v); }; /** @@ -361,6 +376,16 @@ class SrsMp4TrackFragmentBox : public SrsMp4Box public: SrsMp4TrackFragmentBox(); virtual ~SrsMp4TrackFragmentBox(); +public: + // Get the tfhd. + virtual SrsMp4TrackFragmentHeaderBox* tfhd(); + virtual void set_tfhd(SrsMp4TrackFragmentHeaderBox* v); + // Get the tfdt. + virtual SrsMp4TrackFragmentDecodeTimeBox* tfdt(); + virtual void set_tfdt(SrsMp4TrackFragmentDecodeTimeBox* tfdt); + // Get the trun. + virtual SrsMp4TrackFragmentRunBox* trun(); + virtual void set_trun(SrsMp4TrackFragmentRunBox* v); }; /** @@ -413,7 +438,7 @@ class SrsMp4TrackFragmentHeaderBox : public SrsMp4FullBox { public: uint32_t track_id; - // all the following are optional fields +// all the following are optional fields public: // the base offset to use when calculating data offsets uint64_t base_data_offset; @@ -485,8 +510,7 @@ enum SrsMp4TrunFlags */ struct SrsMp4TrunEntry { - uint8_t version; - uint32_t flags; + SrsMp4FullBox* owner; uint32_t sample_duration; uint32_t sample_size; @@ -494,7 +518,7 @@ struct SrsMp4TrunEntry // if version == 0, unsigned int(32); otherwise, signed int(32). int64_t sample_composition_time_offset; - SrsMp4TrunEntry(uint8_t v, uint32_t f); + SrsMp4TrunEntry(SrsMp4FullBox* o); virtual ~SrsMp4TrunEntry(); virtual int nb_header(); @@ -1941,7 +1965,7 @@ class SrsMp4Sample */ class SrsMp4SampleManager { -private: +public: std::vector samples; public: SrsMp4SampleManager(); @@ -1956,6 +1980,9 @@ class SrsMp4SampleManager virtual void append(SrsMp4Sample* sample); // Write the samples info to moov. virtual int write(SrsMp4MovieBox* moov); + // Write the samples info to moof. + // @param The dts is the dts of last segment. + virtual int write(SrsMp4MovieFragmentBox* moof, uint64_t& dts); private: virtual int write_track(SrsFrameType track, SrsMp4DecodingTime2SampleBox* stts, SrsMp4SyncSampleBox* stss, SrsMp4CompositionTime2SampleBox* ctts, @@ -2122,10 +2149,10 @@ class SrsMp4Encoder SrsMp4Encoder(); virtual ~SrsMp4Encoder(); public: - // Initialize the encoder with a writer w. - // @param w The underlayer io writer, user must manage it. + // Initialize the encoder with a writer and seeker ws. + // @param ws The underlayer io writer and seeker, user must manage it. virtual int initialize(ISrsWriteSeeker* ws); - // Write a sampel to mp4. + // Write a sample to mp4. // @param ht, The sample handler type, audio/soun or video/vide. // @param ft, The frame type. For video, it's SrsVideoAvcFrameType. // @param ct, The codec type. For video, it's SrsVideoAvcFrameTrait. For audio, it's SrsAudioAacFrameTrait. @@ -2142,5 +2169,61 @@ class SrsMp4Encoder virtual int do_write_sample(SrsMp4Sample* ps, uint8_t* sample, uint32_t nb_sample); }; +/** + * A fMP4 encoder, to write the init.mp4 with sequence header. + */ +class SrsMp4M2tsInitEncoder +{ +private: + ISrsWriter* writer; +public: + SrsMp4M2tsInitEncoder(); + virtual ~SrsMp4M2tsInitEncoder(); +public: + // Initialize the encoder with a writer w. + virtual int initialize(ISrsWriter* w); + // Write the sequence header. + virtual int write(SrsFormat* format, bool video, int tid); +}; + +/** + * A fMP4 encoder, to cache segments then flush to disk, because the fMP4 should write + * trun box before mdat. + */ +class SrsMp4M2tsSegmentEncoder +{ +private: + ISrsWriter* writer; + SrsBuffer* buffer; + uint32_t sequence_number; + uint64_t decode_basetime; + uint32_t track_id; +private: + uint32_t nb_audios; + uint32_t nb_videos; + uint64_t mdat_bytes; + SrsMp4SampleManager* samples; +private: + uint64_t data_offset; +public: + SrsMp4M2tsSegmentEncoder(); + virtual ~SrsMp4M2tsSegmentEncoder(); +public: + // Initialize the encoder with a writer w. + virtual int initialize(ISrsWriter* w, uint32_t sequence, uint64_t basetime, uint32_t tid); + // Cache a sample. + // @param ht, The sample handler type, audio/soun or video/vide. + // @param ft, The frame type. For video, it's SrsVideoAvcFrameType. + // @param dts The output dts in milliseconds. + // @param pts The output pts in milliseconds. + // @param sample The output payload, user must free it. + // @param nb_sample The output size of payload. + // @remark All samples are RAW AAC/AVC data, because sequence header is writen to init.mp4. + virtual int write_sample(SrsMp4HandlerType ht, uint16_t ft, + uint32_t dts, uint32_t pts, uint8_t* sample, uint32_t nb_sample); + // Flush the encoder, to write the moof and mdat. + virtual int flush(uint64_t& dts); +}; + #endif