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
|
@ -951,8 +951,8 @@ vhost dash.srs.com {
|
||||||
# Default: off
|
# Default: off
|
||||||
enabled on;
|
enabled on;
|
||||||
# The duration of segment in seconds.
|
# The duration of segment in seconds.
|
||||||
# Default: 10
|
# Default: 3
|
||||||
dash_fragment 10;
|
dash_fragment 3;
|
||||||
# The period to update the MPD in seconds.
|
# The period to update the MPD in seconds.
|
||||||
# Default: 30
|
# Default: 30
|
||||||
dash_update_period 30;
|
dash_update_period 30;
|
||||||
|
|
|
@ -6013,7 +6013,7 @@ bool SrsConfig::get_dash_enabled(string vhost)
|
||||||
|
|
||||||
int SrsConfig::get_dash_fragment(string vhost)
|
int SrsConfig::get_dash_fragment(string vhost)
|
||||||
{
|
{
|
||||||
static int DEFAULT = 10 * 1000;
|
static int DEFAULT = 3 * 1000;
|
||||||
|
|
||||||
SrsConfDirective* conf = get_dash(vhost);
|
SrsConfDirective* conf = get_dash(vhost);
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
|
|
|
@ -40,10 +40,12 @@ using namespace std;
|
||||||
SrsInitMp4::SrsInitMp4()
|
SrsInitMp4::SrsInitMp4()
|
||||||
{
|
{
|
||||||
fw = new SrsFileWriter();
|
fw = new SrsFileWriter();
|
||||||
|
init = new SrsMp4M2tsInitEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsInitMp4::~SrsInitMp4()
|
SrsInitMp4::~SrsInitMp4()
|
||||||
{
|
{
|
||||||
|
srs_freep(init);
|
||||||
srs_freep(fw);
|
srs_freep(fw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,228 +59,11 @@ int SrsInitMp4::write(SrsFormat* format, bool video, int tid)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write ftyp box.
|
if ((ret = init->initialize(fw)) != ERROR_SUCCESS) {
|
||||||
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = ftyp->encode(buffer)) != ERROR_SUCCESS) {
|
if ((ret = init->write(format, video, tid)) != 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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,21 +73,24 @@ int SrsInitMp4::write(SrsFormat* format, bool video, int tid)
|
||||||
SrsFragmentedMp4::SrsFragmentedMp4()
|
SrsFragmentedMp4::SrsFragmentedMp4()
|
||||||
{
|
{
|
||||||
fw = new SrsFileWriter();
|
fw = new SrsFileWriter();
|
||||||
|
enc = new SrsMp4M2tsSegmentEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsFragmentedMp4::~SrsFragmentedMp4()
|
SrsFragmentedMp4::~SrsFragmentedMp4()
|
||||||
{
|
{
|
||||||
|
srs_freep(enc);
|
||||||
srs_freep(fw);
|
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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
string file_home;
|
string file_home;
|
||||||
string file_name;
|
string file_name;
|
||||||
|
int64_t sequence_number;
|
||||||
if ((ret = mpd->get_fragment(video, file_home, file_name)) != ERROR_SUCCESS) {
|
uint64_t basetime;
|
||||||
|
if ((ret = mpd->get_fragment(video, file_home, file_name, sequence_number, basetime)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,6 +107,10 @@ int SrsFragmentedMp4::initialize(SrsRequest* r, bool video, SrsMpdWriter* mpd)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((ret = enc->initialize(fw, (uint32_t)sequence_number, basetime, tid)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,15 +118,40 @@ int SrsFragmentedMp4::write(SrsSharedPtrMessage* shared_msg, SrsFormat* format)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
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);
|
append(shared_msg->timestamp);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsFragmentedMp4::reap()
|
int SrsFragmentedMp4::reap(uint64_t& dts)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
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);
|
srs_freep(fw);
|
||||||
|
|
||||||
if ((ret = rename()) != ERROR_SUCCESS) {
|
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);
|
string mpd_path = srs_path_build_stream(mpd_file, req->vhost, req->app, req->stream);
|
||||||
fragment_home = srs_path_dirname(mpd_path) + "/" + req->stream;
|
fragment_home = srs_path_dirname(mpd_path) + "/" + req->stream;
|
||||||
|
|
||||||
|
srs_trace("DASH: Config fragment=%d, period=%d", fragment, update_period);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,17 +270,19 @@ int SrsMpdWriter::write(SrsFormat* format)
|
||||||
return ret;
|
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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
home = fragment_home;
|
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) {
|
if (video) {
|
||||||
file_name = "video-" + srs_int2str(sequence_number) + ".m4s";
|
file_name = "video-" + srs_int2str(sn) + ".m4s";
|
||||||
} else {
|
} else {
|
||||||
file_name = "audio-" + srs_int2str(sequence_number) + ".m4s";
|
file_name = "audio-" + srs_int2str(sn) + ".m4s";
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -476,6 +297,7 @@ SrsDashController::SrsDashController()
|
||||||
vcurrent = acurrent = NULL;
|
vcurrent = acurrent = NULL;
|
||||||
vfragments = new SrsFragmentWindow();
|
vfragments = new SrsFragmentWindow();
|
||||||
afragments = new SrsFragmentWindow();
|
afragments = new SrsFragmentWindow();
|
||||||
|
audio_dts = video_dts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDashController::~SrsDashController()
|
SrsDashController::~SrsDashController()
|
||||||
|
@ -503,14 +325,14 @@ int SrsDashController::initialize(SrsRequest* r)
|
||||||
|
|
||||||
srs_freep(vcurrent);
|
srs_freep(vcurrent);
|
||||||
vcurrent = new SrsFragmentedMp4();
|
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);
|
srs_error("DASH: Initialize the video fragment failed, ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_freep(acurrent);
|
srs_freep(acurrent);
|
||||||
acurrent = new SrsFragmentedMp4();
|
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);
|
srs_error("DASH: Initialize the audio fragment failed, ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -527,14 +349,14 @@ int SrsDashController::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* fo
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acurrent->duration() >= fragment) {
|
if (acurrent->duration() >= fragment) {
|
||||||
if ((ret = acurrent->reap()) != ERROR_SUCCESS) {
|
if ((ret = acurrent->reap(audio_dts)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
afragments->append(acurrent);
|
afragments->append(acurrent);
|
||||||
acurrent = new SrsFragmentedMp4();
|
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);
|
srs_error("DASH: Initialize the audio fragment failed, ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -564,14 +386,14 @@ int SrsDashController::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* fo
|
||||||
bool reopen = format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame
|
bool reopen = format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame
|
||||||
&& vcurrent->duration() >= fragment;
|
&& vcurrent->duration() >= fragment;
|
||||||
if (reopen) {
|
if (reopen) {
|
||||||
if ((ret = vcurrent->reap()) != ERROR_SUCCESS) {
|
if ((ret = vcurrent->reap(video_dts)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
vfragments->append(vcurrent);
|
vfragments->append(vcurrent);
|
||||||
vcurrent = new SrsFragmentedMp4();
|
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);
|
srs_error("DASH: Initialize the video fragment failed, ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -610,8 +432,8 @@ int SrsDashController::refresh_init_mp4(SrsSharedPtrMessage* msg, SrsFormat* for
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
if (msg->size <= 0 || (msg->is_video() && format->vcodec->is_avc_codec_ok())
|
if (msg->size <= 0 || (msg->is_video() && !format->vcodec->is_avc_codec_ok())
|
||||||
|| (msg->is_audio() && format->acodec->is_aac_codec_ok())) {
|
|| (msg->is_audio() && !format->acodec->is_aac_codec_ok())) {
|
||||||
srs_warn("DASH: Ignore empty sequence header.");
|
srs_warn("DASH: Ignore empty sequence header.");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ class SrsSharedPtrMessage;
|
||||||
class SrsFormat;
|
class SrsFormat;
|
||||||
class SrsFileWriter;
|
class SrsFileWriter;
|
||||||
class SrsMpdWriter;
|
class SrsMpdWriter;
|
||||||
|
class SrsMp4M2tsInitEncoder;
|
||||||
|
class SrsMp4M2tsSegmentEncoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The init mp4 for FMP4.
|
* The init mp4 for FMP4.
|
||||||
|
@ -45,6 +47,7 @@ class SrsInitMp4 : public SrsFragment
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsFileWriter* fw;
|
SrsFileWriter* fw;
|
||||||
|
SrsMp4M2tsInitEncoder* init;
|
||||||
public:
|
public:
|
||||||
SrsInitMp4();
|
SrsInitMp4();
|
||||||
virtual ~SrsInitMp4();
|
virtual ~SrsInitMp4();
|
||||||
|
@ -60,16 +63,17 @@ class SrsFragmentedMp4 : public SrsFragment
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsFileWriter* fw;
|
SrsFileWriter* fw;
|
||||||
|
SrsMp4M2tsSegmentEncoder* enc;
|
||||||
public:
|
public:
|
||||||
SrsFragmentedMp4();
|
SrsFragmentedMp4();
|
||||||
virtual ~SrsFragmentedMp4();
|
virtual ~SrsFragmentedMp4();
|
||||||
public:
|
public:
|
||||||
// Initialize the fragment, create the home dir, open the file.
|
// 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.
|
// Write media message to fragment.
|
||||||
virtual int write(SrsSharedPtrMessage* shared_msg, SrsFormat* format);
|
virtual int write(SrsSharedPtrMessage* shared_msg, SrsFormat* format);
|
||||||
// Reap the fragment, close the fd and rename tmp to official file.
|
// 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 @@ public:
|
||||||
virtual int write(SrsFormat* format);
|
virtual int write(SrsFormat* format);
|
||||||
public:
|
public:
|
||||||
// Get the fragment relative home and filename.
|
// 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 @@ private:
|
||||||
SrsFragmentWindow* vfragments;
|
SrsFragmentWindow* vfragments;
|
||||||
SrsFragmentedMp4* acurrent;
|
SrsFragmentedMp4* acurrent;
|
||||||
SrsFragmentWindow* afragments;
|
SrsFragmentWindow* afragments;
|
||||||
|
uint64_t audio_dts;
|
||||||
|
uint64_t video_dts;
|
||||||
private:
|
private:
|
||||||
// The fragment duration in ms to reap it.
|
// The fragment duration in ms to reap it.
|
||||||
int fragment;
|
int fragment;
|
||||||
|
|
|
@ -132,7 +132,7 @@ int SrsDvrSegmenter::write_metadata(SrsSharedPtrMessage* metadata)
|
||||||
return encode_metadata(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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -147,14 +147,14 @@ int SrsDvrSegmenter::write_audio(SrsSharedPtrMessage* shared_audio)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = encode_audio(audio)) != ERROR_SUCCESS) {
|
if ((ret = encode_audio(audio, format)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvrSegmenter::write_video(SrsSharedPtrMessage* shared_video)
|
int SrsDvrSegmenter::write_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ int SrsDvrSegmenter::write_video(SrsSharedPtrMessage* shared_video)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = encode_video(video)) != ERROR_SUCCESS) {
|
if ((ret = encode_video(video, format)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,7 +409,7 @@ int SrsDvrFlvSegmenter::encode_metadata(SrsSharedPtrMessage* metadata)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvrFlvSegmenter::encode_audio(SrsSharedPtrMessage* audio)
|
int SrsDvrFlvSegmenter::encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -422,15 +422,14 @@ int SrsDvrFlvSegmenter::encode_audio(SrsSharedPtrMessage* audio)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvrFlvSegmenter::encode_video(SrsSharedPtrMessage* video)
|
int SrsDvrFlvSegmenter::encode_video(SrsSharedPtrMessage* video, SrsFormat* format)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
char* payload = video->payload;
|
char* payload = video->payload;
|
||||||
int size = video->size;
|
int size = video->size;
|
||||||
bool sh = SrsFlvVideo::sh(payload, size);
|
bool sh = (format->video->avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader);
|
||||||
bool keyframe = SrsFlvVideo::h264(payload, size)
|
bool keyframe = (!sh && format->video->frame_type == SrsVideoAvcFrameTypeKeyFrame);
|
||||||
&& SrsFlvVideo::keyframe(payload, size) && !sh;
|
|
||||||
|
|
||||||
if (keyframe) {
|
if (keyframe) {
|
||||||
has_keyframe = true;
|
has_keyframe = true;
|
||||||
|
@ -460,13 +459,11 @@ int SrsDvrFlvSegmenter::close_encoder()
|
||||||
SrsDvrMp4Segmenter::SrsDvrMp4Segmenter()
|
SrsDvrMp4Segmenter::SrsDvrMp4Segmenter()
|
||||||
{
|
{
|
||||||
enc = new SrsMp4Encoder();
|
enc = new SrsMp4Encoder();
|
||||||
buffer = new SrsBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsDvrMp4Segmenter::~SrsDvrMp4Segmenter()
|
SrsDvrMp4Segmenter::~SrsDvrMp4Segmenter()
|
||||||
{
|
{
|
||||||
srs_freep(enc);
|
srs_freep(enc);
|
||||||
srs_freep(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvrMp4Segmenter::refresh_metadata()
|
int SrsDvrMp4Segmenter::refresh_metadata()
|
||||||
|
@ -493,37 +490,14 @@ int SrsDvrMp4Segmenter::encode_metadata(SrsSharedPtrMessage* /*metadata*/)
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio)
|
int SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
SrsAudioCodecId sound_format = format->acodec->id;
|
||||||
|
SrsAudioSampleRate sound_rate = format->acodec->sound_rate;
|
||||||
if ((ret = buffer->initialize(audio->payload, audio->size)) != ERROR_SUCCESS) {
|
SrsAudioSampleBits sound_size = format->acodec->sound_size;
|
||||||
return ret;
|
SrsAudioChannels channels = format->acodec->sound_type;
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
SrsAudioAacFrameTrait ct = format->audio->aac_packet_type;
|
||||||
if (ct == SrsAudioAacFrameTraitSequenceHeader) {
|
if (ct == SrsAudioAacFrameTraitSequenceHeader) {
|
||||||
enc->acodec = sound_format;
|
enc->acodec = sound_format;
|
||||||
enc->sample_rate = sound_rate;
|
enc->sample_rate = sound_rate;
|
||||||
|
@ -531,38 +505,20 @@ int SrsDvrMp4Segmenter::encode_audio(SrsSharedPtrMessage* audio)
|
||||||
enc->channels = channels;
|
enc->channels = channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* sample = (uint8_t*)(buffer->data() + buffer->pos());
|
uint8_t* sample = (uint8_t*)format->raw;
|
||||||
uint32_t nb_sample = (uint32_t)(buffer->size() - buffer->pos());
|
uint32_t nb_sample = (uint32_t)format->nb_raw;
|
||||||
|
|
||||||
uint32_t dts = (uint32_t)audio->timestamp;
|
uint32_t dts = (uint32_t)audio->timestamp;
|
||||||
return enc->write_sample(SrsMp4HandlerTypeSOUN, 0x00, ct, dts, dts, sample, nb_sample);
|
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;
|
SrsVideoAvcFrameType frame_type = format->video->frame_type;
|
||||||
|
SrsVideoCodecId codec_id = format->vcodec->id;
|
||||||
|
|
||||||
if ((ret = buffer->initialize(video->payload, video->size)) != ERROR_SUCCESS) {
|
SrsVideoAvcFrameTrait ct = format->video->avc_packet_type;
|
||||||
return ret;
|
uint32_t cts = (uint32_t)format->video->cts;
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
if (ct == SrsVideoAvcFrameTraitSequenceHeader) {
|
if (ct == SrsVideoAvcFrameTraitSequenceHeader) {
|
||||||
enc->vcodec = codec_id;
|
enc->vcodec = codec_id;
|
||||||
|
@ -571,8 +527,8 @@ int SrsDvrMp4Segmenter::encode_video(SrsSharedPtrMessage* video)
|
||||||
uint32_t dts = (uint32_t)video->timestamp;
|
uint32_t dts = (uint32_t)video->timestamp;
|
||||||
uint32_t pts = dts + cts;
|
uint32_t pts = dts + cts;
|
||||||
|
|
||||||
uint8_t* sample = (uint8_t*)(buffer->data() + buffer->pos());
|
uint8_t* sample = (uint8_t*)format->raw;
|
||||||
uint32_t nb_sample = (uint32_t)(buffer->size() - buffer->pos());
|
uint32_t nb_sample = (uint32_t)format->nb_raw;
|
||||||
return enc->write_sample(SrsMp4HandlerTypeVIDE, frame_type, ct, dts, pts, sample, nb_sample);
|
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);
|
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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -694,14 +650,14 @@ int SrsDvrPlan::on_audio(SrsSharedPtrMessage* shared_audio)
|
||||||
return ret;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvrPlan::on_video(SrsSharedPtrMessage* shared_video)
|
int SrsDvrPlan::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -709,7 +665,7 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* shared_video)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = segment->write_video(shared_video)) != ERROR_SUCCESS) {
|
if ((ret = segment->write_video(shared_video, format)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -865,14 +821,14 @@ int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* shared_audio)
|
||||||
return ret;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* shared_video)
|
int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -880,7 +836,7 @@ int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* shared_video)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = SrsDvrPlan::on_video(shared_video)) != ERROR_SUCCESS) {
|
if ((ret = SrsDvrPlan::on_video(shared_video, format)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1035,24 +991,24 @@ int SrsDvr::on_meta_data(SrsSharedPtrMessage* metadata)
|
||||||
return ret;
|
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.
|
// the dvr for this stream is not actived.
|
||||||
if (!actived) {
|
if (!actived) {
|
||||||
return ERROR_SUCCESS;
|
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.
|
// the dvr for this stream is not actived.
|
||||||
if (!actived) {
|
if (!actived) {
|
||||||
return ERROR_SUCCESS;
|
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)
|
int SrsDvr::on_reload_vhost_dvr_apply(string vhost)
|
||||||
|
|
|
@ -43,6 +43,7 @@ class SrsJsonObject;
|
||||||
class SrsThread;
|
class SrsThread;
|
||||||
class SrsMp4Encoder;
|
class SrsMp4Encoder;
|
||||||
class SrsFragment;
|
class SrsFragment;
|
||||||
|
class SrsFormat;
|
||||||
|
|
||||||
#include <srs_app_source.hpp>
|
#include <srs_app_source.hpp>
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
|
@ -82,10 +83,10 @@ public:
|
||||||
virtual int write_metadata(SrsSharedPtrMessage* metadata);
|
virtual int write_metadata(SrsSharedPtrMessage* metadata);
|
||||||
// Write audio packet.
|
// Write audio packet.
|
||||||
// @param shared_audio, directly ptr, copy it if need to save it.
|
// @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.
|
// Write video packet.
|
||||||
// @param shared_video, directly ptr, copy it if need to save it.
|
// @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,
|
// Refresh the metadata. For example, there is duration in flv metadata,
|
||||||
// when DVR in append mode, the duration must be update every some seconds.
|
// when DVR in append mode, the duration must be update every some seconds.
|
||||||
// @remark Maybe ignored by concreate segmenter.
|
// @remark Maybe ignored by concreate segmenter.
|
||||||
|
@ -96,8 +97,8 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual int open_encoder() = 0;
|
virtual int open_encoder() = 0;
|
||||||
virtual int encode_metadata(SrsSharedPtrMessage* metadata) = 0;
|
virtual int encode_metadata(SrsSharedPtrMessage* metadata) = 0;
|
||||||
virtual int encode_audio(SrsSharedPtrMessage* audio) = 0;
|
virtual int encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format) = 0;
|
||||||
virtual int encode_video(SrsSharedPtrMessage* video) = 0;
|
virtual int encode_video(SrsSharedPtrMessage* video, SrsFormat* format) = 0;
|
||||||
virtual int close_encoder() = 0;
|
virtual int close_encoder() = 0;
|
||||||
private:
|
private:
|
||||||
// Generate the flv segment path.
|
// Generate the flv segment path.
|
||||||
|
@ -134,8 +135,8 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual int open_encoder();
|
virtual int open_encoder();
|
||||||
virtual int encode_metadata(SrsSharedPtrMessage* metadata);
|
virtual int encode_metadata(SrsSharedPtrMessage* metadata);
|
||||||
virtual int encode_audio(SrsSharedPtrMessage* audio);
|
virtual int encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format);
|
||||||
virtual int encode_video(SrsSharedPtrMessage* video);
|
virtual int encode_video(SrsSharedPtrMessage* video, SrsFormat* format);
|
||||||
virtual int close_encoder();
|
virtual int close_encoder();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -147,8 +148,6 @@ class SrsDvrMp4Segmenter : public SrsDvrSegmenter
|
||||||
private:
|
private:
|
||||||
// The MP4 encoder, for MP4 target.
|
// The MP4 encoder, for MP4 target.
|
||||||
SrsMp4Encoder* enc;
|
SrsMp4Encoder* enc;
|
||||||
// The buffer to demux the packet to mp4 sample.
|
|
||||||
SrsBuffer* buffer;
|
|
||||||
public:
|
public:
|
||||||
SrsDvrMp4Segmenter();
|
SrsDvrMp4Segmenter();
|
||||||
virtual ~SrsDvrMp4Segmenter();
|
virtual ~SrsDvrMp4Segmenter();
|
||||||
|
@ -157,8 +156,8 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual int open_encoder();
|
virtual int open_encoder();
|
||||||
virtual int encode_metadata(SrsSharedPtrMessage* metadata);
|
virtual int encode_metadata(SrsSharedPtrMessage* metadata);
|
||||||
virtual int encode_audio(SrsSharedPtrMessage* audio);
|
virtual int encode_audio(SrsSharedPtrMessage* audio, SrsFormat* format);
|
||||||
virtual int encode_video(SrsSharedPtrMessage* video);
|
virtual int encode_video(SrsSharedPtrMessage* video, SrsFormat* format);
|
||||||
virtual int close_encoder();
|
virtual int close_encoder();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -199,8 +198,8 @@ public:
|
||||||
virtual int on_publish() = 0;
|
virtual int on_publish() = 0;
|
||||||
virtual void on_unpublish() = 0;
|
virtual void on_unpublish() = 0;
|
||||||
virtual int on_meta_data(SrsSharedPtrMessage* shared_metadata);
|
virtual int on_meta_data(SrsSharedPtrMessage* shared_metadata);
|
||||||
virtual int on_audio(SrsSharedPtrMessage* shared_audio);
|
virtual int on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
|
||||||
virtual int on_video(SrsSharedPtrMessage* shared_video);
|
virtual int on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format);
|
||||||
// Internal interface for segmenter.
|
// Internal interface for segmenter.
|
||||||
public:
|
public:
|
||||||
// When segmenter close a segment.
|
// When segmenter close a segment.
|
||||||
|
@ -238,8 +237,8 @@ public:
|
||||||
virtual int initialize(SrsOriginHub* h, SrsDvrSegmenter* s, SrsRequest* r);
|
virtual int initialize(SrsOriginHub* h, SrsDvrSegmenter* s, SrsRequest* r);
|
||||||
virtual int on_publish();
|
virtual int on_publish();
|
||||||
virtual void on_unpublish();
|
virtual void on_unpublish();
|
||||||
virtual int on_audio(SrsSharedPtrMessage* shared_audio);
|
virtual int on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
|
||||||
virtual int on_video(SrsSharedPtrMessage* shared_video);
|
virtual int on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format);
|
||||||
private:
|
private:
|
||||||
virtual int update_duration(SrsSharedPtrMessage* msg);
|
virtual int update_duration(SrsSharedPtrMessage* msg);
|
||||||
// interface ISrsReloadHandler
|
// interface ISrsReloadHandler
|
||||||
|
@ -290,12 +289,12 @@ public:
|
||||||
* mux the audio packets to dvr.
|
* mux the audio packets to dvr.
|
||||||
* @param shared_audio, directly ptr, copy it if need to save it.
|
* @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.
|
* mux the video packets to dvr.
|
||||||
* @param shared_video, directly ptr, copy it if need to save it.
|
* @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
|
// interface ISrsReloadHandler
|
||||||
public:
|
public:
|
||||||
virtual int on_reload_vhost_dvr_apply(std::string vhost);
|
virtual int on_reload_vhost_dvr_apply(std::string vhost);
|
||||||
|
|
|
@ -1024,7 +1024,7 @@ int SrsOriginHub::on_audio(SrsSharedPtrMessage* shared_audio)
|
||||||
ret = ERROR_SUCCESS;
|
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);
|
srs_warn("dvr process audio message failed, ignore and disable dvr. ret=%d", ret);
|
||||||
|
|
||||||
// unpublish, ignore ret.
|
// unpublish, ignore ret.
|
||||||
|
@ -1126,7 +1126,7 @@ int SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_sequence_h
|
||||||
ret = ERROR_SUCCESS;
|
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);
|
srs_warn("dvr process video message failed, ignore and disable dvr. ret=%d", ret);
|
||||||
|
|
||||||
// unpublish, ignore ret.
|
// unpublish, ignore ret.
|
||||||
|
@ -1278,13 +1278,28 @@ int SrsOriginHub::on_dvr_request_sh()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cache_sh_video && (ret = dvr->on_video(cache_sh_video)) != ERROR_SUCCESS) {
|
if (cache_sh_video) {
|
||||||
srs_error("dvr process video sequence header message failed. ret=%d", ret);
|
// TODO: Use cached format for sh.
|
||||||
return ret;
|
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);
|
if (cache_sh_audio) {
|
||||||
return ret;
|
// 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;
|
return ret;
|
||||||
|
|
|
@ -511,6 +511,8 @@ SrsFormat::SrsFormat()
|
||||||
video = NULL;
|
video = NULL;
|
||||||
buffer = new SrsBuffer();
|
buffer = new SrsBuffer();
|
||||||
avc_parse_sps = true;
|
avc_parse_sps = true;
|
||||||
|
raw = NULL;
|
||||||
|
nb_raw = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsFormat::~SrsFormat()
|
SrsFormat::~SrsFormat()
|
||||||
|
@ -689,6 +691,10 @@ int SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
||||||
video->cts = composition_time;
|
video->cts = composition_time;
|
||||||
video->avc_packet_type = (SrsVideoAvcFrameTrait)avc_packet_type;
|
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 (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
|
||||||
if ((ret = avc_demux_sps_pps(stream)) != ERROR_SUCCESS) {
|
if ((ret = avc_demux_sps_pps(stream)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -819,7 +825,9 @@ int SrsFormat::avc_demux_sps()
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsBuffer stream;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1280,6 +1288,10 @@ int SrsFormat::audio_aac_demux(SrsBuffer* stream, int64_t timestamp)
|
||||||
SrsAudioAacFrameTrait aac_packet_type = (SrsAudioAacFrameTrait)stream->read_1bytes();
|
SrsAudioAacFrameTrait aac_packet_type = (SrsAudioAacFrameTrait)stream->read_1bytes();
|
||||||
audio->aac_packet_type = (SrsAudioAacFrameTrait)aac_packet_type;
|
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) {
|
if (aac_packet_type == SrsAudioAacFrameTraitSequenceHeader) {
|
||||||
// AudioSpecificConfig
|
// AudioSpecificConfig
|
||||||
// 1.6.2.1 AudioSpecificConfig, in ISO_IEC_14496-3-AAC-2001.pdf, page 33.
|
// 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.
|
// we always decode aac then mp3.
|
||||||
srs_assert(acodec->id == SrsAudioCodecIdMP3);
|
srs_assert(acodec->id == SrsAudioCodecIdMP3);
|
||||||
|
|
||||||
|
// Update the RAW MP3 data.
|
||||||
|
raw = stream->data() + stream->pos();
|
||||||
|
nb_raw = stream->size() - stream->pos();
|
||||||
|
|
||||||
stream->skip(1);
|
stream->skip(1);
|
||||||
if (stream->empty()) {
|
if (stream->empty()) {
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -235,6 +235,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* check codec h264, keyframe, sequence header
|
* check codec h264, keyframe, sequence header
|
||||||
*/
|
*/
|
||||||
|
// TODO: FIXME: Remove it, use SrsFormat instead.
|
||||||
static bool sh(char* data, int size);
|
static bool sh(char* data, int size);
|
||||||
/**
|
/**
|
||||||
* check codec h264.
|
* check codec h264.
|
||||||
|
@ -678,6 +679,10 @@ public:
|
||||||
SrsAudioCodecConfig* acodec;
|
SrsAudioCodecConfig* acodec;
|
||||||
SrsVideoFrame* video;
|
SrsVideoFrame* video;
|
||||||
SrsVideoCodecConfig* vcodec;
|
SrsVideoCodecConfig* vcodec;
|
||||||
|
public:
|
||||||
|
char* raw;
|
||||||
|
int nb_raw;
|
||||||
|
private:
|
||||||
SrsBuffer* buffer;
|
SrsBuffer* buffer;
|
||||||
public:
|
public:
|
||||||
// for sequence header, whether parse the h.264 sps.
|
// for sequence header, whether parse the h.264 sps.
|
||||||
|
|
|
@ -265,6 +265,7 @@
|
||||||
#define ERROR_MP4_ASC_CHANGE 3086
|
#define ERROR_MP4_ASC_CHANGE 3086
|
||||||
#define ERROR_DASH_WRITE_FAILED 3087
|
#define ERROR_DASH_WRITE_FAILED 3087
|
||||||
#define ERROR_TS_CONTEXT_NOT_READY 3088
|
#define ERROR_TS_CONTEXT_NOT_READY 3088
|
||||||
|
#define ERROR_MP4_ILLEGAL_MOOF 3089
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// HTTP/StreamCaster/KAFKA protocol error.
|
// HTTP/StreamCaster/KAFKA protocol error.
|
||||||
|
|
|
@ -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)
|
void SrsMp4FileTypeBox::set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1, SrsMp4BoxBrand b2, SrsMp4BoxBrand b3)
|
||||||
{
|
{
|
||||||
compatible_brands.resize(4);
|
compatible_brands.resize(4);
|
||||||
|
@ -835,6 +842,30 @@ SrsMp4MovieFragmentBox::~SrsMp4MovieFragmentBox()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsMp4MovieFragmentHeaderBox* SrsMp4MovieFragmentBox::mfhd()
|
||||||
|
{
|
||||||
|
SrsMp4Box* box = get(SrsMp4BoxTypeMFHD);
|
||||||
|
return dynamic_cast<SrsMp4MovieFragmentHeaderBox*>(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsMp4MovieFragmentBox::set_mfhd(SrsMp4MovieFragmentHeaderBox* v)
|
||||||
|
{
|
||||||
|
remove(SrsMp4BoxTypeMFHD);
|
||||||
|
boxes.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsMp4TrackFragmentBox* SrsMp4MovieFragmentBox::traf()
|
||||||
|
{
|
||||||
|
SrsMp4Box* box = get(SrsMp4BoxTypeTRAF);
|
||||||
|
return dynamic_cast<SrsMp4TrackFragmentBox*>(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsMp4MovieFragmentBox::set_traf(SrsMp4TrackFragmentBox* v)
|
||||||
|
{
|
||||||
|
remove(SrsMp4BoxTypeTRAF);
|
||||||
|
boxes.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
SrsMp4MovieFragmentHeaderBox::SrsMp4MovieFragmentHeaderBox()
|
SrsMp4MovieFragmentHeaderBox::SrsMp4MovieFragmentHeaderBox()
|
||||||
{
|
{
|
||||||
type = SrsMp4BoxTypeMFHD;
|
type = SrsMp4BoxTypeMFHD;
|
||||||
|
@ -894,6 +925,42 @@ SrsMp4TrackFragmentBox::~SrsMp4TrackFragmentBox()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsMp4TrackFragmentHeaderBox* SrsMp4TrackFragmentBox::tfhd()
|
||||||
|
{
|
||||||
|
SrsMp4Box* box = get(SrsMp4BoxTypeTFHD);
|
||||||
|
return dynamic_cast<SrsMp4TrackFragmentHeaderBox*>(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsMp4TrackFragmentBox::set_tfhd(SrsMp4TrackFragmentHeaderBox* v)
|
||||||
|
{
|
||||||
|
remove(SrsMp4BoxTypeTFHD);
|
||||||
|
boxes.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsMp4TrackFragmentDecodeTimeBox* SrsMp4TrackFragmentBox::tfdt()
|
||||||
|
{
|
||||||
|
SrsMp4Box* box = get(SrsMp4BoxTypeTFDT);
|
||||||
|
return dynamic_cast<SrsMp4TrackFragmentDecodeTimeBox*>(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsMp4TrackFragmentBox::set_tfdt(SrsMp4TrackFragmentDecodeTimeBox* v)
|
||||||
|
{
|
||||||
|
remove(SrsMp4BoxTypeTFDT);
|
||||||
|
boxes.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsMp4TrackFragmentRunBox* SrsMp4TrackFragmentBox::trun()
|
||||||
|
{
|
||||||
|
SrsMp4Box* box = get(SrsMp4BoxTypeTRUN);
|
||||||
|
return dynamic_cast<SrsMp4TrackFragmentRunBox*>(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsMp4TrackFragmentBox::set_trun(SrsMp4TrackFragmentRunBox* v)
|
||||||
|
{
|
||||||
|
remove(SrsMp4BoxTypeTRUN);
|
||||||
|
boxes.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
SrsMp4TrackFragmentHeaderBox::SrsMp4TrackFragmentHeaderBox()
|
SrsMp4TrackFragmentHeaderBox::SrsMp4TrackFragmentHeaderBox()
|
||||||
{
|
{
|
||||||
type = SrsMp4BoxTypeTFHD;
|
type = SrsMp4BoxTypeTFHD;
|
||||||
|
@ -1012,6 +1079,13 @@ stringstream& SrsMp4TrackFragmentHeaderBox::dumps_detail(stringstream& ss, SrsMp
|
||||||
ss << ", dsf=" << default_sample_flags;
|
ss << ", dsf=" << default_sample_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((flags&SrsMp4TfhdFlagsDurationIsEmpty) == SrsMp4TfhdFlagsDurationIsEmpty) {
|
||||||
|
ss << ", empty-duration";
|
||||||
|
}
|
||||||
|
if ((flags&SrsMp4TfhdFlagsDefaultBaseIsMoof) == SrsMp4TfhdFlagsDefaultBaseIsMoof) {
|
||||||
|
ss << ", moof-base";
|
||||||
|
}
|
||||||
|
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1073,10 +1147,9 @@ stringstream& SrsMp4TrackFragmentDecodeTimeBox::dumps_detail(stringstream& ss, S
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsMp4TrunEntry::SrsMp4TrunEntry(uint8_t v, uint32_t f)
|
SrsMp4TrunEntry::SrsMp4TrunEntry(SrsMp4FullBox* o)
|
||||||
{
|
{
|
||||||
flags = f;
|
owner = o;
|
||||||
version = v;
|
|
||||||
sample_duration = sample_size = sample_flags = 0;
|
sample_duration = sample_size = sample_flags = 0;
|
||||||
sample_composition_time_offset = 0;
|
sample_composition_time_offset = 0;
|
||||||
}
|
}
|
||||||
|
@ -1089,16 +1162,16 @@ int SrsMp4TrunEntry::nb_header()
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) {
|
||||||
size += 4;
|
size += 4;
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) {
|
||||||
size += 4;
|
size += 4;
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) {
|
||||||
size += 4;
|
size += 4;
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) {
|
||||||
size += 4;
|
size += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1109,17 +1182,17 @@ int SrsMp4TrunEntry::encode_header(SrsBuffer* buf)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) {
|
||||||
buf->write_4bytes(sample_duration);
|
buf->write_4bytes(sample_duration);
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) {
|
||||||
buf->write_4bytes(sample_size);
|
buf->write_4bytes(sample_size);
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) {
|
||||||
buf->write_4bytes(sample_flags);
|
buf->write_4bytes(sample_flags);
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) {
|
||||||
if (!version) {
|
if (!owner->version) {
|
||||||
uint32_t v = (uint32_t)sample_composition_time_offset;
|
uint32_t v = (uint32_t)sample_composition_time_offset;
|
||||||
buf->write_4bytes(v);
|
buf->write_4bytes(v);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1135,17 +1208,17 @@ int SrsMp4TrunEntry::decode_header(SrsBuffer* buf)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) {
|
||||||
sample_duration = buf->read_4bytes();
|
sample_duration = buf->read_4bytes();
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) {
|
||||||
sample_size = buf->read_4bytes();
|
sample_size = buf->read_4bytes();
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) {
|
||||||
sample_flags = buf->read_4bytes();
|
sample_flags = buf->read_4bytes();
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) {
|
||||||
if (!version) {
|
if (!owner->version) {
|
||||||
uint32_t v = buf->read_4bytes();
|
uint32_t v = buf->read_4bytes();
|
||||||
sample_composition_time_offset = v;
|
sample_composition_time_offset = v;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1159,17 +1232,17 @@ int SrsMp4TrunEntry::decode_header(SrsBuffer* buf)
|
||||||
|
|
||||||
stringstream& SrsMp4TrunEntry::dumps_detail(stringstream& ss, SrsMp4DumpContext dc)
|
stringstream& SrsMp4TrunEntry::dumps_detail(stringstream& ss, SrsMp4DumpContext dc)
|
||||||
{
|
{
|
||||||
if ((flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleDuration) == SrsMp4TrunFlagsSampleDuration) {
|
||||||
ss << "duration=" << sample_duration;
|
ss << "duration=" << sample_duration;
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleSize) == SrsMp4TrunFlagsSampleSize) {
|
||||||
ss << ", size=" << sample_size;
|
ss << ", size=" << sample_size;
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleFlag) == SrsMp4TrunFlagsSampleFlag) {
|
||||||
ss << ", flags=" << sample_flags;
|
ss << ", flags=" << sample_flags;
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) {
|
if ((owner->flags&SrsMp4TrunFlagsSampleCtsOffset) == SrsMp4TrunFlagsSampleCtsOffset) {
|
||||||
ss << ", cts-offset=" << sample_composition_time_offset;
|
ss << ", cts=" << sample_composition_time_offset;
|
||||||
}
|
}
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
@ -1256,7 +1329,7 @@ int SrsMp4TrackFragmentRunBox::decode_header(SrsBuffer* buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < sample_count; i++) {
|
for (int i = 0; i < sample_count; i++) {
|
||||||
SrsMp4TrunEntry* entry = new SrsMp4TrunEntry(version, flags);
|
SrsMp4TrunEntry* entry = new SrsMp4TrunEntry(this);
|
||||||
entries.push_back(entry);
|
entries.push_back(entry);
|
||||||
|
|
||||||
if ((ret = entry->decode_header(buf)) != ERROR_SUCCESS) {
|
if ((ret = entry->decode_header(buf)) != ERROR_SUCCESS) {
|
||||||
|
@ -1274,10 +1347,10 @@ stringstream& SrsMp4TrackFragmentRunBox::dumps_detail(stringstream& ss, SrsMp4Du
|
||||||
ss << ", samples=" << sample_count;
|
ss << ", samples=" << sample_count;
|
||||||
|
|
||||||
if ((flags&SrsMp4TrunFlagsDataOffset) == SrsMp4TrunFlagsDataOffset) {
|
if ((flags&SrsMp4TrunFlagsDataOffset) == SrsMp4TrunFlagsDataOffset) {
|
||||||
ss << ", do" << data_offset;
|
ss << ", data-offset=" << data_offset;
|
||||||
}
|
}
|
||||||
if ((flags&SrsMp4TrunFlagsFirstSample) == SrsMp4TrunFlagsFirstSample) {
|
if ((flags&SrsMp4TrunFlagsFirstSample) == SrsMp4TrunFlagsFirstSample) {
|
||||||
ss << ", fsf=" << first_sample_flags;
|
ss << ", first-sample=" << first_sample_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample_count > 0) {
|
if (sample_count > 0) {
|
||||||
|
@ -1325,8 +1398,6 @@ int SrsMp4MediaDataBox::decode(SrsBuffer* buf)
|
||||||
|
|
||||||
nb_data = (int)(sz() - nb_header());
|
nb_data = (int)(sz() - nb_header());
|
||||||
|
|
||||||
// Because the
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4721,6 +4792,49 @@ int SrsMp4SampleManager::write(SrsMp4MovieBox* moov)
|
||||||
return ret;
|
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<SrsMp4Sample*>::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,
|
int SrsMp4SampleManager::write_track(SrsFrameType track,
|
||||||
SrsMp4DecodingTime2SampleBox* stts, SrsMp4SyncSampleBox* stss, SrsMp4CompositionTime2SampleBox* ctts,
|
SrsMp4DecodingTime2SampleBox* stts, SrsMp4SyncSampleBox* stss, SrsMp4CompositionTime2SampleBox* ctts,
|
||||||
SrsMp4Sample2ChunkBox* stsc, SrsMp4SampleSizeBox* stsz, SrsMp4ChunkOffsetBox* stco)
|
SrsMp4Sample2ChunkBox* stsc, SrsMp4SampleSizeBox* stsz, SrsMp4ChunkOffsetBox* stco)
|
||||||
|
@ -5546,7 +5660,7 @@ int SrsMp4Encoder::flush()
|
||||||
|
|
||||||
if (!nb_audios && !nb_videos) {
|
if (!nb_audios && !nb_videos) {
|
||||||
ret = ERROR_MP4_ILLEGAL_MOOV;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5821,3 +5935,438 @@ int SrsMp4Encoder::do_write_sample(SrsMp4Sample* ps, uint8_t* sample, uint32_t n
|
||||||
return ret;
|
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<char> 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<SrsMp4Sample*>::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
class ISrsWriter;
|
||||||
class ISrsWriteSeeker;
|
class ISrsWriteSeeker;
|
||||||
class ISrsReadSeeker;
|
class ISrsReadSeeker;
|
||||||
class SrsMp4TrackBox;
|
class SrsMp4TrackBox;
|
||||||
|
@ -64,6 +65,11 @@ class SrsMp4DataReferenceBox;
|
||||||
class SrsMp4SoundMeidaHeaderBox;
|
class SrsMp4SoundMeidaHeaderBox;
|
||||||
class SrsMp4MovieExtendsBox;
|
class SrsMp4MovieExtendsBox;
|
||||||
class SrsMp4TrackExtendsBox;
|
class SrsMp4TrackExtendsBox;
|
||||||
|
class SrsMp4MovieFragmentHeaderBox;
|
||||||
|
class SrsMp4TrackFragmentBox;
|
||||||
|
class SrsMp4TrackFragmentHeaderBox;
|
||||||
|
class SrsMp4TrackFragmentDecodeTimeBox;
|
||||||
|
class SrsMp4TrackFragmentRunBox;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 4.2 Object Structure
|
* 4.2 Object Structure
|
||||||
|
@ -147,6 +153,7 @@ enum SrsMp4BoxBrand
|
||||||
SrsMp4BoxBrandISO5 = 0x69736f35, // 'iso5'
|
SrsMp4BoxBrandISO5 = 0x69736f35, // 'iso5'
|
||||||
SrsMp4BoxBrandMP42 = 0x6d703432, // 'mp42'
|
SrsMp4BoxBrandMP42 = 0x6d703432, // 'mp42'
|
||||||
SrsMp4BoxBrandDASH = 0x64617368, // 'dash'
|
SrsMp4BoxBrandDASH = 0x64617368, // 'dash'
|
||||||
|
SrsMp4BoxBrandMSDH = 0x6d736468, // 'msdh'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,6 +294,7 @@ public:
|
||||||
SrsMp4FileTypeBox();
|
SrsMp4FileTypeBox();
|
||||||
virtual ~SrsMp4FileTypeBox();
|
virtual ~SrsMp4FileTypeBox();
|
||||||
public:
|
public:
|
||||||
|
virtual void set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1);
|
||||||
virtual void set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1, SrsMp4BoxBrand b2, SrsMp4BoxBrand b3);
|
virtual void set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1, SrsMp4BoxBrand b2, SrsMp4BoxBrand b3);
|
||||||
protected:
|
protected:
|
||||||
virtual int nb_header();
|
virtual int nb_header();
|
||||||
|
@ -323,6 +331,13 @@ class SrsMp4MovieFragmentBox : public SrsMp4Box
|
||||||
public:
|
public:
|
||||||
SrsMp4MovieFragmentBox();
|
SrsMp4MovieFragmentBox();
|
||||||
virtual ~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:
|
public:
|
||||||
SrsMp4TrackFragmentBox();
|
SrsMp4TrackFragmentBox();
|
||||||
virtual ~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:
|
public:
|
||||||
uint32_t track_id;
|
uint32_t track_id;
|
||||||
// all the following are optional fields
|
// all the following are optional fields
|
||||||
public:
|
public:
|
||||||
// the base offset to use when calculating data offsets
|
// the base offset to use when calculating data offsets
|
||||||
uint64_t base_data_offset;
|
uint64_t base_data_offset;
|
||||||
|
@ -485,8 +510,7 @@ enum SrsMp4TrunFlags
|
||||||
*/
|
*/
|
||||||
struct SrsMp4TrunEntry
|
struct SrsMp4TrunEntry
|
||||||
{
|
{
|
||||||
uint8_t version;
|
SrsMp4FullBox* owner;
|
||||||
uint32_t flags;
|
|
||||||
|
|
||||||
uint32_t sample_duration;
|
uint32_t sample_duration;
|
||||||
uint32_t sample_size;
|
uint32_t sample_size;
|
||||||
|
@ -494,7 +518,7 @@ struct SrsMp4TrunEntry
|
||||||
// if version == 0, unsigned int(32); otherwise, signed int(32).
|
// if version == 0, unsigned int(32); otherwise, signed int(32).
|
||||||
int64_t sample_composition_time_offset;
|
int64_t sample_composition_time_offset;
|
||||||
|
|
||||||
SrsMp4TrunEntry(uint8_t v, uint32_t f);
|
SrsMp4TrunEntry(SrsMp4FullBox* o);
|
||||||
virtual ~SrsMp4TrunEntry();
|
virtual ~SrsMp4TrunEntry();
|
||||||
|
|
||||||
virtual int nb_header();
|
virtual int nb_header();
|
||||||
|
@ -1941,7 +1965,7 @@ public:
|
||||||
*/
|
*/
|
||||||
class SrsMp4SampleManager
|
class SrsMp4SampleManager
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
std::vector<SrsMp4Sample*> samples;
|
std::vector<SrsMp4Sample*> samples;
|
||||||
public:
|
public:
|
||||||
SrsMp4SampleManager();
|
SrsMp4SampleManager();
|
||||||
|
@ -1956,6 +1980,9 @@ public:
|
||||||
virtual void append(SrsMp4Sample* sample);
|
virtual void append(SrsMp4Sample* sample);
|
||||||
// Write the samples info to moov.
|
// Write the samples info to moov.
|
||||||
virtual int write(SrsMp4MovieBox* 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:
|
private:
|
||||||
virtual int write_track(SrsFrameType track,
|
virtual int write_track(SrsFrameType track,
|
||||||
SrsMp4DecodingTime2SampleBox* stts, SrsMp4SyncSampleBox* stss, SrsMp4CompositionTime2SampleBox* ctts,
|
SrsMp4DecodingTime2SampleBox* stts, SrsMp4SyncSampleBox* stss, SrsMp4CompositionTime2SampleBox* ctts,
|
||||||
|
@ -2122,10 +2149,10 @@ public:
|
||||||
SrsMp4Encoder();
|
SrsMp4Encoder();
|
||||||
virtual ~SrsMp4Encoder();
|
virtual ~SrsMp4Encoder();
|
||||||
public:
|
public:
|
||||||
// Initialize the encoder with a writer w.
|
// Initialize the encoder with a writer and seeker ws.
|
||||||
// @param w The underlayer io writer, user must manage it.
|
// @param ws The underlayer io writer and seeker, user must manage it.
|
||||||
virtual int initialize(ISrsWriteSeeker* ws);
|
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 ht, The sample handler type, audio/soun or video/vide.
|
||||||
// @param ft, The frame type. For video, it's SrsVideoAvcFrameType.
|
// @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.
|
// @param ct, The codec type. For video, it's SrsVideoAvcFrameTrait. For audio, it's SrsAudioAacFrameTrait.
|
||||||
|
@ -2142,5 +2169,61 @@ private:
|
||||||
virtual int do_write_sample(SrsMp4Sample* ps, uint8_t* sample, uint32_t nb_sample);
|
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
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue