For #299, A/V init mp4 are fine.

pull/800/head
winlin 8 years ago
parent 886930c22a
commit 3ec9df6578

@ -31,6 +31,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_kernel_utility.hpp>
#include <srs_app_utility.hpp>
#include <srs_kernel_file.hpp>
#include <srs_core_autofree.hpp>
#include <srs_kernel_mp4.hpp>
#include <sstream>
using namespace std;
@ -119,15 +121,17 @@ int SrsMpdWriter::write(SrsFormat* format)
ss << " </Period>" << endl
<< "</MPD>" << endl;
SrsFileWriter fw;
SrsFileWriter* fw = new SrsFileWriter();
SrsAutoFree(SrsFileWriter, fw);
string full_path_tmp = full_path + ".tmp";
if ((ret = fw.open(full_path_tmp)) != ERROR_SUCCESS) {
if ((ret = fw->open(full_path_tmp)) != ERROR_SUCCESS) {
srs_error("DASH: Open MPD file=%s failed, ret=%d", full_path_tmp.c_str(), ret);
return ret;
}
string content = ss.str();
if ((ret = fw.write((void*)content.data(), content.length(), NULL)) != ERROR_SUCCESS) {
if ((ret = fw->write((void*)content.data(), content.length(), NULL)) != ERROR_SUCCESS) {
srs_error("DASH: Write MPD file=%s failed, ret=%d", full_path.c_str(), ret);
return ret;
}
@ -138,13 +142,16 @@ int SrsMpdWriter::write(SrsFormat* format)
return ret;
}
srs_trace("DASH: Refresh MPD successed, size=%dB, file=%s", content.length(), full_path.c_str());
srs_trace("DASH: Refresh MPD success, size=%dB, file=%s", content.length(), full_path.c_str());
return ret;
}
SrsDashController::SrsDashController()
{
req = NULL;
video_tack_id = 2;
audio_track_id = 1;
mpd = new SrsMpdWriter();
}
@ -157,6 +164,9 @@ int SrsDashController::initialize(SrsRequest* r)
{
int ret = ERROR_SUCCESS;
req = r;
home = _srs_config->get_dash_path(r->vhost);
if ((ret = mpd->initialize(r)) != ERROR_SUCCESS) {
return ret;
}
@ -168,6 +178,10 @@ int SrsDashController::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* fo
{
int ret = ERROR_SUCCESS;
if (format->is_aac_sequence_header()) {
return refresh_init_mp4(shared_audio, format);
}
if ((ret = refresh_mpd(format)) != ERROR_SUCCESS) {
srs_error("DASH: Refresh the MPD failed. ret=%d", ret);
return ret;
@ -180,6 +194,10 @@ int SrsDashController::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* fo
{
int ret = ERROR_SUCCESS;
if (format->is_avc_sequence_header()) {
return refresh_init_mp4(shared_video, format);
}
if ((ret = refresh_mpd(format)) != ERROR_SUCCESS) {
srs_error("DASH: Refresh the MPD failed. ret=%d", ret);
return ret;
@ -204,6 +222,278 @@ int SrsDashController::refresh_mpd(SrsFormat* format)
return ret;
}
int SrsDashController::refresh_init_mp4(SrsSharedPtrMessage* msg, SrsFormat* format)
{
int ret = ERROR_SUCCESS;
if (msg->size <= 0 || (msg->is_video() && !format->vcodec->avc_extra_size)
|| (msg->is_audio() && !format->acodec->aac_extra_size)) {
srs_warn("DASH: Ignore empty sequence header.");
return ret;
}
string full_home = home + "/" + req->app + "/" + req->stream;
if ((ret = srs_create_dir_recursively(full_home)) != ERROR_SUCCESS) {
srs_error("DASH: Create media home failed, home=%s, ret=%d", full_home.c_str(), ret);
return ret;
}
std::string path = full_home;
if (msg->is_video()) {
path += "/video-init.mp4";
} else {
path += "/audio-init.mp4";
}
SrsFileWriter* fw = new SrsFileWriter();
SrsAutoFree(SrsFileWriter, fw);
string path_tmp = path + ".tmp";
if ((ret = fw->open(path_tmp)) != ERROR_SUCCESS) {
srs_error("DASH: Open media failed, path=%s, ret=%d", path_tmp.c_str(), ret);
return ret;
}
// Write ftyp box.
SrsMp4FileTypeBox* ftyp = new SrsMp4FileTypeBox();
SrsAutoFree(SrsMp4FileTypeBox, ftyp);
if (true) {
ftyp->major_brand = SrsMp4BoxBrandISO5;
ftyp->minor_version = 0;
ftyp->set_compatible_brands(SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO5, SrsMp4BoxBrandDASH, SrsMp4BoxBrandMP42);
}
// Write moov.
SrsMp4MovieBox* moov = new SrsMp4MovieBox();
SrsAutoFree(SrsMp4MovieBox, moov);
if (true) {
SrsMp4MovieHeaderBox* mvhd = new SrsMp4MovieHeaderBox();
moov->set_mvhd(mvhd);
mvhd->timescale = 1000; // Use tbn ms.
mvhd->duration_in_tbn = 0;
mvhd->next_track_ID = (msg->is_video()? video_tack_id : audio_track_id);
if (msg->is_video()) {
SrsMp4TrackBox* trak = new SrsMp4TrackBox();
moov->add_trak(trak);
SrsMp4TrackHeaderBox* tkhd = new SrsMp4TrackHeaderBox();
trak->set_tkhd(tkhd);
tkhd->track_ID = mvhd->next_track_ID++;
tkhd->duration = 0;
tkhd->width = (format->vcodec->width << 16);
tkhd->height = (format->vcodec->height << 16);
SrsMp4MediaBox* mdia = new SrsMp4MediaBox();
trak->set_mdia(mdia);
SrsMp4MediaHeaderBox* mdhd = new SrsMp4MediaHeaderBox();
mdia->set_mdhd(mdhd);
mdhd->timescale = 1000;
mdhd->duration = 0;
mdhd->set_language0('u');
mdhd->set_language1('n');
mdhd->set_language2('d');
SrsMp4HandlerReferenceBox* hdlr = new SrsMp4HandlerReferenceBox();
mdia->set_hdlr(hdlr);
hdlr->handler_type = SrsMp4HandlerTypeVIDE;
hdlr->name = "VideoHandler";
SrsMp4MediaInformationBox* minf = new SrsMp4MediaInformationBox();
mdia->set_minf(minf);
SrsMp4VideoMeidaHeaderBox* vmhd = new SrsMp4VideoMeidaHeaderBox();
minf->set_vmhd(vmhd);
SrsMp4DataInformationBox* dinf = new SrsMp4DataInformationBox();
minf->set_dinf(dinf);
SrsMp4DataReferenceBox* dref = new SrsMp4DataReferenceBox();
dinf->set_dref(dref);
SrsMp4DataEntryBox* url = new SrsMp4DataEntryUrlBox();
dref->append(url);
SrsMp4SampleTableBox* stbl = new SrsMp4SampleTableBox();
minf->set_stbl(stbl);
SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox();
stbl->set_stsd(stsd);
SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry();
stsd->append(avc1);
avc1->width = format->vcodec->width;
avc1->height = format->vcodec->height;
SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
avc1->set_avcC(avcC);
avcC->nb_config = format->vcodec->avc_extra_size;
avcC->avc_config = new uint8_t[format->vcodec->avc_extra_size];
memcpy(avcC->avc_config, format->vcodec->avc_extra_data, format->vcodec->avc_extra_size);
SrsMp4DecodingTime2SampleBox* stts = new SrsMp4DecodingTime2SampleBox();
stbl->set_stts(stts);
SrsMp4Sample2ChunkBox* stsc = new SrsMp4Sample2ChunkBox();
stbl->set_stsc(stsc);
SrsMp4SampleSizeBox* stsz = new SrsMp4SampleSizeBox();
stbl->set_stsz(stsz);
SrsMp4ChunkOffsetBox* stco = new SrsMp4ChunkOffsetBox();
stbl->set_stco(stco);
SrsMp4MovieExtendsBox* mvex = new SrsMp4MovieExtendsBox();
moov->set_mvex(mvex);
SrsMp4TrackExtendsBox* trex = new SrsMp4TrackExtendsBox();
mvex->set_trex(trex);
trex->track_ID = video_tack_id;
trex->default_sample_description_index = 1;
} else {
SrsMp4TrackBox* trak = new SrsMp4TrackBox();
moov->add_trak(trak);
SrsMp4TrackHeaderBox* tkhd = new SrsMp4TrackHeaderBox();
tkhd->volume = 0x0100;
trak->set_tkhd(tkhd);
tkhd->track_ID = mvhd->next_track_ID++;
tkhd->duration = 0;
SrsMp4MediaBox* mdia = new SrsMp4MediaBox();
trak->set_mdia(mdia);
SrsMp4MediaHeaderBox* mdhd = new SrsMp4MediaHeaderBox();
mdia->set_mdhd(mdhd);
mdhd->timescale = 1000;
mdhd->duration = 0;
mdhd->set_language0('u');
mdhd->set_language1('n');
mdhd->set_language2('d');
SrsMp4HandlerReferenceBox* hdlr = new SrsMp4HandlerReferenceBox();
mdia->set_hdlr(hdlr);
hdlr->handler_type = SrsMp4HandlerTypeSOUN;
hdlr->name = "SoundHandler";
SrsMp4MediaInformationBox* minf = new SrsMp4MediaInformationBox();
mdia->set_minf(minf);
SrsMp4SoundMeidaHeaderBox* smhd = new SrsMp4SoundMeidaHeaderBox();
minf->set_smhd(smhd);
SrsMp4DataInformationBox* dinf = new SrsMp4DataInformationBox();
minf->set_dinf(dinf);
SrsMp4DataReferenceBox* dref = new SrsMp4DataReferenceBox();
dinf->set_dref(dref);
SrsMp4DataEntryBox* url = new SrsMp4DataEntryUrlBox();
dref->append(url);
SrsMp4SampleTableBox* stbl = new SrsMp4SampleTableBox();
minf->set_stbl(stbl);
SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox();
stbl->set_stsd(stsd);
SrsMp4AudioSampleEntry* mp4a = new SrsMp4AudioSampleEntry();
mp4a->samplerate = uint32_t(srs_flv_srates[format->acodec->sound_rate]) << 16;
if (format->acodec->sound_size == SrsAudioSampleBits16bit) {
mp4a->samplesize = 16;
} else {
mp4a->samplesize = 8;
}
if (format->acodec->sound_type == SrsAudioChannelsStereo) {
mp4a->channelcount = 2;
} else {
mp4a->channelcount = 1;
}
stsd->append(mp4a);
SrsMp4EsdsBox* esds = new SrsMp4EsdsBox();
mp4a->set_esds(esds);
SrsMp4ES_Descriptor* es = esds->es;
es->ES_ID = 0x02;
SrsMp4DecoderConfigDescriptor& desc = es->decConfigDescr;
desc.objectTypeIndication = SrsMp4ObjectTypeAac;
desc.streamType = SrsMp4StreamTypeAudioStream;
srs_freep(desc.decSpecificInfo);
SrsMp4DecoderSpecificInfo* asc = new SrsMp4DecoderSpecificInfo();
desc.decSpecificInfo = asc;
asc->nb_asc = format->acodec->aac_extra_size;
asc->asc = new uint8_t[format->acodec->aac_extra_size];
memcpy(asc->asc, format->acodec->aac_extra_data, format->acodec->aac_extra_size);
SrsMp4DecodingTime2SampleBox* stts = new SrsMp4DecodingTime2SampleBox();
stbl->set_stts(stts);
SrsMp4Sample2ChunkBox* stsc = new SrsMp4Sample2ChunkBox();
stbl->set_stsc(stsc);
SrsMp4SampleSizeBox* stsz = new SrsMp4SampleSizeBox();
stbl->set_stsz(stsz);
SrsMp4ChunkOffsetBox* stco = new SrsMp4ChunkOffsetBox();
stbl->set_stco(stco);
SrsMp4MovieExtendsBox* mvex = new SrsMp4MovieExtendsBox();
moov->set_mvex(mvex);
SrsMp4TrackExtendsBox* trex = new SrsMp4TrackExtendsBox();
mvex->set_trex(trex);
trex->track_ID = audio_track_id;
trex->default_sample_description_index = 1;
}
}
int nb_data = ftyp->nb_bytes() + moov->nb_bytes();
uint8_t* data = new uint8_t[nb_data];
SrsAutoFreeA(uint8_t, data);
SrsBuffer* buffer = new SrsBuffer();
SrsAutoFree(SrsBuffer, buffer);
if ((ret = buffer->initialize((char*)data, nb_data)) != ERROR_SUCCESS) {
return ret;
}
if ((ret = ftyp->encode(buffer)) != ERROR_SUCCESS) {
return ret;
}
if ((ret = moov->encode(buffer)) != ERROR_SUCCESS) {
return ret;
}
if ((ret = fw->write(data, nb_data, NULL)) != ERROR_SUCCESS) {
return ret;
}
if (::rename(path_tmp.c_str(), path.c_str()) < 0) {
ret = ERROR_DASH_WRITE_FAILED;
srs_error("DASH: Rename %s to %s failed, ret=%d", path_tmp.c_str(), path.c_str(), ret);
return ret;
}
srs_trace("DASH: Refresh media success, file=%s", path.c_str());
return ret;
}
SrsDash::SrsDash()
{
hub = NULL;

@ -80,7 +80,12 @@ public:
class SrsDashController
{
private:
SrsRequest* req;
SrsMpdWriter* mpd;
private:
std::string home;
int video_tack_id;
int audio_track_id;
public:
SrsDashController();
virtual ~SrsDashController();
@ -90,6 +95,7 @@ public:
virtual int on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format);
private:
virtual int refresh_mpd(SrsFormat* format);
virtual int refresh_init_mp4(SrsSharedPtrMessage* msg, SrsFormat* format);
};
/**

@ -560,7 +560,8 @@ int SrsFormat::on_audio(int64_t timestamp, char* data, int size)
}
// @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76
SrsAudioCodecId codec = (SrsAudioCodecId)((buffer->read_1bytes() >> 4) & 0x0f);
uint8_t v = buffer->read_1bytes();
SrsAudioCodecId codec = (SrsAudioCodecId)((v >> 4) & 0x0f);
if (codec != SrsAudioCodecIdMP3 && codec != SrsAudioCodecIdAAC) {
return ret;
@ -1361,6 +1362,21 @@ int SrsFormat::audio_mp3_demux(SrsBuffer* stream, int64_t timestamp)
audio->cts = 0;
audio->dts = timestamp;
// @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76
int8_t sound_format = stream->read_1bytes();
int8_t sound_type = sound_format & 0x01;
int8_t sound_size = (sound_format >> 1) & 0x01;
int8_t sound_rate = (sound_format >> 2) & 0x03;
sound_format = (sound_format >> 4) & 0x0f;
SrsAudioCodecId codec_id = (SrsAudioCodecId)sound_format;
acodec->id = codec_id;
acodec->sound_type = (SrsAudioChannels)sound_type;
acodec->sound_rate = (SrsAudioSampleRate)sound_rate;
acodec->sound_size = (SrsAudioSampleBits)sound_size;
// we always decode aac then mp3.
srs_assert(acodec->id == SrsAudioCodecIdMP3);

@ -515,14 +515,17 @@ public:
*/
class SrsAudioCodecConfig : public SrsCodecConfig
{
// In FLV specification.
public:
// audio specified
SrsAudioCodecId id;
// audio aac specified.
SrsAudioSampleRate sound_rate;
SrsAudioSampleBits sound_size;
// TODO: FIXME: Rename to sound_channels.
SrsAudioChannels sound_type;
int audio_data_rate; // in bps
// In AAC specification.
public:
/**
* audio specified
@ -539,6 +542,7 @@ public:
* channelConfiguration
*/
uint8_t aac_channels;
// Sequence header payload.
public:
/**
* the aac extra data, the AAC sequence header,

@ -226,6 +226,8 @@ int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox)
case SrsMp4BoxTypeMP4A: box = new SrsMp4AudioSampleEntry(); break;
case SrsMp4BoxTypeESDS: box = new SrsMp4EsdsBox(); break;
case SrsMp4BoxTypeUDTA: box = new SrsMp4UserDataBox(); break;
case SrsMp4BoxTypeMVEX: box = new SrsMp4MovieExtendsBox(); break;
case SrsMp4BoxTypeTREX: box = new SrsMp4TrackExtendsBox(); break;
default:
ret = ERROR_MP4_BOX_ILLEGAL_TYPE;
srs_error("MP4 illegal box type=%d. ret=%d", type, ret);
@ -690,7 +692,19 @@ SrsMp4MovieHeaderBox* SrsMp4MovieBox::mvhd()
void SrsMp4MovieBox::set_mvhd(SrsMp4MovieHeaderBox* v)
{
remove(SrsMp4BoxTypeMVHD);
boxes.insert(boxes.begin(), v);
boxes.push_back(v);
}
SrsMp4MovieExtendsBox* SrsMp4MovieBox::mvex()
{
SrsMp4Box* box = get(SrsMp4BoxTypeMVEX);
return dynamic_cast<SrsMp4MovieExtendsBox*>(box);
}
void SrsMp4MovieBox::set_mvex(SrsMp4MovieExtendsBox* v)
{
remove(SrsMp4BoxTypeMVEX);
boxes.push_back(v);
}
SrsMp4TrackBox* SrsMp4MovieBox::video()
@ -884,6 +898,77 @@ int SrsMp4MovieHeaderBox::decode_header(SrsBuffer* buf)
return ret;
}
SrsMp4MovieExtendsBox::SrsMp4MovieExtendsBox()
{
type = SrsMp4BoxTypeMVEX;
}
SrsMp4MovieExtendsBox::~SrsMp4MovieExtendsBox()
{
}
SrsMp4TrackExtendsBox* SrsMp4MovieExtendsBox::trex()
{
SrsMp4Box* box = get(SrsMp4BoxTypeTREX);
return dynamic_cast<SrsMp4TrackExtendsBox*>(box);
}
void SrsMp4MovieExtendsBox::set_trex(SrsMp4TrackExtendsBox* v)
{
remove(SrsMp4BoxTypeTREX);
boxes.push_back(v);
}
SrsMp4TrackExtendsBox::SrsMp4TrackExtendsBox()
{
type = SrsMp4BoxTypeTREX;
track_ID = default_sample_size = default_sample_flags = 0;
default_sample_size = default_sample_duration = default_sample_description_index = 0;
}
SrsMp4TrackExtendsBox::~SrsMp4TrackExtendsBox()
{
}
int SrsMp4TrackExtendsBox::nb_header()
{
return SrsMp4FullBox::nb_header() + 4*5;
}
int SrsMp4TrackExtendsBox::encode_header(SrsBuffer* buf)
{
int ret = ERROR_SUCCESS;
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
return ret;
}
buf->write_4bytes(track_ID);
buf->write_4bytes(default_sample_description_index);
buf->write_4bytes(default_sample_duration);
buf->write_4bytes(default_sample_size);
buf->write_4bytes(default_sample_flags);
return ret;
}
int SrsMp4TrackExtendsBox::decode_header(SrsBuffer* buf)
{
int ret = ERROR_SUCCESS;
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
return ret;
}
track_ID = buf->read_4bytes();
default_sample_description_index = buf->read_4bytes();
default_sample_duration = buf->read_4bytes();
default_sample_size = buf->read_4bytes();
default_sample_flags = buf->read_4bytes();
return ret;
}
SrsMp4TrackBox::SrsMp4TrackBox()
{
type = SrsMp4BoxTypeTRAK;
@ -4010,7 +4095,8 @@ int SrsMp4Decoder::parse_ftyp(SrsMp4FileTypeBox* ftyp)
// File Type Box (ftyp)
bool legal_brand = false;
static SrsMp4BoxBrand legal_brands[] = {
SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO2, SrsMp4BoxBrandAVC1, SrsMp4BoxBrandMP41
SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO2, SrsMp4BoxBrandAVC1, SrsMp4BoxBrandMP41,
SrsMp4BoxBrandISO5
};
for (int i = 0; i < sizeof(legal_brands)/sizeof(SrsMp4BoxBrand); i++) {
if (ftyp->major_brand == legal_brands[i]) {
@ -4375,7 +4461,7 @@ int SrsMp4Encoder::flush()
mvhd->timescale = 1000; // Use tbn ms.
mvhd->duration_in_tbn = srs_max(vduration, aduration);
mvhd->next_track_ID++;
mvhd->next_track_ID = 1; // Starts from 1, increase when use it.
if (nb_videos) {
SrsMp4TrackBox* trak = new SrsMp4TrackBox();

@ -64,6 +64,8 @@ class SrsMp4VideoMeidaHeaderBox;
class SrsMp4DataInformationBox;
class SrsMp4DataReferenceBox;
class SrsMp4SoundMeidaHeaderBox;
class SrsMp4MovieExtendsBox;
class SrsMp4TrackExtendsBox;
/**
* 4.2 Object Structure
@ -109,6 +111,8 @@ enum SrsMp4BoxType
SrsMp4BoxTypeMP4A = 0x6d703461, // 'mp4a'
SrsMp4BoxTypeESDS = 0x65736473, // 'esds'
SrsMp4BoxTypeUDTA = 0x75647461, // 'udta'
SrsMp4BoxTypeMVEX = 0x6d766578, // 'mvex'
SrsMp4BoxTypeTREX = 0x74726578, // 'trex'
};
/**
@ -134,6 +138,9 @@ enum SrsMp4BoxBrand
SrsMp4BoxBrandISO2 = 0x69736f32, // 'iso2'
SrsMp4BoxBrandAVC1 = 0x61766331, // 'avc1'
SrsMp4BoxBrandMP41 = 0x6d703431, // 'mp41'
SrsMp4BoxBrandISO5 = 0x69736f35, // 'iso5'
SrsMp4BoxBrandMP42 = 0x6d703432, // 'mp42'
SrsMp4BoxBrandDASH = 0x64617368, // 'dash'
};
/**
@ -321,6 +328,9 @@ public:
// Get the header of moov.
virtual SrsMp4MovieHeaderBox* mvhd();
virtual void set_mvhd(SrsMp4MovieHeaderBox* v);
// Get the movie extends header.
virtual SrsMp4MovieExtendsBox* mvex();
virtual void set_mvex(SrsMp4MovieExtendsBox* v);
// Get the first video track.
virtual SrsMp4TrackBox* video();
// Get the first audio track.
@ -395,6 +405,47 @@ enum SrsMp4TrackType
SrsMp4TrackTypeVideo = 0x02,
};
/**
* 8.8.1 Movie Extends Box (mvex)
* ISO_IEC_14496-12-base-format-2012.pdf, page 64
* This box warns readers that there might be Movie Fragment Boxes in this file. To know of all samples in the
* tracks, these Movie Fragment Boxes must be found and scanned in order, and their information logically
* added to that found in the Movie Box.
*/
class SrsMp4MovieExtendsBox : public SrsMp4Box
{
public:
SrsMp4MovieExtendsBox();
virtual ~SrsMp4MovieExtendsBox();
public:
// Get the track extends box.
virtual SrsMp4TrackExtendsBox* trex();
virtual void set_trex(SrsMp4TrackExtendsBox* v);
};
/**
* 8.8.3 Track Extends Box(trex)
* ISO_IEC_14496-12-base-format-2012.pdf, page 65
*/
class SrsMp4TrackExtendsBox : public SrsMp4FullBox
{
public:
// identifies the track; this shall be the track ID of a track in the Movie Box
uint32_t track_ID;
// these fields set up defaults used in the track fragments.
uint32_t default_sample_description_index;
uint32_t default_sample_duration;
uint32_t default_sample_size;
uint32_t default_sample_flags;
public:
SrsMp4TrackExtendsBox();
virtual ~SrsMp4TrackExtendsBox();
protected:
virtual int nb_header();
virtual int encode_header(SrsBuffer* buf);
virtual int decode_header(SrsBuffer* buf);
};
/**
* 8.3.1 Track Box (trak)
* ISO_IEC_14496-12-base-format-2012.pdf, page 32

Loading…
Cancel
Save