Skip to content

Commit

Permalink
dvr mp4 support H265
Browse files Browse the repository at this point in the history
  • Loading branch information
mapengfei53 committed Jan 4, 2023
1 parent b104826 commit 6eee088
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 31 deletions.
4 changes: 2 additions & 2 deletions trunk/src/kernel/srs_kernel_error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@
XX(ERROR_HTTP_URL_UNESCAPE , 3096, "HttpUrlUnescape", "Failed to unescape URL for HTTP") \
XX(ERROR_HTTP_WITH_BODY , 3097, "HttpWithBody", "Failed for HTTP body") \
XX(ERROR_HEVC_DISABLED , 3098, "HevcDisabled", "HEVC is disabled") \
XX(ERROR_HEVC_DECODE_ERROR , 3099, "HevcDecode", "HEVC decode av stream failed")

XX(ERROR_HEVC_DECODE_ERROR , 3099, "HevcDecode", "HEVC decode av stream failed") \
XX(ERROR_MP4_HVCC_CHANGE , 3100, "Mp4HvcCChange", "MP4 does not support video HvcC change")
/**************************************************/
/* HTTP/StreamConverter protocol error. */
#define SRS_ERRNO_MAP_HTTP(XX) \
Expand Down
155 changes: 130 additions & 25 deletions trunk/src/kernel/srs_kernel_mp4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@ srs_error_t SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox)
case SrsMp4BoxTypeSTCO: box = new SrsMp4ChunkOffsetBox(); break;
case SrsMp4BoxTypeCO64: box = new SrsMp4ChunkLargeOffsetBox(); 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 SrsMp4BoxTypeMP4A: box = new SrsMp4AudioSampleEntry(); break;
case SrsMp4BoxTypeESDS: box = new SrsMp4EsdsBox(); break;
Expand Down Expand Up @@ -646,6 +647,14 @@ void SrsMp4FileTypeBox::set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand
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)
{
compatible_brands.resize(4);
Expand Down Expand Up @@ -3019,9 +3028,9 @@ stringstream& SrsMp4SampleEntry::dumps_detail(stringstream& ss, SrsMp4DumpContex
return ss;
}

SrsMp4VisualSampleEntry::SrsMp4VisualSampleEntry() : width(0), height(0)
SrsMp4VisualSampleEntry::SrsMp4VisualSampleEntry(SrsMp4BoxType boxType) : width(0), height(0)
{
type = SrsMp4BoxTypeAVC1;
type = boxType;

pre_defined0 = 0;
reserved0 = 0;
Expand Down Expand Up @@ -3051,6 +3060,18 @@ void SrsMp4VisualSampleEntry::set_avcC(SrsMp4AvccBox* 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()
{
return SrsMp4SampleEntry::nb_header()+2+2+12+2+2+4+4+4+2+32+2+2;
Expand Down Expand Up @@ -3170,6 +3191,62 @@ stringstream& SrsMp4AvccBox::dumps_detail(stringstream& ss, SrsMp4DumpContext dc
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)
{
type = SrsMp4BoxTypeMP4A;
Expand Down Expand Up @@ -5668,7 +5745,7 @@ srs_error_t SrsMp4Encoder::initialize(ISrsWriteSeeker* ws)

ftyp->major_brand = SrsMp4BoxBrandISOM;
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();
std::vector<char> data(nb_data);
Expand Down Expand Up @@ -5806,7 +5883,7 @@ srs_error_t SrsMp4Encoder::flush()
mvhd->duration_in_tbn = srs_max(vduration, aduration);
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();
moov->add_trak(trak);

Expand Down Expand Up @@ -5868,18 +5945,32 @@ srs_error_t SrsMp4Encoder::flush()

SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox();
stbl->set_stsd(stsd);

SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry();
stsd->append(avc1);

avc1->width = width;
avc1->height = height;
avc1->data_reference_index = 1;

SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
avc1->set_avcC(avcC);

avcC->avc_config = pavcc;

if (vcodec == SrsVideoCodecIdHEVC) {
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;
} else {
SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeAVC1);
stsd->append(avc1);

avc1->width = width;
avc1->height = height;
avc1->data_reference_index = 1;

SrsMp4AvccBox* avcC = new SrsMp4AvccBox();
avc1->set_avcC(avcC);

avcC->avc_config = pavcc;
}
}

if (nb_audios || !pasc.empty()) {
Expand Down Expand Up @@ -6036,13 +6127,23 @@ srs_error_t SrsMp4Encoder::flush()
srs_error_t SrsMp4Encoder::copy_sequence_header(SrsFormat* format, bool vsh, uint8_t* sample, uint32_t nb_sample)
{
srs_error_t err = srs_success;

if (vsh && !pavcc.empty()) {
if (nb_sample == (uint32_t)pavcc.size() && srs_bytes_equals(sample, &pavcc[0], (int)pavcc.size())) {
return err;

if (vsh) {
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_HVCC_CHANGE, "doesn't support hvcC change");
} else {
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");
}
}

return srs_error_new(ERROR_MP4_AVCC_CHANGE, "doesn't support avcc change");
}

if (!vsh && !pasc.empty()) {
Expand All @@ -6054,7 +6155,11 @@ srs_error_t SrsMp4Encoder::copy_sequence_header(SrsFormat* format, bool vsh, uin
}

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) {
width = format->vcodec->width;
height = format->vcodec->height;
Expand Down Expand Up @@ -6199,7 +6304,7 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
SrsMp4SampleDescriptionBox* stsd = new SrsMp4SampleDescriptionBox();
stbl->set_stsd(stsd);

SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry();
SrsMp4VisualSampleEntry* avc1 = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeAVC1);
stsd->append(avc1);

avc1->width = format->vcodec->width;
Expand Down
31 changes: 29 additions & 2 deletions trunk/src/kernel/srs_kernel_mp4.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class SrsMp4SampleDescriptionBox;
class SrsMp4AvccBox;
class SrsMp4DecoderSpecificInfo;
class SrsMp4VisualSampleEntry;
class SrsMp4AvccBox;
class SrsMp4HvcCBox;
class SrsMp4AudioSampleEntry;
class SrsMp4EsdsBox;
class SrsMp4ChunkOffsetBox;
Expand Down Expand Up @@ -111,6 +111,8 @@ enum SrsMp4BoxType
SrsMp4BoxTypeTFDT = 0x74666474, // 'tfdt'
SrsMp4BoxTypeTRUN = 0x7472756e, // 'trun'
SrsMp4BoxTypeSIDX = 0x73696478, // 'sidx'
SrsMp4BoxTypeHEV1 = 0x68657631, // 'hev1'
SrsMp4BoxTypeHVCC = 0x68766343, // 'hvcC'
};

// 8.4.3.3 Semantics
Expand Down Expand Up @@ -138,6 +140,7 @@ enum SrsMp4BoxBrand
SrsMp4BoxBrandDASH = 0x64617368, // 'dash'
SrsMp4BoxBrandMSDH = 0x6d736468, // 'msdh'
SrsMp4BoxBrandMSIX = 0x6d736978, // 'msix'
SrsMp4BoxBrandHEV1 = 0x68657631, // 'hev1'
};

// The context to dump.
Expand Down Expand Up @@ -277,6 +280,7 @@ class SrsMp4FileTypeBox : public SrsMp4Box
virtual ~SrsMp4FileTypeBox();
public:
virtual void set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1);
virtual void set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1, SrsMp4BoxBrand b2);
virtual void set_compatible_brands(SrsMp4BoxBrand b0, SrsMp4BoxBrand b1, SrsMp4BoxBrand b2, SrsMp4BoxBrand b3);
protected:
virtual int nb_header();
Expand Down Expand Up @@ -1261,12 +1265,16 @@ class SrsMp4VisualSampleEntry : public SrsMp4SampleEntry
uint16_t depth;
int16_t pre_defined2;
public:
SrsMp4VisualSampleEntry();
SrsMp4VisualSampleEntry(SrsMp4BoxType boxType);
virtual ~SrsMp4VisualSampleEntry();
public:
// For avc1, get the avcc box.
virtual SrsMp4AvccBox* avcC();
virtual void set_avcC(SrsMp4AvccBox* v);
public:
// For hev1, get the hvcC box.
virtual SrsMp4HvcCBox* hvcC();
virtual void set_hvcC(SrsMp4HvcCBox* v);
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
Expand All @@ -1292,6 +1300,23 @@ class SrsMp4AvccBox : public SrsMp4Box
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};

// 8.4.1 HEVC Video Stream Definition (hvcC)
// ISO-14496-15-AVC-file-format-2017.pdf, page 73
class SrsMp4HvcCBox : public SrsMp4Box
{
public:
std::vector<char> hevc_config;
public:
SrsMp4HvcCBox();
virtual ~SrsMp4HvcCBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};

// 8.5.2 Sample Description Box (mp4a)
// ISO_IEC_14496-12-base-format-2012.pdf, page 45
class SrsMp4AudioSampleEntry : public SrsMp4SampleEntry
Expand Down Expand Up @@ -2051,6 +2076,8 @@ class SrsMp4Encoder
private:
// For H.264/AVC, the avcc contains the sps/pps.
std::vector<char> pavcc;
// For H.265/HEVC, the hvcC contains the vps/sps/pps.
std::vector<char> phvcc;
// The number of video samples.
uint32_t nb_videos;
// The duration of video stream.
Expand Down
Loading

0 comments on commit 6eee088

Please sign in to comment.