|
|
@ -339,8 +339,10 @@ srs_error_t SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox)
|
|
|
|
case SrsMp4BoxTypeSTCO: box = new SrsMp4ChunkOffsetBox(); break;
|
|
|
|
case SrsMp4BoxTypeSTCO: box = new SrsMp4ChunkOffsetBox(); break;
|
|
|
|
case SrsMp4BoxTypeCO64: box = new SrsMp4ChunkLargeOffsetBox(); break;
|
|
|
|
case SrsMp4BoxTypeCO64: box = new SrsMp4ChunkLargeOffsetBox(); break;
|
|
|
|
case SrsMp4BoxTypeSTSZ: box = new SrsMp4SampleSizeBox(); break;
|
|
|
|
case SrsMp4BoxTypeSTSZ: box = new SrsMp4SampleSizeBox(); break;
|
|
|
|
case SrsMp4BoxTypeAVC1: box = new SrsMp4VisualSampleEntry(); break;
|
|
|
|
case SrsMp4BoxTypeAVC1: box = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeAVC1); break;
|
|
|
|
|
|
|
|
case SrsMp4BoxTypeHEV1: box = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeHEV1); break;
|
|
|
|
case SrsMp4BoxTypeAVCC: box = new SrsMp4AvccBox(); break;
|
|
|
|
case SrsMp4BoxTypeAVCC: box = new SrsMp4AvccBox(); break;
|
|
|
|
|
|
|
|
case SrsMp4BoxTypeHVCC: box = new SrsMp4HvcCBox(); break;
|
|
|
|
case SrsMp4BoxTypeMP4A: box = new SrsMp4AudioSampleEntry(); break;
|
|
|
|
case SrsMp4BoxTypeMP4A: box = new SrsMp4AudioSampleEntry(); break;
|
|
|
|
case SrsMp4BoxTypeESDS: box = new SrsMp4EsdsBox(); break;
|
|
|
|
case SrsMp4BoxTypeESDS: box = new SrsMp4EsdsBox(); break;
|
|
|
|
case SrsMp4BoxTypeUDTA: box = new SrsMp4UserDataBox(); break;
|
|
|
|
case SrsMp4BoxTypeUDTA: box = new SrsMp4UserDataBox(); break;
|
|
|
@ -646,6 +648,14 @@ void SrsMp4FileTypeBox::set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand
|
|
|
|
compatible_brands[1] = b1;
|
|
|
|
compatible_brands[1] = b1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SrsMp4FileTypeBox::set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1, SrsMp4BoxBrand b2)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
compatible_brands.resize(3);
|
|
|
|
|
|
|
|
compatible_brands[0] = b0;
|
|
|
|
|
|
|
|
compatible_brands[1] = b1;
|
|
|
|
|
|
|
|
compatible_brands[2] = b2;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
@ -3019,9 +3029,9 @@ stringstream& SrsMp4SampleEntry::dumps_detail(stringstream& ss, SrsMp4DumpContex
|
|
|
|
return ss;
|
|
|
|
return ss;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4VisualSampleEntry::SrsMp4VisualSampleEntry() : width(0), height(0)
|
|
|
|
SrsMp4VisualSampleEntry::SrsMp4VisualSampleEntry(SrsMp4BoxType boxType) : width(0), height(0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
type = SrsMp4BoxTypeAVC1;
|
|
|
|
type = boxType;
|
|
|
|
|
|
|
|
|
|
|
|
pre_defined0 = 0;
|
|
|
|
pre_defined0 = 0;
|
|
|
|
reserved0 = 0;
|
|
|
|
reserved0 = 0;
|
|
|
@ -3051,6 +3061,18 @@ void SrsMp4VisualSampleEntry::set_avcC(SrsMp4AvccBox* v)
|
|
|
|
boxes.push_back(v);
|
|
|
|
boxes.push_back(v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4HvcCBox* SrsMp4VisualSampleEntry::hvcC()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeHVCC);
|
|
|
|
|
|
|
|
return dynamic_cast<SrsMp4HvcCBox*>(box);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SrsMp4VisualSampleEntry::set_hvcC(SrsMp4HvcCBox* v)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
remove(SrsMp4BoxTypeHVCC);
|
|
|
|
|
|
|
|
boxes.push_back(v);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int SrsMp4VisualSampleEntry::nb_header()
|
|
|
|
int SrsMp4VisualSampleEntry::nb_header()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return SrsMp4SampleEntry::nb_header()+2+2+12+2+2+4+4+4+2+32+2+2;
|
|
|
|
return SrsMp4SampleEntry::nb_header()+2+2+12+2+2+4+4+4+2+32+2+2;
|
|
|
@ -3170,6 +3192,62 @@ stringstream& SrsMp4AvccBox::dumps_detail(stringstream& ss, SrsMp4DumpContext dc
|
|
|
|
return ss;
|
|
|
|
return ss;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4HvcCBox::SrsMp4HvcCBox()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
type = SrsMp4BoxTypeHVCC;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4HvcCBox::~SrsMp4HvcCBox()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int SrsMp4HvcCBox::nb_header()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return SrsMp4Box::nb_header() + (int)hevc_config.size();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsMp4HvcCBox::encode_header(SrsBuffer* buf)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((err = SrsMp4Box::encode_header(buf)) != srs_success) {
|
|
|
|
|
|
|
|
return srs_error_wrap(err, "encode header");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!hevc_config.empty()) {
|
|
|
|
|
|
|
|
buf->write_bytes(&hevc_config[0], (int)hevc_config.size());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsMp4HvcCBox::decode_header(SrsBuffer* buf)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((err = SrsMp4Box::decode_header(buf)) != srs_success) {
|
|
|
|
|
|
|
|
return srs_error_wrap(err, "decode header");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int nb_config = left_space(buf);
|
|
|
|
|
|
|
|
if (nb_config) {
|
|
|
|
|
|
|
|
hevc_config.resize(nb_config);
|
|
|
|
|
|
|
|
buf->read_bytes(&hevc_config[0], nb_config);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stringstream& SrsMp4HvcCBox::dumps_detail(stringstream& ss, SrsMp4DumpContext dc)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
SrsMp4Box::dumps_detail(ss, dc);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ss << ", HEVC Config: " << (int)hevc_config.size() << "B" << endl;
|
|
|
|
|
|
|
|
srs_mp4_padding(ss, dc.indent());
|
|
|
|
|
|
|
|
srs_mp4_print_bytes(ss, (const char*)&hevc_config[0], (int)hevc_config.size(), dc.indent());
|
|
|
|
|
|
|
|
return ss;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4AudioSampleEntry::SrsMp4AudioSampleEntry() : samplerate(0)
|
|
|
|
SrsMp4AudioSampleEntry::SrsMp4AudioSampleEntry() : samplerate(0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
type = SrsMp4BoxTypeMP4A;
|
|
|
|
type = SrsMp4BoxTypeMP4A;
|
|
|
@ -5668,7 +5746,7 @@ srs_error_t SrsMp4Encoder::initialize(ISrsWriteSeeker* ws)
|
|
|
|
|
|
|
|
|
|
|
|
ftyp->major_brand = SrsMp4BoxBrandISOM;
|
|
|
|
ftyp->major_brand = SrsMp4BoxBrandISOM;
|
|
|
|
ftyp->minor_version = 512;
|
|
|
|
ftyp->minor_version = 512;
|
|
|
|
ftyp->set_compatible_brands(SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO2, SrsMp4BoxBrandAVC1, SrsMp4BoxBrandMP41);
|
|
|
|
ftyp->set_compatible_brands(SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO2, SrsMp4BoxBrandMP41);
|
|
|
|
|
|
|
|
|
|
|
|
int nb_data = ftyp->nb_bytes();
|
|
|
|
int nb_data = ftyp->nb_bytes();
|
|
|
|
std::vector<char> data(nb_data);
|
|
|
|
std::vector<char> data(nb_data);
|
|
|
@ -5806,7 +5884,7 @@ srs_error_t SrsMp4Encoder::flush()
|
|
|
|
mvhd->duration_in_tbn = srs_max(vduration, aduration);
|
|
|
|
mvhd->duration_in_tbn = srs_max(vduration, aduration);
|
|
|
|
mvhd->next_track_ID = 1; // Starts from 1, increase when use it.
|
|
|
|
mvhd->next_track_ID = 1; // Starts from 1, increase when use it.
|
|
|
|
|
|
|
|
|
|
|
|
if (nb_videos || !pavcc.empty()) {
|
|
|
|
if (nb_videos || !pavcc.empty() || !phvcc.empty()) {
|
|
|
|
SrsMp4TrackBox* trak = new SrsMp4TrackBox();
|
|
|
|
SrsMp4TrackBox* trak = new SrsMp4TrackBox();
|
|
|
|
moov->add_trak(trak);
|
|
|
|
moov->add_trak(trak);
|
|
|
|
|
|
|
|
|
|
|
@ -5869,17 +5947,31 @@ srs_error_t SrsMp4Encoder::flush()
|
|
|
|
SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox();
|
|
|
|
SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox();
|
|
|
|
stbl->set_stsd(stsd);
|
|
|
|
stbl->set_stsd(stsd);
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry();
|
|
|
|
if (vcodec == SrsVideoCodecIdAVC) {
|
|
|
|
stsd->append(avc1);
|
|
|
|
SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeAVC1);
|
|
|
|
|
|
|
|
stsd->append(avc1);
|
|
|
|
|
|
|
|
|
|
|
|
avc1->width = width;
|
|
|
|
avc1->width = width;
|
|
|
|
avc1->height = height;
|
|
|
|
avc1->height = height;
|
|
|
|
avc1->data_reference_index = 1;
|
|
|
|
avc1->data_reference_index = 1;
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
|
|
|
|
SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
|
|
|
|
avc1->set_avcC(avcC);
|
|
|
|
avc1->set_avcC(avcC);
|
|
|
|
|
|
|
|
|
|
|
|
avcC->avc_config = pavcc;
|
|
|
|
avcC->avc_config = pavcc;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
SrsMp4VisualSampleEntry* hev1 = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeHEV1);
|
|
|
|
|
|
|
|
stsd->append(hev1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hev1->width = width;
|
|
|
|
|
|
|
|
hev1->height = height;
|
|
|
|
|
|
|
|
hev1->data_reference_index = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4HvcCBox* hvcC = new SrsMp4HvcCBox();
|
|
|
|
|
|
|
|
hev1->set_hvcC(hvcC);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hvcC->hevc_config = phvcc;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (nb_audios || !pasc.empty()) {
|
|
|
|
if (nb_audios || !pasc.empty()) {
|
|
|
@ -6037,12 +6129,23 @@ srs_error_t SrsMp4Encoder::copy_sequence_header(SrsFormat* format, bool vsh, uin
|
|
|
|
{
|
|
|
|
{
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
|
|
if (vsh && !pavcc.empty()) {
|
|
|
|
if (vsh) {
|
|
|
|
if (nb_sample == (uint32_t)pavcc.size() && srs_bytes_equals(sample, &pavcc[0], (int)pavcc.size())) {
|
|
|
|
// AVC
|
|
|
|
return err;
|
|
|
|
if (format->vcodec->id == SrsVideoCodecIdAVC && !pavcc.empty()) {
|
|
|
|
|
|
|
|
if (nb_sample == (uint32_t)pavcc.size() && srs_bytes_equals(sample, &pavcc[0], (int)pavcc.size())) {
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return srs_error_new(ERROR_MP4_AVCC_CHANGE, "doesn't support avcc change");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// HEVC
|
|
|
|
|
|
|
|
if (format->vcodec->id == SrsVideoCodecIdHEVC && !phvcc.empty()) {
|
|
|
|
|
|
|
|
if (nb_sample == (uint32_t)phvcc.size() && srs_bytes_equals(sample, &phvcc[0], (int)phvcc.size())) {
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return srs_error_new(ERROR_MP4_AVCC_CHANGE, "doesn't support avcc change");
|
|
|
|
return srs_error_new(ERROR_MP4_HVCC_CHANGE, "doesn't support hvcC change");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!vsh && !pasc.empty()) {
|
|
|
|
if (!vsh && !pasc.empty()) {
|
|
|
@ -6054,7 +6157,11 @@ srs_error_t SrsMp4Encoder::copy_sequence_header(SrsFormat* format, bool vsh, uin
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (vsh) {
|
|
|
|
if (vsh) {
|
|
|
|
pavcc = std::vector<char>(sample, sample + nb_sample);
|
|
|
|
if (format->vcodec->id == SrsVideoCodecIdHEVC) {
|
|
|
|
|
|
|
|
phvcc = std::vector<char>(sample, sample + nb_sample);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
pavcc = std::vector<char>(sample, sample + nb_sample);
|
|
|
|
|
|
|
|
}
|
|
|
|
if (format && format->vcodec) {
|
|
|
|
if (format && format->vcodec) {
|
|
|
|
width = format->vcodec->width;
|
|
|
|
width = format->vcodec->width;
|
|
|
|
height = format->vcodec->height;
|
|
|
|
height = format->vcodec->height;
|
|
|
@ -6199,17 +6306,31 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
|
|
|
|
SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox();
|
|
|
|
SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox();
|
|
|
|
stbl->set_stsd(stsd);
|
|
|
|
stbl->set_stsd(stsd);
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry();
|
|
|
|
if (format->vcodec->id == SrsVideoCodecIdAVC) {
|
|
|
|
stsd->append(avc1);
|
|
|
|
SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeAVC1);
|
|
|
|
|
|
|
|
stsd->append(avc1);
|
|
|
|
|
|
|
|
|
|
|
|
avc1->width = format->vcodec->width;
|
|
|
|
avc1->width = format->vcodec->width;
|
|
|
|
avc1->height = format->vcodec->height;
|
|
|
|
avc1->height = format->vcodec->height;
|
|
|
|
avc1->data_reference_index = 1;
|
|
|
|
avc1->data_reference_index = 1;
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
|
|
|
|
SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
|
|
|
|
avc1->set_avcC(avcC);
|
|
|
|
avc1->set_avcC(avcC);
|
|
|
|
|
|
|
|
|
|
|
|
avcC->avc_config = format->vcodec->avc_extra_data;
|
|
|
|
avcC->avc_config = format->vcodec->avc_extra_data;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
SrsMp4VisualSampleEntry* hev1 = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeHEV1);
|
|
|
|
|
|
|
|
stsd->append(hev1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hev1->width = format->vcodec->width;
|
|
|
|
|
|
|
|
hev1->height = format->vcodec->height;
|
|
|
|
|
|
|
|
hev1->data_reference_index = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4HvcCBox* hvcC = new SrsMp4HvcCBox();
|
|
|
|
|
|
|
|
hev1->set_hvcC(hvcC);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hvcC->hevc_config = format->vcodec->avc_extra_data;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SrsMp4DecodingTime2SampleBox* stts = new SrsMp4DecodingTime2SampleBox();
|
|
|
|
SrsMp4DecodingTime2SampleBox* stts = new SrsMp4DecodingTime2SampleBox();
|
|
|
|
stbl->set_stts(stts);
|
|
|
|
stbl->set_stts(stts);
|
|
|
|