mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
For #299, write fMP4 for DASH.
This commit is contained in:
parent
baed1cc043
commit
0e9e1792fe
12 changed files with 834 additions and 381 deletions
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue