2017-01-27 03:27:02 +00:00
|
|
|
/*
|
|
|
|
The MIT License (MIT)
|
|
|
|
|
|
|
|
Copyright (c) 2013-2017 SRS(ossrs)
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
|
|
the Software without restriction, including without limitation the rights to
|
|
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
|
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
|
|
subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <srs_kernel_mp4.hpp>
|
|
|
|
|
2017-01-31 13:36:51 +00:00
|
|
|
#include <srs_kernel_log.hpp>
|
2017-01-31 12:43:48 +00:00
|
|
|
#include <srs_kernel_error.hpp>
|
2017-02-01 13:57:32 +00:00
|
|
|
#include <srs_kernel_stream.hpp>
|
|
|
|
#include <srs_core_autofree.hpp>
|
|
|
|
#include <srs_kernel_io.hpp>
|
|
|
|
#include <srs_kernel_buffer.hpp>
|
|
|
|
|
|
|
|
#include <string.h>
|
2017-02-03 13:03:26 +00:00
|
|
|
#include <sstream>
|
2017-02-01 13:57:32 +00:00
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
#define SRS_MP4_EOF_SIZE 0
|
|
|
|
#define SRS_MP4_USE_LARGE_SIZE 1
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
#define SRS_MP4_BUF_SIZE 4096
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int srs_mp4_string_length(const string& v)
|
|
|
|
{
|
|
|
|
return (int)v.length()+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void srs_mp4_string_write(SrsBuffer* buf, const string& v)
|
|
|
|
{
|
2017-02-02 07:10:11 +00:00
|
|
|
// Nothing for empty string.
|
|
|
|
if (v.empty()) {
|
|
|
|
return;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
2017-02-02 07:10:11 +00:00
|
|
|
|
|
|
|
buf->write_bytes((char*)v.data(), (int)v.length());
|
2017-02-01 13:57:32 +00:00
|
|
|
buf->write_1bytes(0x00);
|
|
|
|
}
|
|
|
|
|
|
|
|
int srs_mp4_string_read(SrsBuffer* buf, string& v, int left)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
if (left == 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-02-01 13:57:32 +00:00
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
char* start = buf->data() + buf->pos();
|
|
|
|
size_t len = strnlen(start, left);
|
|
|
|
|
|
|
|
if (len == left) {
|
|
|
|
ret = ERROR_MP4_BOX_STRING;
|
|
|
|
srs_error("MP4 string corrupt, left=%d. ret=%d", left, ret);
|
|
|
|
return ret;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
v.append(start, len);
|
|
|
|
buf->skip((int)len + 1);
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2017-01-31 12:43:48 +00:00
|
|
|
|
2017-01-28 11:32:43 +00:00
|
|
|
SrsMp4Box::SrsMp4Box()
|
2017-01-27 12:54:05 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
smallsize = 0;
|
|
|
|
largesize = 0;
|
|
|
|
usertype = NULL;
|
|
|
|
start_pos = 0;
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeForbidden;
|
2017-01-27 12:54:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Box::~SrsMp4Box()
|
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
vector<SrsMp4Box*>::iterator it;
|
|
|
|
for (it = boxes.begin(); it != boxes.end(); ++it) {
|
|
|
|
SrsMp4Box* box = *it;
|
|
|
|
srs_freep(box);
|
|
|
|
}
|
|
|
|
boxes.clear();
|
|
|
|
|
|
|
|
srs_freepa(usertype);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t SrsMp4Box::sz()
|
|
|
|
{
|
|
|
|
return smallsize == SRS_MP4_USE_LARGE_SIZE? largesize:smallsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::left_space(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
return (int)sz() - (buf->pos() - start_pos);
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
bool SrsMp4Box::is_ftyp()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
return type == SrsMp4BoxTypeFTYP;
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool SrsMp4Box::is_moov()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
return type == SrsMp4BoxTypeMOOV;
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool SrsMp4Box::is_mdat()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
return type == SrsMp4BoxTypeMDAT;
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
|
|
|
|
2017-02-03 06:57:28 +00:00
|
|
|
SrsMp4Box* SrsMp4Box::get(SrsMp4BoxType bt)
|
|
|
|
{
|
|
|
|
vector<SrsMp4Box*>::iterator it;
|
|
|
|
for (it = boxes.begin(); it != boxes.end(); ++it) {
|
|
|
|
SrsMp4Box* box = *it;
|
|
|
|
if (box->type == bt) {
|
|
|
|
return box;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4Box::discovery(SrsBuffer* buf, SrsMp4Box** ppbox)
|
|
|
|
{
|
|
|
|
*ppbox = NULL;
|
|
|
|
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if (!buf->require(8)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 discovery require 8 bytes space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Discovery the size and type.
|
|
|
|
uint64_t largesize = 0;
|
|
|
|
uint32_t smallsize = (uint32_t)buf->read_4bytes();
|
2017-02-02 14:02:39 +00:00
|
|
|
SrsMp4BoxType type = (SrsMp4BoxType)buf->read_4bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
if (smallsize == SRS_MP4_USE_LARGE_SIZE) {
|
|
|
|
if (!buf->require(8)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 discovery require 16 bytes space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
largesize = (uint64_t)buf->read_8bytes();
|
|
|
|
buf->skip(-8);
|
|
|
|
}
|
|
|
|
buf->skip(-8);
|
|
|
|
|
|
|
|
// Only support 31bits size.
|
|
|
|
if (largesize > 0x7fffffff) {
|
|
|
|
ret = ERROR_MP4_BOX_OVERFLOW;
|
|
|
|
srs_error("MP4 discovery overflow 31bits, size=%"PRId64". ret=%d", largesize, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Box* box = NULL;
|
|
|
|
switch(type) {
|
2017-02-02 14:02:39 +00:00
|
|
|
case SrsMp4BoxTypeFTYP: box = new SrsMp4FileTypeBox(); break;
|
|
|
|
case SrsMp4BoxTypeMDAT: box = new SrsMp4MediaDataBox(); break;
|
|
|
|
case SrsMp4BoxTypeFREE: case SrsMp4BoxTypeSKIP: box = new SrsMp4FreeSpaceBox(); break;
|
|
|
|
case SrsMp4BoxTypeMOOV: box = new SrsMp4MovieBox(); break;
|
|
|
|
case SrsMp4BoxTypeMVHD: box = new SrsMp4MovieHeaderBox(); break;
|
|
|
|
case SrsMp4BoxTypeTRAK: box = new SrsMp4TrackBox(); break;
|
|
|
|
case SrsMp4BoxTypeTKHD: box = new SrsMp4TrackHeaderBox(); break;
|
|
|
|
case SrsMp4BoxTypeEDTS: box = new SrsMp4EditBox(); break;
|
|
|
|
case SrsMp4BoxTypeELST: box = new SrsMp4EditListBox(); break;
|
|
|
|
case SrsMp4BoxTypeMDIA: box = new SrsMp4MediaBox(); break;
|
|
|
|
case SrsMp4BoxTypeMDHD: box = new SrsMp4MediaHeaderBox(); break;
|
|
|
|
case SrsMp4BoxTypeHDLR: box = new SrsMp4HandlerReferenceBox(); break;
|
|
|
|
case SrsMp4BoxTypeMINF: box = new SrsMp4MediaInformationBox(); break;
|
|
|
|
case SrsMp4BoxTypeVMHD: box = new SrsMp4VideoMeidaHeaderBox(); break;
|
|
|
|
case SrsMp4BoxTypeSMHD: box = new SrsMp4SoundMeidaHeaderBox(); break;
|
|
|
|
case SrsMp4BoxTypeDINF: box = new SrsMp4DataInformationBox(); break;
|
|
|
|
case SrsMp4BoxTypeURL: box = new SrsMp4DataEntryUrlBox(); break;
|
|
|
|
case SrsMp4BoxTypeURN: box = new SrsMp4DataEntryUrnBox(); break;
|
|
|
|
case SrsMp4BoxTypeDREF: box = new SrsMp4DataReferenceBox(); break;
|
|
|
|
case SrsMp4BoxTypeSTBL: box = new SrsMp4SampleTableBox(); break;
|
|
|
|
case SrsMp4BoxTypeSTSD: box = new SrsMp4SampleDescriptionBox(); break;
|
|
|
|
case SrsMp4BoxTypeSTTS: box = new SrsMp4DecodingTime2SampleBox(); break;
|
|
|
|
case SrsMp4BoxTypeCTTS: box = new SrsMp4CompositionTime2SampleBox(); break;
|
|
|
|
case SrsMp4BoxTypeSTSS: box = new SrsMp4SyncSampleBox(); break;
|
|
|
|
case SrsMp4BoxTypeSTSC: box = new SrsMp4Sample2ChunkBox(); break;
|
|
|
|
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 SrsMp4BoxTypeAVCC: box = new SrsMp4AvccBox(); break;
|
|
|
|
case SrsMp4BoxTypeMP4A: box = new SrsMp4AudioSampleEntry(); break;
|
|
|
|
case SrsMp4BoxTypeESDS: box = new SrsMp4EsdsBox(); break;
|
2017-02-02 14:20:33 +00:00
|
|
|
case SrsMp4BoxTypeUDTA: box = new SrsMp4UserDataBox(); break;
|
2017-02-01 13:57:32 +00:00
|
|
|
default:
|
|
|
|
ret = ERROR_MP4_BOX_ILLEGAL_TYPE;
|
|
|
|
srs_error("MP4 illegal box type=%d. ret=%d", type, ret);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (box) {
|
|
|
|
box->smallsize = smallsize;
|
|
|
|
box->largesize = largesize;
|
|
|
|
box->type = type;
|
|
|
|
*ppbox = box;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::nb_bytes()
|
|
|
|
{
|
|
|
|
int sz = nb_header();
|
|
|
|
|
|
|
|
vector<SrsMp4Box*>::iterator it;
|
|
|
|
for (it = boxes.begin(); it != boxes.end(); ++it) {
|
|
|
|
SrsMp4Box* box = *it;
|
|
|
|
sz += box->nb_bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::encode(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 encode box header failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = encode_boxes(buf)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 encode contained boxes failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::decode(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
start_pos = buf->pos();
|
|
|
|
|
|
|
|
if ((ret = decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 decode box header failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = decode_boxes(buf)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 decode contained boxes failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::encode_boxes(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
vector<SrsMp4Box*>::iterator it;
|
|
|
|
for (it = boxes.begin(); it != boxes.end(); ++it) {
|
|
|
|
SrsMp4Box* box = *it;
|
|
|
|
if ((ret = box->encode(buf)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 encode contained box failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::decode_boxes(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
int left = left_space(buf);
|
|
|
|
while (left > 0) {
|
|
|
|
SrsMp4Box* box = NULL;
|
|
|
|
if ((ret = discovery(buf, &box)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 discovery contained box failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
srs_assert(box);
|
|
|
|
if ((ret = box->decode(buf)) != ERROR_SUCCESS) {
|
|
|
|
srs_freep(box);
|
|
|
|
srs_error("MP4 decode contained box failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
boxes.push_back(box);
|
|
|
|
left -= box->sz();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::nb_header()
|
|
|
|
{
|
|
|
|
int size = 8;
|
|
|
|
if (smallsize == SRS_MP4_USE_LARGE_SIZE) {
|
|
|
|
size += 8;
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
if (type == SrsMp4BoxTypeUUID) {
|
2017-02-01 13:57:32 +00:00
|
|
|
size += 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
// Only support 31bits size.
|
|
|
|
if (sz() > 0x7fffffff) {
|
|
|
|
ret = ERROR_MP4_BOX_OVERFLOW;
|
|
|
|
srs_error("MP4 box size overflow 31bits, size=%"PRId64". ret=%d", sz(), ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int size = nb_header();
|
|
|
|
if (!buf->require(size)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 box require %d bytes space. ret=%d", size, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_4bytes(smallsize);
|
|
|
|
if (smallsize == SRS_MP4_USE_LARGE_SIZE) {
|
|
|
|
buf->write_8bytes(largesize);
|
|
|
|
}
|
|
|
|
buf->write_4bytes(type);
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
if (type == SrsMp4BoxTypeUUID) {
|
2017-02-01 13:57:32 +00:00
|
|
|
buf->write_bytes((char*)usertype, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Box::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if (!buf->require(8)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 box require 8 bytes space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
smallsize = (uint32_t)buf->read_4bytes();
|
2017-02-02 14:02:39 +00:00
|
|
|
type = (SrsMp4BoxType)buf->read_4bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
|
|
|
|
if (smallsize == SRS_MP4_EOF_SIZE) {
|
|
|
|
srs_warn("MP4 box EOF.");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (smallsize == SRS_MP4_USE_LARGE_SIZE) {
|
|
|
|
if (!buf->require(8)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 box require 8 bytes space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
largesize = (uint64_t)buf->read_8bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only support 31bits size.
|
|
|
|
if (sz() > 0x7fffffff) {
|
|
|
|
ret = ERROR_MP4_BOX_OVERFLOW;
|
|
|
|
srs_error("MP4 box size overflow 31bits, size=%"PRId64". ret=%d", sz(), ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
if (type == SrsMp4BoxTypeUUID) {
|
2017-02-01 13:57:32 +00:00
|
|
|
if (!buf->require(16)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 box requires 16 bytes space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
usertype = new uint8_t[16];
|
|
|
|
buf->read_bytes((char*)usertype, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The left required size, determined by the default version(0).
|
|
|
|
int lrsz = nb_header() - SrsMp4Box::nb_header();
|
|
|
|
if (!buf->require(lrsz)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 box requires %d bytes space. ret=%d", lrsz, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-01-27 12:54:05 +00:00
|
|
|
}
|
|
|
|
|
2017-01-28 11:32:43 +00:00
|
|
|
SrsMp4FullBox::SrsMp4FullBox()
|
2017-01-27 12:54:05 +00:00
|
|
|
{
|
2017-01-28 11:32:43 +00:00
|
|
|
version = 0;
|
|
|
|
flags = 0;
|
2017-01-27 12:54:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4FullBox::~SrsMp4FullBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4FullBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4Box::nb_header() + 1 + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4FullBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_1bytes(version);
|
|
|
|
buf->write_3bytes(flags);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4FullBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!buf->require(4)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 full box requires 4 bytes space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = (uint32_t)buf->read_4bytes();
|
|
|
|
|
|
|
|
version = (uint8_t)((flags >> 24) & 0xff);
|
|
|
|
flags &= 0x00ffffff;
|
|
|
|
|
|
|
|
// The left required size, determined by the version.
|
|
|
|
int lrsz = nb_header() - SrsMp4FullBox::nb_header();
|
|
|
|
if (!buf->require(lrsz)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 full box requires %d bytes space. ret=%d", lrsz, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-28 11:32:43 +00:00
|
|
|
SrsMp4FileTypeBox::SrsMp4FileTypeBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeFTYP;
|
2017-01-28 11:32:43 +00:00
|
|
|
nb_compatible_brands = 0;
|
|
|
|
compatible_brands = NULL;
|
2017-02-02 14:02:39 +00:00
|
|
|
major_brand = SrsMp4BoxBrandForbidden;
|
|
|
|
minor_version = 0;
|
2017-01-28 11:32:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4FileTypeBox::~SrsMp4FileTypeBox()
|
|
|
|
{
|
|
|
|
srs_freepa(compatible_brands);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4FileTypeBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4Box::nb_header() + 8 + nb_compatible_brands * 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4FileTypeBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_4bytes(major_brand);
|
|
|
|
buf->write_4bytes(minor_version);
|
|
|
|
|
|
|
|
for (int i = 0; i < nb_compatible_brands; i++) {
|
2017-02-02 14:02:39 +00:00
|
|
|
buf->write_4bytes(compatible_brands[i]);
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4FileTypeBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
major_brand = (SrsMp4BoxBrand)buf->read_4bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
minor_version = buf->read_4bytes();
|
|
|
|
|
|
|
|
// Compatible brands to the end of the box.
|
|
|
|
int left = left_space(buf);
|
|
|
|
|
|
|
|
if (left > 0) {
|
|
|
|
nb_compatible_brands = left / 4;
|
2017-02-02 14:02:39 +00:00
|
|
|
compatible_brands = new SrsMp4BoxBrand[nb_compatible_brands];
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; left > 0; i++, left -= 4){
|
2017-02-02 14:02:39 +00:00
|
|
|
compatible_brands[i] = (SrsMp4BoxBrand)buf->read_4bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4MediaDataBox::SrsMp4MediaDataBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeMDAT;
|
2017-01-29 07:45:27 +00:00
|
|
|
data = NULL;
|
|
|
|
nb_data = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MediaDataBox::~SrsMp4MediaDataBox()
|
|
|
|
{
|
|
|
|
srs_freepa(data);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4MediaDataBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4Box::nb_header() + nb_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MediaDataBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nb_data) {
|
|
|
|
buf->write_bytes((char*)data, nb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MediaDataBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int left = left_space(buf);
|
|
|
|
if (left) {
|
|
|
|
data = new uint8_t[left];
|
|
|
|
buf->read_bytes((char*)data, left);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4FreeSpaceBox::SrsMp4FreeSpaceBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeFREE; // 'free' or 'skip'
|
2017-02-01 13:57:32 +00:00
|
|
|
data = NULL;
|
|
|
|
nb_data = 0;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4FreeSpaceBox::~SrsMp4FreeSpaceBox()
|
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
srs_freepa(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4FreeSpaceBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4Box::nb_header() + nb_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4FreeSpaceBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nb_data) {
|
|
|
|
buf->write_bytes((char*)data, nb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4FreeSpaceBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int left = left_space(buf);
|
|
|
|
if (left) {
|
|
|
|
data = new uint8_t[left];
|
|
|
|
buf->read_bytes((char*)data, left);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-01-28 11:32:43 +00:00
|
|
|
SrsMp4MovieBox::SrsMp4MovieBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeMOOV;
|
2017-01-28 11:32:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MovieBox::~SrsMp4MovieBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-03 06:57:28 +00:00
|
|
|
SrsMp4MovieHeaderBox* SrsMp4MovieBox::mvhd()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeMVHD);
|
|
|
|
return dynamic_cast<SrsMp4MovieHeaderBox*>(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4TrackBox* SrsMp4MovieBox::video()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < boxes.size(); i++) {
|
|
|
|
SrsMp4Box* box = boxes.at(i);
|
|
|
|
if (box->type == SrsMp4BoxTypeTRAK) {
|
|
|
|
SrsMp4TrackBox* trak = dynamic_cast<SrsMp4TrackBox*>(box);
|
|
|
|
if ((trak->track_type() & SrsMp4TrackTypeVideo) == SrsMp4TrackTypeVideo) {
|
|
|
|
return trak;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4TrackBox* SrsMp4MovieBox::audio()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < boxes.size(); i++) {
|
|
|
|
SrsMp4Box* box = boxes.at(i);
|
|
|
|
if (box->type == SrsMp4BoxTypeTRAK) {
|
|
|
|
SrsMp4TrackBox* trak = dynamic_cast<SrsMp4TrackBox*>(box);
|
|
|
|
if ((trak->track_type() & SrsMp4TrackTypeAudio) == SrsMp4TrackTypeAudio) {
|
|
|
|
return trak;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
int SrsMp4MovieBox::nb_vide_tracks()
|
|
|
|
{
|
|
|
|
int nb_tracks = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < boxes.size(); i++) {
|
|
|
|
SrsMp4Box* box = boxes.at(i);
|
|
|
|
if (box->type == SrsMp4BoxTypeTRAK) {
|
|
|
|
SrsMp4TrackBox* trak = dynamic_cast<SrsMp4TrackBox*>(box);
|
|
|
|
if ((trak->track_type() & SrsMp4TrackTypeVideo) == SrsMp4TrackTypeVideo) {
|
|
|
|
nb_tracks++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nb_tracks;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MovieBox::nb_soun_tracks()
|
|
|
|
{
|
|
|
|
int nb_tracks = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < boxes.size(); i++) {
|
|
|
|
SrsMp4Box* box = boxes.at(i);
|
|
|
|
if (box->type == SrsMp4BoxTypeTRAK) {
|
|
|
|
SrsMp4TrackBox* trak = dynamic_cast<SrsMp4TrackBox*>(box);
|
|
|
|
if ((trak->track_type() & SrsMp4TrackTypeAudio) == SrsMp4TrackTypeAudio) {
|
|
|
|
nb_tracks++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nb_tracks;
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:20:33 +00:00
|
|
|
int SrsMp4MovieBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4Box::nb_header();
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MovieBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
return SrsMp4Box::encode_header(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MovieBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
return SrsMp4Box::decode_header(buf);
|
|
|
|
}
|
|
|
|
|
2017-01-28 11:32:43 +00:00
|
|
|
SrsMp4MovieHeaderBox::SrsMp4MovieHeaderBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeMVHD;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
|
|
|
rate = 0x00010000; // typically 1.0
|
|
|
|
volume = 0x0100; // typically, full volume
|
|
|
|
reserved0 = 0;
|
|
|
|
reserved1 = 0;
|
|
|
|
|
|
|
|
int32_t v[] = {0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
|
|
|
|
memcpy(matrix, v, 36);
|
|
|
|
|
|
|
|
memset(pre_defined, 0, 24);
|
2017-01-28 11:32:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MovieHeaderBox::~SrsMp4MovieHeaderBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-03 06:57:28 +00:00
|
|
|
uint64_t SrsMp4MovieHeaderBox::duration()
|
|
|
|
{
|
|
|
|
return duration_in_tbn * 1000 / timescale;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4MovieHeaderBox::nb_header()
|
|
|
|
{
|
|
|
|
int size = SrsMp4FullBox::nb_header();
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
size += 8+8+4+8;
|
|
|
|
} else {
|
|
|
|
size += 4+4+4+4;
|
|
|
|
}
|
|
|
|
|
|
|
|
size += 4+2+2+8+36+24+4;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MovieHeaderBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
buf->write_8bytes(creation_time);
|
|
|
|
buf->write_8bytes(modification_time);
|
|
|
|
buf->write_4bytes(timescale);
|
2017-02-03 06:57:28 +00:00
|
|
|
buf->write_8bytes(duration_in_tbn);
|
2017-02-01 13:57:32 +00:00
|
|
|
} else {
|
|
|
|
buf->write_4bytes((uint32_t)creation_time);
|
|
|
|
buf->write_4bytes((uint32_t)modification_time);
|
|
|
|
buf->write_4bytes(timescale);
|
2017-02-03 06:57:28 +00:00
|
|
|
buf->write_4bytes((uint32_t)duration_in_tbn);
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_4bytes(rate);
|
|
|
|
buf->write_2bytes(volume);
|
|
|
|
buf->write_2bytes(reserved0);
|
|
|
|
buf->write_8bytes(reserved1);
|
|
|
|
for (int i = 0; i < 9; i++) {
|
|
|
|
buf->write_4bytes(matrix[i]);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
|
|
buf->write_4bytes(pre_defined[i]);
|
|
|
|
}
|
|
|
|
buf->write_4bytes(next_track_ID);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MovieHeaderBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
creation_time = buf->read_8bytes();
|
|
|
|
modification_time = buf->read_8bytes();
|
|
|
|
timescale = buf->read_4bytes();
|
2017-02-03 06:57:28 +00:00
|
|
|
duration_in_tbn = buf->read_8bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
} else {
|
|
|
|
creation_time = buf->read_4bytes();
|
|
|
|
modification_time = buf->read_4bytes();
|
|
|
|
timescale = buf->read_4bytes();
|
2017-02-03 06:57:28 +00:00
|
|
|
duration_in_tbn = buf->read_4bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rate = buf->read_4bytes();
|
2017-02-02 07:10:11 +00:00
|
|
|
volume = buf->read_2bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
buf->skip(2);
|
|
|
|
buf->skip(8);
|
|
|
|
for (int i = 0; i < 9; i++) {
|
|
|
|
matrix[i] = buf->read_4bytes();
|
|
|
|
}
|
|
|
|
buf->skip(24);
|
|
|
|
next_track_ID = buf->read_4bytes();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4TrackBox::SrsMp4TrackBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeTRAK;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4TrackBox::~SrsMp4TrackBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4TrackHeaderBox::SrsMp4TrackHeaderBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeTKHD;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
|
|
|
reserved0 = 0;
|
|
|
|
reserved1 = 0;
|
|
|
|
reserved2 = 0;
|
|
|
|
layer = alternate_group = 0;
|
2017-02-01 13:57:32 +00:00
|
|
|
volume = 0; // if track_is_audio 0x0100 else 0
|
2017-01-29 07:45:27 +00:00
|
|
|
|
|
|
|
int32_t v[] = {0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
|
|
|
|
memcpy(matrix, v, 36);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4TrackHeaderBox::~SrsMp4TrackHeaderBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-03 06:57:28 +00:00
|
|
|
SrsMp4TrackType SrsMp4TrackBox::track_type()
|
|
|
|
{
|
2017-02-03 13:03:26 +00:00
|
|
|
// TODO: Maybe should discovery all mdia boxes.
|
|
|
|
SrsMp4MediaBox* box = mdia();
|
2017-02-03 06:57:28 +00:00
|
|
|
if (!box) {
|
|
|
|
return SrsMp4TrackTypeForbidden;
|
|
|
|
}
|
2017-02-03 13:03:26 +00:00
|
|
|
return box->track_type();
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4TrackHeaderBox* SrsMp4TrackBox::tkhd()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeTKHD);
|
|
|
|
return dynamic_cast<SrsMp4TrackHeaderBox*>(box);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
SrsMp4ChunkOffsetBox* SrsMp4TrackBox::stco()
|
|
|
|
{
|
|
|
|
SrsMp4SampleTableBox* box = stbl();
|
|
|
|
return box? box->stco():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleSizeBox* SrsMp4TrackBox::stsz()
|
|
|
|
{
|
|
|
|
SrsMp4SampleTableBox* box = stbl();
|
|
|
|
return box? box->stsz():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Sample2ChunkBox* SrsMp4TrackBox::stsc()
|
|
|
|
{
|
|
|
|
SrsMp4SampleTableBox* box = stbl();
|
|
|
|
return box? box->stsc():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecodingTime2SampleBox* SrsMp4TrackBox::stts()
|
|
|
|
{
|
|
|
|
SrsMp4SampleTableBox* box = stbl();
|
|
|
|
return box? box->stts():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4CompositionTime2SampleBox* SrsMp4TrackBox::ctts()
|
|
|
|
{
|
|
|
|
SrsMp4SampleTableBox* box = stbl();
|
|
|
|
return box? box->ctts():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SyncSampleBox* SrsMp4TrackBox::stss()
|
|
|
|
{
|
|
|
|
SrsMp4SampleTableBox* box = stbl();
|
|
|
|
return box? box->stss():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MediaHeaderBox* SrsMp4TrackBox::mdhd()
|
|
|
|
{
|
|
|
|
SrsMp4MediaBox* box = mdia();
|
|
|
|
return box? box->mdhd():NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsCodecVideo SrsMp4TrackBox::vide_codec()
|
|
|
|
{
|
|
|
|
SrsMp4SampleDescriptionBox* box = stsd();
|
|
|
|
if (!box) {
|
|
|
|
return SrsCodecVideoForbidden;
|
|
|
|
}
|
2017-02-03 06:57:28 +00:00
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
if (box->entry_count() == 0) {
|
|
|
|
return SrsCodecVideoForbidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleEntry* entry = box->entrie_at(0);
|
|
|
|
switch(entry->type) {
|
|
|
|
case SrsMp4BoxTypeAVC1: return SrsCodecVideoAVC;
|
|
|
|
default: return SrsCodecVideoForbidden;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsCodecAudio SrsMp4TrackBox::soun_codec()
|
|
|
|
{
|
|
|
|
SrsMp4SampleDescriptionBox* box = stsd();
|
|
|
|
if (!box) {
|
|
|
|
return SrsCodecAudioForbidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (box->entry_count() == 0) {
|
|
|
|
return SrsCodecAudioForbidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleEntry* entry = box->entrie_at(0);
|
|
|
|
switch(entry->type) {
|
|
|
|
case SrsMp4BoxTypeMP4A: return SrsCodecAudioAAC;
|
|
|
|
default: return SrsCodecAudioForbidden;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4AvccBox* SrsMp4TrackBox::avcc()
|
|
|
|
{
|
|
|
|
SrsMp4VisualSampleEntry* box = avc1();
|
|
|
|
return box? box->avcC():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecoderSpecificInfo* SrsMp4TrackBox::asc()
|
|
|
|
{
|
|
|
|
SrsMp4AudioSampleEntry* box = mp4a();
|
|
|
|
return box? box->asc():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MediaBox* SrsMp4TrackBox::mdia()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeMDIA);
|
|
|
|
return dynamic_cast<SrsMp4MediaBox*>(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MediaInformationBox* SrsMp4TrackBox::minf()
|
|
|
|
{
|
|
|
|
SrsMp4MediaBox* box = mdia();
|
|
|
|
return box? box->minf():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleTableBox* SrsMp4TrackBox::stbl()
|
|
|
|
{
|
|
|
|
SrsMp4MediaInformationBox* box = minf();
|
|
|
|
return box? box->stbl():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleDescriptionBox* SrsMp4TrackBox::stsd()
|
|
|
|
{
|
|
|
|
SrsMp4SampleTableBox* box = stbl();
|
|
|
|
return box? box->stsd():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4VisualSampleEntry* SrsMp4TrackBox::avc1()
|
|
|
|
{
|
|
|
|
SrsMp4SampleDescriptionBox* box = stsd();
|
|
|
|
return box? box->avc1():NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4AudioSampleEntry* SrsMp4TrackBox::mp4a()
|
|
|
|
{
|
|
|
|
SrsMp4SampleDescriptionBox* box = stsd();
|
|
|
|
return box? box->mp4a():NULL;
|
2017-02-03 06:57:28 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4TrackHeaderBox::nb_header()
|
|
|
|
{
|
|
|
|
int size = SrsMp4FullBox::nb_header();
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
size += 8+8+4+4+8;
|
|
|
|
} else {
|
|
|
|
size += 4+4+4+4+4;
|
|
|
|
}
|
|
|
|
|
|
|
|
size += 8+2+2+2+2+36+4+4;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4TrackHeaderBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
buf->write_8bytes(creation_time);
|
|
|
|
buf->write_8bytes(modification_time);
|
|
|
|
buf->write_4bytes(track_ID);
|
|
|
|
buf->write_4bytes(reserved0);
|
|
|
|
buf->write_8bytes(duration);
|
|
|
|
} else {
|
|
|
|
buf->write_4bytes((uint32_t)creation_time);
|
|
|
|
buf->write_4bytes((uint32_t)modification_time);
|
|
|
|
buf->write_4bytes(track_ID);
|
|
|
|
buf->write_4bytes(reserved0);
|
|
|
|
buf->write_4bytes((uint32_t)duration);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_8bytes(reserved1);
|
|
|
|
buf->write_2bytes(layer);
|
|
|
|
buf->write_2bytes(alternate_group);
|
|
|
|
buf->write_2bytes(volume);
|
|
|
|
buf->write_2bytes(reserved2);
|
|
|
|
for (int i = 0; i < 9; i++) {
|
|
|
|
buf->write_4bytes(matrix[i]);
|
|
|
|
}
|
|
|
|
buf->write_4bytes(width);
|
|
|
|
buf->write_4bytes(height);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4TrackHeaderBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
creation_time = buf->read_8bytes();
|
|
|
|
modification_time = buf->read_8bytes();
|
|
|
|
track_ID = buf->read_4bytes();
|
|
|
|
buf->skip(4);
|
|
|
|
duration = buf->read_8bytes();
|
|
|
|
} else {
|
|
|
|
creation_time = buf->read_4bytes();
|
|
|
|
modification_time = buf->read_4bytes();
|
|
|
|
track_ID = buf->read_4bytes();
|
|
|
|
buf->skip(4);
|
|
|
|
duration = buf->read_4bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->skip(8);
|
|
|
|
layer = buf->read_2bytes();
|
|
|
|
alternate_group = buf->read_2bytes();
|
|
|
|
volume = buf->read_2bytes();
|
|
|
|
buf->skip(2);
|
|
|
|
for (int i = 0; i < 9; i++) {
|
|
|
|
matrix[i] = buf->read_4bytes();
|
|
|
|
}
|
|
|
|
width = buf->read_4bytes();
|
|
|
|
height = buf->read_4bytes();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4EditBox::SrsMp4EditBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeEDTS;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4EditBox::~SrsMp4EditBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4ElstEntry::SrsMp4ElstEntry()
|
|
|
|
{
|
|
|
|
media_rate_fraction = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4EditListBox::SrsMp4EditListBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeELST;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
|
|
|
entry_count = 0;
|
|
|
|
entries = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4EditListBox::~SrsMp4EditListBox()
|
|
|
|
{
|
|
|
|
srs_freepa(entries);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4EditListBox::nb_header()
|
|
|
|
{
|
2017-02-02 07:10:11 +00:00
|
|
|
int size = SrsMp4FullBox::nb_header() + 4;
|
2017-02-01 13:57:32 +00:00
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
if (version == 1) {
|
|
|
|
size += entry_count * (2+2+8+8);
|
|
|
|
} else {
|
|
|
|
size += entry_count * (2+2+4+4);
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4EditListBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
buf->write_4bytes(entry_count);
|
2017-02-01 13:57:32 +00:00
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
SrsMp4ElstEntry& entry = entries[i];
|
2017-02-02 07:10:11 +00:00
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
buf->write_8bytes(entry.segment_duration);
|
|
|
|
buf->write_8bytes(entry.media_time);
|
|
|
|
} else {
|
|
|
|
buf->write_4bytes((uint32_t)entry.segment_duration);
|
|
|
|
buf->write_4bytes((int32_t)entry.media_time);
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
2017-02-02 07:10:11 +00:00
|
|
|
|
|
|
|
buf->write_2bytes(entry.media_rate_integer);
|
|
|
|
buf->write_2bytes(entry.media_rate_fraction);
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4EditListBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
entry_count = buf->read_4bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
if (entry_count > 0) {
|
|
|
|
entries = new SrsMp4ElstEntry[entry_count];
|
|
|
|
}
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
SrsMp4ElstEntry& entry = entries[i];
|
2017-02-02 07:10:11 +00:00
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
entry.segment_duration = buf->read_8bytes();
|
|
|
|
entry.media_time = buf->read_8bytes();
|
|
|
|
} else {
|
|
|
|
entry.segment_duration = buf->read_4bytes();
|
|
|
|
entry.media_time = buf->read_4bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
2017-02-02 07:10:11 +00:00
|
|
|
|
|
|
|
entry.media_rate_integer = buf->read_2bytes();
|
|
|
|
entry.media_rate_fraction = buf->read_2bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4MediaBox::SrsMp4MediaBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeMDIA;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MediaBox::~SrsMp4MediaBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-03 06:57:28 +00:00
|
|
|
SrsMp4TrackType SrsMp4MediaBox::track_type()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeHDLR);
|
|
|
|
if (!box) {
|
|
|
|
return SrsMp4TrackTypeForbidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4HandlerReferenceBox* hdlr = dynamic_cast<SrsMp4HandlerReferenceBox*>(box);
|
|
|
|
if (hdlr->handler_type == SrsMp4HandlerTypeSOUN) {
|
|
|
|
return SrsMp4TrackTypeAudio;
|
|
|
|
} else if (hdlr->handler_type == SrsMp4HandlerTypeVIDE) {
|
|
|
|
return SrsMp4TrackTypeVideo;
|
|
|
|
} else {
|
|
|
|
return SrsMp4TrackTypeForbidden;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
SrsMp4MediaHeaderBox* SrsMp4MediaBox::mdhd()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeMDHD);
|
|
|
|
return dynamic_cast<SrsMp4MediaHeaderBox*>(box);
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsMp4MediaInformationBox* SrsMp4MediaBox::minf()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeMINF);
|
|
|
|
return dynamic_cast<SrsMp4MediaInformationBox*>(box);
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4MediaHeaderBox::SrsMp4MediaHeaderBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeMDHD;
|
2017-02-01 13:57:32 +00:00
|
|
|
language = 0;
|
2017-01-29 07:45:27 +00:00
|
|
|
pre_defined = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MediaHeaderBox::~SrsMp4MediaHeaderBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
uint8_t SrsMp4MediaHeaderBox::language0()
|
|
|
|
{
|
|
|
|
return (language >> 10) & 0x1f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SrsMp4MediaHeaderBox::set_language0(uint8_t v)
|
|
|
|
{
|
|
|
|
language |= uint16_t(v & 0x1f) << 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t SrsMp4MediaHeaderBox::language1()
|
|
|
|
{
|
|
|
|
return (language >> 5) & 0x1f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SrsMp4MediaHeaderBox::set_language1(uint8_t v)
|
|
|
|
{
|
|
|
|
language |= uint16_t(v & 0x1f) << 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t SrsMp4MediaHeaderBox::language2()
|
|
|
|
{
|
|
|
|
return language & 0x1f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SrsMp4MediaHeaderBox::set_language2(uint8_t v)
|
|
|
|
{
|
|
|
|
language |= uint16_t(v & 0x1f);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MediaHeaderBox::nb_header()
|
|
|
|
{
|
|
|
|
int size = SrsMp4FullBox::nb_header();
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
size += 8+8+4+8;
|
|
|
|
} else {
|
|
|
|
size += 4+4+4+4;
|
|
|
|
}
|
|
|
|
|
|
|
|
size += 2+2;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MediaHeaderBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
buf->write_8bytes(creation_time);
|
|
|
|
buf->write_8bytes(modification_time);
|
|
|
|
buf->write_4bytes(timescale);
|
|
|
|
buf->write_8bytes(duration);
|
|
|
|
} else {
|
|
|
|
buf->write_4bytes((uint32_t)creation_time);
|
|
|
|
buf->write_4bytes((uint32_t)modification_time);
|
|
|
|
buf->write_4bytes(timescale);
|
|
|
|
buf->write_4bytes((uint32_t)duration);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_2bytes(language);
|
|
|
|
buf->write_2bytes(pre_defined);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4MediaHeaderBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version == 1) {
|
|
|
|
creation_time = buf->read_8bytes();
|
|
|
|
modification_time = buf->read_8bytes();
|
|
|
|
timescale = buf->read_4bytes();
|
|
|
|
duration = buf->read_8bytes();
|
|
|
|
} else {
|
|
|
|
creation_time = buf->read_4bytes();
|
|
|
|
modification_time = buf->read_4bytes();
|
|
|
|
timescale = buf->read_4bytes();
|
|
|
|
duration = buf->read_4bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
language = buf->read_2bytes();
|
|
|
|
buf->skip(2);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4HandlerReferenceBox::SrsMp4HandlerReferenceBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeHDLR;
|
2017-02-01 13:57:32 +00:00
|
|
|
|
|
|
|
pre_defined = 0;
|
|
|
|
memset(reserved, 0, 12);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4HandlerReferenceBox::~SrsMp4HandlerReferenceBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
bool SrsMp4HandlerReferenceBox::is_video()
|
|
|
|
{
|
2017-02-03 06:57:28 +00:00
|
|
|
return handler_type == SrsMp4HandlerTypeVIDE;
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool SrsMp4HandlerReferenceBox::is_audio()
|
|
|
|
{
|
2017-02-03 06:57:28 +00:00
|
|
|
return handler_type == SrsMp4HandlerTypeSOUN;
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4HandlerReferenceBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4FullBox::nb_header()+4+4+12+srs_mp4_string_length(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4HandlerReferenceBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_4bytes(pre_defined);
|
|
|
|
buf->write_4bytes(handler_type);
|
|
|
|
buf->write_4bytes(reserved[0]);
|
|
|
|
buf->write_4bytes(reserved[1]);
|
|
|
|
buf->write_4bytes(reserved[2]);
|
|
|
|
srs_mp4_string_write(buf, name);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4HandlerReferenceBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->skip(4);
|
2017-02-03 06:57:28 +00:00
|
|
|
handler_type = (SrsMp4HandlerType)buf->read_4bytes();
|
2017-02-01 13:57:32 +00:00
|
|
|
buf->skip(12);
|
|
|
|
|
|
|
|
if ((ret = srs_mp4_string_read(buf, name, left_space(buf))) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 hdlr read string failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MediaInformationBox::SrsMp4MediaInformationBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeMINF;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4MediaInformationBox::~SrsMp4MediaInformationBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsMp4SampleTableBox* SrsMp4MediaInformationBox::stbl()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeSTBL);
|
|
|
|
return dynamic_cast<SrsMp4SampleTableBox*>(box);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4VideoMeidaHeaderBox::SrsMp4VideoMeidaHeaderBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeVMHD;
|
2017-02-01 13:57:32 +00:00
|
|
|
version = 0;
|
|
|
|
flags = 1;
|
|
|
|
|
|
|
|
graphicsmode = 0;
|
|
|
|
memset(opcolor, 0, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4VideoMeidaHeaderBox::~SrsMp4VideoMeidaHeaderBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4VideoMeidaHeaderBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4FullBox::nb_header()+2+6;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4VideoMeidaHeaderBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_2bytes(graphicsmode);
|
|
|
|
buf->write_2bytes(opcolor[0]);
|
|
|
|
buf->write_2bytes(opcolor[1]);
|
|
|
|
buf->write_2bytes(opcolor[2]);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4VideoMeidaHeaderBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
graphicsmode = buf->read_2bytes();
|
|
|
|
opcolor[0] = buf->read_2bytes();
|
|
|
|
opcolor[1] = buf->read_2bytes();
|
|
|
|
opcolor[2] = buf->read_2bytes();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SoundMeidaHeaderBox::SrsMp4SoundMeidaHeaderBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeSMHD;
|
2017-02-01 13:57:32 +00:00
|
|
|
|
|
|
|
reserved = balance = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SoundMeidaHeaderBox::~SrsMp4SoundMeidaHeaderBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SoundMeidaHeaderBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4FullBox::nb_header()+2+2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SoundMeidaHeaderBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_2bytes(balance);
|
|
|
|
buf->write_2bytes(reserved);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SoundMeidaHeaderBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
balance = buf->read_2bytes();
|
|
|
|
buf->skip(2);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DataInformationBox::SrsMp4DataInformationBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeDINF;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DataInformationBox::~SrsMp4DataInformationBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DataEntryBox::SrsMp4DataEntryBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DataEntryBox::~SrsMp4DataEntryBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
SrsMp4DataEntryUrlBox::SrsMp4DataEntryUrlBox()
|
2017-02-01 13:57:32 +00:00
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeURL;
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DataEntryUrlBox::~SrsMp4DataEntryUrlBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4DataEntryUrlBox::nb_header()
|
|
|
|
{
|
|
|
|
// a 24-bit integer with flags; one flag is defined (x000001) which means that the media
|
|
|
|
// data is in the same file as the Movie Box containing this data reference.
|
|
|
|
if (flags == 1) {
|
|
|
|
return SrsMp4FullBox::nb_header();
|
|
|
|
}
|
2017-02-01 13:57:32 +00:00
|
|
|
return SrsMp4FullBox::nb_header()+srs_mp4_string_length(location);
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
int SrsMp4DataEntryUrlBox::encode_header(SrsBuffer* buf)
|
2017-02-01 13:57:32 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
// a 24-bit integer with flags; one flag is defined (x000001) which means that the media
|
|
|
|
// data is in the same file as the Movie Box containing this data reference.
|
|
|
|
if (location.empty()) {
|
|
|
|
flags = 0x01;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
srs_mp4_string_write(buf, location);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
int SrsMp4DataEntryUrlBox::decode_header(SrsBuffer* buf)
|
2017-02-01 13:57:32 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
// a 24-bit integer with flags; one flag is defined (x000001) which means that the media
|
|
|
|
// data is in the same file as the Movie Box containing this data reference.
|
|
|
|
if (flags == 0x01) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
if ((ret = srs_mp4_string_read(buf, location, left_space(buf))) != ERROR_SUCCESS) {
|
2017-02-02 07:10:11 +00:00
|
|
|
srs_error("MP4 url read location failed. ret=%d", ret);
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4DataEntryUrnBox::SrsMp4DataEntryUrnBox()
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeURN;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4DataEntryUrnBox::~SrsMp4DataEntryUrnBox()
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4DataEntryUrnBox::nb_header()
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-02 07:10:11 +00:00
|
|
|
return SrsMp4FullBox::nb_header()+srs_mp4_string_length(location)+srs_mp4_string_length(name);
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4DataEntryUrnBox::encode_header(SrsBuffer* buf)
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
if ((ret = SrsMp4DataEntryBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
srs_mp4_string_write(buf, location);
|
2017-02-01 13:57:32 +00:00
|
|
|
srs_mp4_string_write(buf, name);
|
|
|
|
|
|
|
|
return ret;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4DataEntryUrnBox::decode_header(SrsBuffer* buf)
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4DataEntryBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
if ((ret = srs_mp4_string_read(buf, location, left_space(buf))) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 urn read location failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
if ((ret = srs_mp4_string_read(buf, name, left_space(buf))) != ERROR_SUCCESS) {
|
2017-02-02 07:10:11 +00:00
|
|
|
srs_error("MP4 urn read name failed. ret=%d", ret);
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4DataReferenceBox::SrsMp4DataReferenceBox()
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeDREF;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4DataReferenceBox::~SrsMp4DataReferenceBox()
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
vector<SrsMp4DataEntryBox*>::iterator it;
|
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
SrsMp4DataEntryBox* entry = *it;
|
|
|
|
srs_freep(entry);
|
|
|
|
}
|
|
|
|
entries.clear();
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
uint32_t SrsMp4DataReferenceBox::entry_count()
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
return (uint32_t)entries.size();
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4DataEntryBox* SrsMp4DataReferenceBox::entry_at(int index)
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
return entries.at(index);
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4DataReferenceBox::nb_header()
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
int size = SrsMp4FullBox::nb_header();
|
|
|
|
|
|
|
|
size += 4;
|
|
|
|
|
|
|
|
vector<SrsMp4DataEntryBox*>::iterator it;
|
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
SrsMp4DataEntryBox* entry = *it;
|
|
|
|
size += entry->nb_bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4DataReferenceBox::encode_header(SrsBuffer* buf)
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_4bytes((int32_t)entries.size());
|
|
|
|
|
|
|
|
vector<SrsMp4DataEntryBox*>::iterator it;
|
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
SrsMp4DataEntryBox* entry = *it;
|
|
|
|
if ((ret = entry->encode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4DataReferenceBox::decode_header(SrsBuffer* buf)
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
uint32_t nb_entries = buf->read_4bytes();
|
|
|
|
for (uint32_t i = 0; i < nb_entries; i++) {
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4Box* box = NULL;
|
|
|
|
if ((ret = SrsMp4Box::discovery(buf, &box)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = box->decode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4FullBox* fbox = dynamic_cast<SrsMp4FullBox*>(box);
|
|
|
|
if (fbox) {
|
|
|
|
fbox->version = version;
|
|
|
|
fbox->flags = flags;
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
if (box->type == SrsMp4BoxTypeURL) {
|
2017-02-01 13:57:32 +00:00
|
|
|
entries.push_back(dynamic_cast<SrsMp4DataEntryUrlBox*>(box));
|
2017-02-02 14:02:39 +00:00
|
|
|
} else if (box->type == SrsMp4BoxTypeURN) {
|
2017-02-01 13:57:32 +00:00
|
|
|
entries.push_back(dynamic_cast<SrsMp4DataEntryUrnBox*>(box));
|
|
|
|
} else {
|
|
|
|
srs_freep(box);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleTableBox::SrsMp4SampleTableBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeSTBL;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleTableBox::~SrsMp4SampleTableBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsMp4SampleDescriptionBox* SrsMp4SampleTableBox::stsd()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeSTSD);
|
|
|
|
return dynamic_cast<SrsMp4SampleDescriptionBox*>(box);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
SrsMp4ChunkOffsetBox* SrsMp4SampleTableBox::stco()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeSTCO);
|
|
|
|
return dynamic_cast<SrsMp4ChunkOffsetBox*>(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleSizeBox* SrsMp4SampleTableBox::stsz()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeSTSZ);
|
|
|
|
return dynamic_cast<SrsMp4SampleSizeBox*>(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Sample2ChunkBox* SrsMp4SampleTableBox::stsc()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeSTSC);
|
|
|
|
return dynamic_cast<SrsMp4Sample2ChunkBox*>(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecodingTime2SampleBox* SrsMp4SampleTableBox::stts()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeSTTS);
|
|
|
|
return dynamic_cast<SrsMp4DecodingTime2SampleBox*>(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4CompositionTime2SampleBox* SrsMp4SampleTableBox::ctts()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeCTTS);
|
|
|
|
return dynamic_cast<SrsMp4CompositionTime2SampleBox*>(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SyncSampleBox* SrsMp4SampleTableBox::stss()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeSTSS);
|
|
|
|
return dynamic_cast<SrsMp4SyncSampleBox*>(box);
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4SampleEntry::SrsMp4SampleEntry()
|
|
|
|
{
|
|
|
|
memset(reserved, 0, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleEntry::~SrsMp4SampleEntry()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4SampleEntry::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4Box::nb_header()+6+2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleEntry::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
|
|
buf->write_1bytes(reserved[i]);
|
|
|
|
}
|
|
|
|
buf->write_2bytes(data_reference_index);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleEntry::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->skip(6);
|
|
|
|
data_reference_index = buf->read_2bytes();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4VisualSampleEntry::SrsMp4VisualSampleEntry()
|
|
|
|
{
|
|
|
|
pre_defined0 = 0;
|
|
|
|
reserved0 = 0;
|
|
|
|
reserved1 = 0;
|
|
|
|
memset(pre_defined1, 0, 12);
|
|
|
|
memset(compressorname, 0, 32);
|
|
|
|
frame_count = 1;
|
|
|
|
horizresolution = 0x00480000; // 72 dpi
|
|
|
|
vertresolution = 0x00480000; // 72 dpi
|
|
|
|
depth = 0x0018;
|
|
|
|
pre_defined2 = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4VisualSampleEntry::~SrsMp4VisualSampleEntry()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsMp4AvccBox* SrsMp4VisualSampleEntry::avcC()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeAVCC);
|
|
|
|
return dynamic_cast<SrsMp4AvccBox*>(box);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4VisualSampleEntry::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4SampleEntry::nb_header()+2+2+12+2+2+4+4+4+2+32+2+2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4VisualSampleEntry::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4SampleEntry::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_2bytes(pre_defined0);
|
|
|
|
buf->write_2bytes(reserved0);
|
|
|
|
buf->write_4bytes(pre_defined1[0]);
|
|
|
|
buf->write_4bytes(pre_defined1[1]);
|
|
|
|
buf->write_4bytes(pre_defined1[2]);
|
|
|
|
buf->write_2bytes(width);
|
|
|
|
buf->write_2bytes(height);
|
|
|
|
buf->write_4bytes(horizresolution);
|
|
|
|
buf->write_4bytes(vertresolution);
|
|
|
|
buf->write_4bytes(reserved1);
|
|
|
|
buf->write_2bytes(frame_count);
|
|
|
|
buf->write_bytes(compressorname, 32);
|
|
|
|
buf->write_2bytes(depth);
|
|
|
|
buf->write_2bytes(pre_defined2);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4VisualSampleEntry::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4SampleEntry::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->skip(2);
|
|
|
|
buf->skip(2);
|
|
|
|
buf->skip(12);
|
|
|
|
width = buf->read_2bytes();
|
|
|
|
height = buf->read_2bytes();
|
|
|
|
horizresolution = buf->read_4bytes();
|
|
|
|
vertresolution = buf->read_4bytes();
|
|
|
|
buf->skip(4);
|
|
|
|
frame_count = buf->read_2bytes();
|
|
|
|
buf->read_bytes(compressorname, 32);
|
|
|
|
depth = buf->read_2bytes();
|
|
|
|
buf->skip(2);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
SrsMp4AvccBox::SrsMp4AvccBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeAVCC;
|
2017-02-02 07:10:11 +00:00
|
|
|
nb_config = 0;
|
|
|
|
avc_config = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4AvccBox::~SrsMp4AvccBox()
|
|
|
|
{
|
|
|
|
srs_freepa(avc_config);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4AvccBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4Box::nb_header()+nb_config;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4AvccBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nb_config) {
|
|
|
|
buf->write_bytes((char*)avc_config, nb_config);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4AvccBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
nb_config = left_space(buf);
|
|
|
|
if (nb_config) {
|
|
|
|
avc_config = new uint8_t[nb_config];
|
|
|
|
buf->read_bytes((char*)avc_config, nb_config);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4AudioSampleEntry::SrsMp4AudioSampleEntry()
|
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
reserved0 = 0;
|
2017-01-29 07:45:27 +00:00
|
|
|
pre_defined0 = 0;
|
|
|
|
reserved1 = 0;
|
|
|
|
channelcount = 2;
|
|
|
|
samplesize = 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4AudioSampleEntry::~SrsMp4AudioSampleEntry()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsMp4EsdsBox* SrsMp4AudioSampleEntry::esds()
|
|
|
|
{
|
|
|
|
SrsMp4Box* box = get(SrsMp4BoxTypeESDS);
|
|
|
|
return dynamic_cast<SrsMp4EsdsBox*>(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecoderSpecificInfo* SrsMp4AudioSampleEntry::asc()
|
|
|
|
{
|
|
|
|
SrsMp4EsdsBox* box = esds();
|
|
|
|
return box? box->asc():NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4AudioSampleEntry::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4SampleEntry::nb_header()+8+2+2+2+2+4;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4AudioSampleEntry::encode_header(SrsBuffer* buf)
|
2017-01-29 07:45:27 +00:00
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
if ((ret = SrsMp4SampleEntry::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_8bytes(reserved0);
|
|
|
|
buf->write_2bytes(channelcount);
|
|
|
|
buf->write_2bytes(samplesize);
|
|
|
|
buf->write_2bytes(pre_defined0);
|
|
|
|
buf->write_2bytes(reserved1);
|
|
|
|
buf->write_4bytes(samplerate);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4AudioSampleEntry::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4SampleEntry::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->skip(8);
|
|
|
|
channelcount = buf->read_2bytes();
|
|
|
|
samplesize = buf->read_2bytes();
|
|
|
|
buf->skip(2);
|
|
|
|
buf->skip(2);
|
|
|
|
samplerate = buf->read_4bytes();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 11:05:08 +00:00
|
|
|
SrsMp4BaseDescriptor::SrsMp4BaseDescriptor()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
tag = SrsMp4ESTagESforbidden;
|
|
|
|
vlen = -1;
|
|
|
|
start_pos = 0;
|
2017-02-02 11:05:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4BaseDescriptor::~SrsMp4BaseDescriptor()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
int SrsMp4BaseDescriptor::left_space(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
return vlen - (buf->pos() - start_pos);
|
|
|
|
}
|
|
|
|
|
2017-02-02 11:05:08 +00:00
|
|
|
int SrsMp4BaseDescriptor::nb_bytes()
|
|
|
|
{
|
|
|
|
// 1 byte tag.
|
|
|
|
int size = 1;
|
|
|
|
|
|
|
|
// 1-3 bytes size.
|
2017-02-02 14:02:39 +00:00
|
|
|
int32_t length = vlen = nb_payload(); // bit(8) to bit(32)
|
2017-02-02 11:05:08 +00:00
|
|
|
if (length > 0x1fffff) {
|
|
|
|
size += 4;
|
|
|
|
} else if (length > 0x3fff) {
|
|
|
|
size += 3;
|
|
|
|
} else if (length > 0x7f) {
|
|
|
|
size += 2;
|
|
|
|
} else {
|
|
|
|
size += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// length bytes payload.
|
|
|
|
size += length;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4BaseDescriptor::encode(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
int size = nb_bytes();
|
|
|
|
if (!buf->require(size)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 ES requires %d bytes space. ret=%d", size, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_1bytes((uint8_t)tag);
|
|
|
|
|
|
|
|
// As an expandable class the size of each class instance in bytes is encoded and accessible
|
|
|
|
// through the instance variable sizeOfInstance (see 8.3.3).
|
2017-02-02 14:02:39 +00:00
|
|
|
int32_t length = vlen; // bit(8) to bit(32)
|
|
|
|
srs_assert(vlen > 0);
|
|
|
|
|
|
|
|
if (length > 0x1fffff) {
|
|
|
|
buf->write_1bytes(uint8_t(length>>21)|0x80);
|
|
|
|
}
|
|
|
|
if (length > 0x3fff) {
|
|
|
|
buf->write_1bytes(uint8_t(length>>14)|0x80);
|
|
|
|
}
|
|
|
|
if (length > 0x7f) {
|
|
|
|
buf->write_1bytes(uint8_t(length>>7)|0x80);
|
|
|
|
}
|
|
|
|
buf->write_1bytes(length&0x7f);
|
|
|
|
|
|
|
|
if ((ret = encode_payload(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-02-02 11:05:08 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4BaseDescriptor::decode(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
int size = nb_bytes();
|
|
|
|
if (!buf->require(size)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 ES requires %d bytes space. ret=%d", size, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
tag = (SrsMp4ESTagEs)buf->read_1bytes();
|
|
|
|
|
|
|
|
uint8_t v = 0x80;
|
|
|
|
int32_t length = 0x00;
|
|
|
|
while ((v&0x80) == 0x80) {
|
|
|
|
if (!buf->require(1)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 ES requires 1 byte space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
v = buf->read_1bytes();
|
|
|
|
|
|
|
|
length = (length<<7) | (v&0x7f);
|
|
|
|
}
|
|
|
|
vlen = length;
|
|
|
|
|
|
|
|
if (!buf->require(vlen)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 ES requires %d bytes space. ret=%d", vlen, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_pos = buf->pos();
|
|
|
|
|
|
|
|
if ((ret = decode_payload(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecoderSpecificInfo::SrsMp4DecoderSpecificInfo()
|
|
|
|
{
|
|
|
|
tag = SrsMp4ESTagESDecSpecificInfoTag;
|
|
|
|
nb_asc = 0;
|
|
|
|
asc = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecoderSpecificInfo::~SrsMp4DecoderSpecificInfo()
|
|
|
|
{
|
|
|
|
srs_freepa(asc);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t SrsMp4DecoderSpecificInfo::nb_payload()
|
|
|
|
{
|
|
|
|
return nb_asc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4DecoderSpecificInfo::encode_payload(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if (nb_asc) {
|
|
|
|
buf->write_bytes((char*)asc, nb_asc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4DecoderSpecificInfo::decode_payload(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
nb_asc = vlen;
|
|
|
|
if (nb_asc) {
|
|
|
|
asc = new uint8_t[nb_asc];
|
|
|
|
buf->read_bytes((char*)asc, nb_asc);
|
|
|
|
}
|
|
|
|
|
2017-02-02 11:05:08 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecoderConfigDescriptor::SrsMp4DecoderConfigDescriptor()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
tag = SrsMp4ESTagESDecoderConfigDescrTag;
|
|
|
|
objectTypeIndication = SrsMp4ObjectTypeForbidden;
|
|
|
|
streamType = SrsMp4StreamTypeForbidden;
|
|
|
|
decSpecificInfo = NULL;
|
|
|
|
reserved = 1;
|
2017-02-02 11:05:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecoderConfigDescriptor::~SrsMp4DecoderConfigDescriptor()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
srs_freep(decSpecificInfo);
|
2017-02-02 11:05:08 +00:00
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
int32_t SrsMp4DecoderConfigDescriptor::nb_payload()
|
2017-02-02 11:05:08 +00:00
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
return 12 + (decSpecificInfo? decSpecificInfo->nb_bytes():0);
|
2017-02-02 11:05:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4DecoderConfigDescriptor::encode_payload(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-02 14:02:39 +00:00
|
|
|
|
|
|
|
buf->write_1bytes(objectTypeIndication);
|
|
|
|
|
|
|
|
uint8_t v = reserved;
|
|
|
|
v |= (upStream&0x01)<<1;
|
|
|
|
v |= uint8_t(streamType&0x3f)<<2;
|
|
|
|
buf->write_1bytes(v);
|
|
|
|
|
|
|
|
buf->write_3bytes(bufferSizeDB);
|
|
|
|
buf->write_4bytes(maxBitrate);
|
|
|
|
buf->write_4bytes(avgBitrate);
|
|
|
|
|
2017-02-02 11:05:08 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4DecoderConfigDescriptor::decode_payload(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-02 14:02:39 +00:00
|
|
|
|
|
|
|
objectTypeIndication = (SrsMp4ObjectType)buf->read_1bytes();
|
|
|
|
|
|
|
|
uint8_t v = buf->read_1bytes();
|
|
|
|
upStream = (v>>1) & 0x01;
|
|
|
|
streamType = (SrsMp4StreamType)((v>>2) & 0x3f);
|
|
|
|
reserved = v&0x01;
|
|
|
|
|
|
|
|
bufferSizeDB = buf->read_3bytes();
|
|
|
|
maxBitrate = buf->read_4bytes();
|
|
|
|
avgBitrate = buf->read_4bytes();
|
|
|
|
|
|
|
|
int left = left_space(buf);
|
|
|
|
if (left > 0) {
|
|
|
|
decSpecificInfo = new SrsMp4DecoderSpecificInfo();
|
|
|
|
if ((ret = decSpecificInfo->decode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-02 11:05:08 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SLConfigDescriptor::SrsMp4SLConfigDescriptor()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
tag = SrsMp4ESTagESSLConfigDescrTag;
|
|
|
|
predefined = 2;
|
2017-02-02 11:05:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SLConfigDescriptor::~SrsMp4SLConfigDescriptor()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
int32_t SrsMp4SLConfigDescriptor::nb_payload()
|
2017-02-02 11:05:08 +00:00
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
return 1;
|
2017-02-02 11:05:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SLConfigDescriptor::encode_payload(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-02 14:02:39 +00:00
|
|
|
|
|
|
|
buf->write_1bytes(predefined);
|
|
|
|
|
2017-02-02 11:05:08 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SLConfigDescriptor::decode_payload(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-02 14:02:39 +00:00
|
|
|
|
|
|
|
predefined = buf->read_1bytes();
|
|
|
|
|
|
|
|
// TODO: FIXME: To support complete SL Config.
|
|
|
|
if (predefined != 0x02) {
|
|
|
|
ret = ERROR_MP4_ESDS_SL_Config;
|
|
|
|
srs_error("MP4 illegal ESDS SL Config, predefined=%d. ret=%d", predefined, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 11:05:08 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4ES_Descriptor::SrsMp4ES_Descriptor()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
tag = SrsMp4ESTagESDescrTag;
|
2017-02-02 11:05:08 +00:00
|
|
|
streamDependenceFlag = URL_Flag = OCRstreamFlag = 0;
|
|
|
|
URLlength = 0;
|
|
|
|
URLstring = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4ES_Descriptor::~SrsMp4ES_Descriptor()
|
|
|
|
{
|
|
|
|
srs_freepa(URLstring);
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:02:39 +00:00
|
|
|
int32_t SrsMp4ES_Descriptor::nb_payload()
|
2017-02-02 11:05:08 +00:00
|
|
|
{
|
|
|
|
int size = 2 +1;
|
|
|
|
size += streamDependenceFlag? 2:0;
|
|
|
|
if (URL_Flag) {
|
|
|
|
size += 1 + URLlength;
|
|
|
|
}
|
|
|
|
size += OCRstreamFlag? 2:0;
|
|
|
|
size += decConfigDescr.nb_bytes() +slConfigDescr.nb_bytes();
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4ES_Descriptor::encode_payload(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
buf->write_2bytes(ES_ID);
|
|
|
|
|
|
|
|
uint8_t v = streamPriority & 0x1f;
|
|
|
|
v |= (streamDependenceFlag & 0x01) << 7;
|
|
|
|
v |= (URL_Flag & 0x01) << 6;
|
|
|
|
v |= (OCRstreamFlag & 0x01) << 5;
|
|
|
|
buf->write_1bytes(v);
|
|
|
|
|
|
|
|
if (streamDependenceFlag) {
|
|
|
|
buf->write_2bytes(dependsOn_ES_ID);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (URL_Flag && URLlength) {
|
|
|
|
buf->write_1bytes(URLlength);
|
|
|
|
buf->write_bytes((char*)URLstring, URLlength);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OCRstreamFlag) {
|
|
|
|
buf->write_2bytes(OCR_ES_Id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = decConfigDescr.encode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = slConfigDescr.encode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4ES_Descriptor::decode_payload(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
ES_ID = buf->read_2bytes();
|
|
|
|
|
|
|
|
uint8_t v = buf->read_1bytes();
|
|
|
|
streamPriority = v & 0x1f;
|
|
|
|
streamDependenceFlag = (v >> 7) & 0x01;
|
|
|
|
URL_Flag = (v >> 6) & 0x01;
|
|
|
|
OCRstreamFlag = (v >> 5) & 0x01;
|
|
|
|
|
|
|
|
if (streamDependenceFlag) {
|
|
|
|
if (!buf->require(2)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 ES requires 2 bytes space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
dependsOn_ES_ID = buf->read_2bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (URL_Flag) {
|
|
|
|
if (!buf->require(1)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 ES requires 1 byte space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
URLlength = buf->read_1bytes();
|
|
|
|
|
|
|
|
if (!buf->require(URLlength)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 ES requires %d bytes space. ret=%d", URLlength, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
URLstring = new uint8_t[URLlength];
|
|
|
|
buf->read_bytes((char*)URLstring, URLlength);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OCRstreamFlag) {
|
|
|
|
if (!buf->require(2)) {
|
|
|
|
ret = ERROR_MP4_BOX_REQUIRE_SPACE;
|
|
|
|
srs_error("MP4 ES requires 2 bytes space. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
OCR_ES_Id = buf->read_2bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = decConfigDescr.decode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = slConfigDescr.decode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4EsdsBox::SrsMp4EsdsBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeESDS;
|
2017-02-02 11:05:08 +00:00
|
|
|
es = new SrsMp4ES_Descriptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4EsdsBox::~SrsMp4EsdsBox()
|
|
|
|
{
|
|
|
|
srs_freep(es);
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsMp4DecoderSpecificInfo* SrsMp4EsdsBox::asc()
|
|
|
|
{
|
|
|
|
return es->decConfigDescr.decSpecificInfo;
|
|
|
|
}
|
|
|
|
|
2017-02-02 11:05:08 +00:00
|
|
|
int SrsMp4EsdsBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4FullBox::nb_header() + es->nb_bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4EsdsBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int left = left_space(buf);
|
|
|
|
SrsBuffer buffer(buf->data() + buf->pos(), left);
|
|
|
|
if ((ret = es->encode(&buffer)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->skip(buffer.pos());
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4EsdsBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int left = left_space(buf);
|
|
|
|
SrsBuffer buffer(buf->data() + buf->pos(), left);
|
|
|
|
if ((ret = es->decode(&buffer)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->skip(buffer.pos());
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4SampleDescriptionBox::SrsMp4SampleDescriptionBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeSTSD;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleDescriptionBox::~SrsMp4SampleDescriptionBox()
|
|
|
|
{
|
2017-02-01 13:57:32 +00:00
|
|
|
vector<SrsMp4SampleEntry*>::iterator it;
|
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
SrsMp4SampleEntry* entry = *it;
|
|
|
|
srs_freep(entry);
|
|
|
|
}
|
|
|
|
entries.clear();
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsMp4VisualSampleEntry* SrsMp4SampleDescriptionBox::avc1()
|
|
|
|
{
|
|
|
|
vector<SrsMp4SampleEntry*>::iterator it;
|
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
SrsMp4SampleEntry* entry = *it;
|
|
|
|
if (entry->type == SrsMp4BoxTypeAVC1) {
|
|
|
|
return dynamic_cast<SrsMp4VisualSampleEntry*>(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4AudioSampleEntry* SrsMp4SampleDescriptionBox::mp4a()
|
|
|
|
{
|
|
|
|
vector<SrsMp4SampleEntry*>::iterator it;
|
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
SrsMp4SampleEntry* entry = *it;
|
|
|
|
if (entry->type == SrsMp4BoxTypeMP4A) {
|
|
|
|
return dynamic_cast<SrsMp4AudioSampleEntry*>(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
uint32_t SrsMp4SampleDescriptionBox::entry_count()
|
|
|
|
{
|
|
|
|
return (uint32_t)entries.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleEntry* SrsMp4SampleDescriptionBox::entrie_at(int index)
|
|
|
|
{
|
|
|
|
return entries.at(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleDescriptionBox::nb_header()
|
|
|
|
{
|
|
|
|
int size = SrsMp4FullBox::nb_header();
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
size += 4;
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
vector<SrsMp4SampleEntry*>::iterator it;
|
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
SrsMp4SampleEntry* entry = *it;
|
|
|
|
size += entry->nb_bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleDescriptionBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
buf->write_4bytes(entry_count());
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
vector<SrsMp4SampleEntry*>::iterator it;
|
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
SrsMp4SampleEntry* entry = *it;
|
|
|
|
if ((ret = entry->encode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleDescriptionBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
uint32_t nb_entries = buf->read_4bytes();
|
|
|
|
for (uint32_t i = 0; i < nb_entries; i++) {
|
2017-02-01 13:57:32 +00:00
|
|
|
SrsMp4Box* box = NULL;
|
|
|
|
if ((ret = SrsMp4Box::discovery(buf, &box)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = box->decode(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleEntry* entry = dynamic_cast<SrsMp4SampleEntry*>(box);
|
|
|
|
if (entry) {
|
|
|
|
entries.push_back(entry);
|
|
|
|
} else {
|
|
|
|
srs_freep(box);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SttsEntry::SrsMp4SttsEntry()
|
|
|
|
{
|
|
|
|
sample_count = 0;
|
|
|
|
sample_delta = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecodingTime2SampleBox::SrsMp4DecodingTime2SampleBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeSTTS;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
|
|
|
entry_count = 0;
|
|
|
|
entries = NULL;
|
2017-02-04 14:25:03 +00:00
|
|
|
|
|
|
|
index = count = 0;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4DecodingTime2SampleBox::~SrsMp4DecodingTime2SampleBox()
|
|
|
|
{
|
|
|
|
srs_freepa(entries);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
int SrsMp4DecodingTime2SampleBox::initialize_counter()
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
index = 0;
|
|
|
|
if (index >= entry_count) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_TIMESTAMP;
|
|
|
|
srs_error("MP4 illegal ts, empty stts. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = entries[0].sample_count;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4DecodingTime2SampleBox::on_sample(uint32_t sample_index, SrsMp4SttsEntry** ppentry)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if (sample_index + 1 > count) {
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if (index >= entry_count) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_TIMESTAMP;
|
|
|
|
srs_error("MP4 illegal ts, stts overflow, count=%d. ret=%d", entry_count, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
count += entries[index].sample_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ppentry = &entries[index];
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4DecodingTime2SampleBox::nb_header()
|
|
|
|
{
|
2017-02-02 04:36:48 +00:00
|
|
|
return SrsMp4FullBox::nb_header() + 4 + 8*entry_count;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4DecodingTime2SampleBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
buf->write_4bytes(entry_count);
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
SrsMp4SttsEntry& entry = entries[i];
|
|
|
|
buf->write_4bytes(entry.sample_count);
|
|
|
|
buf->write_4bytes(entry.sample_delta);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4DecodingTime2SampleBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
entry_count = buf->read_4bytes();
|
|
|
|
if (entry_count) {
|
|
|
|
entries = new SrsMp4SttsEntry[entry_count];
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
SrsMp4SttsEntry& entry = entries[i];
|
|
|
|
entry.sample_count = buf->read_4bytes();
|
|
|
|
entry.sample_delta = buf->read_4bytes();
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 07:45:27 +00:00
|
|
|
SrsMp4CttsEntry::SrsMp4CttsEntry()
|
|
|
|
{
|
|
|
|
sample_count = 0;
|
|
|
|
sample_offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4CompositionTime2SampleBox::SrsMp4CompositionTime2SampleBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeCTTS;
|
2017-01-29 07:45:27 +00:00
|
|
|
|
|
|
|
entry_count = 0;
|
|
|
|
entries = NULL;
|
2017-02-04 14:25:03 +00:00
|
|
|
|
|
|
|
index = count = 0;
|
2017-01-29 07:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4CompositionTime2SampleBox::~SrsMp4CompositionTime2SampleBox()
|
|
|
|
{
|
|
|
|
srs_freepa(entries);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
int SrsMp4CompositionTime2SampleBox::initialize_counter()
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
index = 0;
|
|
|
|
if (index >= entry_count) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_TIMESTAMP;
|
|
|
|
srs_error("MP4 illegal ts, empty ctts. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = entries[0].sample_count;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4CompositionTime2SampleBox::on_sample(uint32_t sample_index, SrsMp4CttsEntry** ppentry)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if (sample_index + 1 > count) {
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if (index >= entry_count) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_TIMESTAMP;
|
|
|
|
srs_error("MP4 illegal ts, ctts overflow, count=%d. ret=%d", entry_count, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
count += entries[index].sample_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ppentry = &entries[index];
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4CompositionTime2SampleBox::nb_header()
|
|
|
|
{
|
2017-02-02 04:36:48 +00:00
|
|
|
return SrsMp4FullBox::nb_header() + 4 + 8*entry_count;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4CompositionTime2SampleBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
buf->write_4bytes(entry_count);
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
SrsMp4CttsEntry& entry = entries[i];
|
|
|
|
buf->write_4bytes(entry.sample_count);
|
|
|
|
if (version == 0) {
|
|
|
|
buf->write_4bytes((uint32_t)entry.sample_offset);
|
|
|
|
} else if (version == 1) {
|
|
|
|
buf->write_4bytes((int32_t)entry.sample_offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4CompositionTime2SampleBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
entry_count = buf->read_4bytes();
|
|
|
|
if (entry_count) {
|
|
|
|
entries = new SrsMp4CttsEntry[entry_count];
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
SrsMp4CttsEntry& entry = entries[i];
|
|
|
|
entry.sample_count = buf->read_4bytes();
|
|
|
|
if (version == 0) {
|
|
|
|
entry.sample_offset = (uint32_t)buf->read_4bytes();
|
|
|
|
} else if (version == 1) {
|
|
|
|
entry.sample_offset = (int32_t)buf->read_4bytes();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 09:16:20 +00:00
|
|
|
SrsMp4SyncSampleBox::SrsMp4SyncSampleBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeSTSS;
|
2017-01-29 09:16:20 +00:00
|
|
|
|
|
|
|
entry_count = 0;
|
|
|
|
sample_numbers = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SyncSampleBox::~SrsMp4SyncSampleBox()
|
|
|
|
{
|
|
|
|
srs_freepa(sample_numbers);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:45:52 +00:00
|
|
|
bool SrsMp4SyncSampleBox::is_sync(uint32_t sample_index)
|
|
|
|
{
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
if (sample_index + 1 == sample_numbers[i]) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4SyncSampleBox::nb_header()
|
|
|
|
{
|
2017-02-02 04:36:48 +00:00
|
|
|
return SrsMp4FullBox::nb_header() +4 +4*entry_count;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SyncSampleBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
buf->write_4bytes(entry_count);
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
uint32_t sample_number = sample_numbers[i];
|
|
|
|
buf->write_4bytes(sample_number);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SyncSampleBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
entry_count = buf->read_4bytes();
|
|
|
|
if (entry_count > 0) {
|
|
|
|
sample_numbers = new uint32_t[entry_count];
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
2017-02-02 07:10:11 +00:00
|
|
|
sample_numbers[i] = buf->read_4bytes();
|
2017-02-02 04:36:48 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 09:16:20 +00:00
|
|
|
SrsMp4StscEntry::SrsMp4StscEntry()
|
|
|
|
{
|
|
|
|
first_chunk = 0;
|
|
|
|
samples_per_chunk = 0;
|
|
|
|
sample_description_index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Sample2ChunkBox::SrsMp4Sample2ChunkBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeSTSC;
|
2017-01-29 09:16:20 +00:00
|
|
|
|
|
|
|
entry_count = 0;
|
|
|
|
entries = NULL;
|
2017-02-04 14:45:52 +00:00
|
|
|
index = 0;
|
2017-01-29 09:16:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Sample2ChunkBox::~SrsMp4Sample2ChunkBox()
|
|
|
|
{
|
|
|
|
srs_freepa(entries);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:45:52 +00:00
|
|
|
void SrsMp4Sample2ChunkBox::initialize_counter()
|
|
|
|
{
|
|
|
|
index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4StscEntry* SrsMp4Sample2ChunkBox::on_chunk(uint32_t chunk_index)
|
|
|
|
{
|
|
|
|
// Last chunk?
|
|
|
|
if (index >= entry_count - 1) {
|
|
|
|
return &entries[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move next chunk?
|
|
|
|
if (chunk_index + 1 >= entries[index + 1].first_chunk) {
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
return &entries[index];
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4Sample2ChunkBox::nb_header()
|
|
|
|
{
|
2017-02-02 04:36:48 +00:00
|
|
|
return SrsMp4FullBox::nb_header() +4 + 12*entry_count;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Sample2ChunkBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
buf->write_4bytes(entry_count);
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
SrsMp4StscEntry& entry = entries[i];
|
|
|
|
buf->write_4bytes(entry.first_chunk);
|
|
|
|
buf->write_4bytes(entry.samples_per_chunk);
|
|
|
|
buf->write_4bytes(entry.sample_description_index);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Sample2ChunkBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
entry_count = buf->read_4bytes();
|
|
|
|
if (entry_count) {
|
|
|
|
entries = new SrsMp4StscEntry[entry_count];
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
SrsMp4StscEntry& entry = entries[i];
|
|
|
|
entry.first_chunk = buf->read_4bytes();
|
|
|
|
entry.samples_per_chunk = buf->read_4bytes();
|
|
|
|
entry.sample_description_index = buf->read_4bytes();
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 09:16:20 +00:00
|
|
|
SrsMp4ChunkOffsetBox::SrsMp4ChunkOffsetBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeSTCO;
|
2017-01-29 09:16:20 +00:00
|
|
|
|
|
|
|
entry_count = 0;
|
|
|
|
entries = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4ChunkOffsetBox::~SrsMp4ChunkOffsetBox()
|
|
|
|
{
|
|
|
|
srs_freepa(entries);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4ChunkOffsetBox::nb_header()
|
|
|
|
{
|
2017-02-02 04:36:48 +00:00
|
|
|
return SrsMp4FullBox::nb_header() +4 +4*entry_count;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4ChunkOffsetBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
buf->write_4bytes(entry_count);
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
buf->write_4bytes(entries[i]);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4ChunkOffsetBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
entry_count = buf->read_4bytes();
|
|
|
|
if (entry_count) {
|
|
|
|
entries = new uint32_t[entry_count];
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
entries[i] = buf->read_4bytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4ChunkLargeOffsetBox::SrsMp4ChunkLargeOffsetBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeCO64;
|
2017-02-02 04:36:48 +00:00
|
|
|
|
|
|
|
entry_count = 0;
|
|
|
|
entries = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4ChunkLargeOffsetBox::~SrsMp4ChunkLargeOffsetBox()
|
|
|
|
{
|
|
|
|
srs_freepa(entries);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4ChunkLargeOffsetBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4FullBox::nb_header() +4 +8*entry_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4ChunkLargeOffsetBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->write_4bytes(entry_count);
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
buf->write_8bytes(entries[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4ChunkLargeOffsetBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry_count = buf->read_4bytes();
|
|
|
|
if (entry_count) {
|
|
|
|
entries = new uint64_t[entry_count];
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < entry_count; i++) {
|
|
|
|
entries[i] = buf->read_8bytes();
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-29 09:16:20 +00:00
|
|
|
SrsMp4SampleSizeBox::SrsMp4SampleSizeBox()
|
|
|
|
{
|
2017-02-02 14:02:39 +00:00
|
|
|
type = SrsMp4BoxTypeSTSZ;
|
2017-01-29 09:16:20 +00:00
|
|
|
|
|
|
|
sample_size = sample_count = 0;
|
|
|
|
entry_sizes = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleSizeBox::~SrsMp4SampleSizeBox()
|
|
|
|
{
|
|
|
|
srs_freepa(entry_sizes);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:45:52 +00:00
|
|
|
int SrsMp4SampleSizeBox::get_sample_size(uint32_t sample_index, uint32_t* psample_size)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if (sample_size != 0) {
|
|
|
|
*psample_size = sample_size;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sample_index >= sample_count) {
|
|
|
|
ret = ERROR_MP4_MOOV_OVERFLOW;
|
|
|
|
srs_error("MP4 stsz overflow, sample_count=%d. ret=%d", sample_count, ret);
|
|
|
|
}
|
|
|
|
*psample_size = entry_sizes[sample_index];
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
int SrsMp4SampleSizeBox::nb_header()
|
|
|
|
{
|
2017-02-02 04:36:48 +00:00
|
|
|
int size = SrsMp4FullBox::nb_header() +4+4;
|
|
|
|
if (sample_size == 0) {
|
|
|
|
size += 4*sample_count;
|
|
|
|
}
|
|
|
|
return size;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleSizeBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
buf->write_4bytes(sample_size);
|
|
|
|
buf->write_4bytes(sample_count);
|
|
|
|
for (uint32_t i = 0; i < sample_count && sample_size == 0; i++) {
|
|
|
|
buf->write_4bytes(entry_sizes[i]);
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleSizeBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4FullBox::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 04:36:48 +00:00
|
|
|
sample_size = buf->read_4bytes();
|
|
|
|
sample_count = buf->read_4bytes();
|
|
|
|
if (sample_size == 0) {
|
|
|
|
entry_sizes = new uint32_t[sample_count];
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < sample_count && sample_size == 0; i++) {
|
|
|
|
entry_sizes[i] = buf->read_4bytes();
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 14:20:33 +00:00
|
|
|
SrsMp4UserDataBox::SrsMp4UserDataBox()
|
|
|
|
{
|
|
|
|
type = SrsMp4BoxTypeUDTA;
|
|
|
|
nb_data = 0;
|
|
|
|
data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4UserDataBox::~SrsMp4UserDataBox()
|
|
|
|
{
|
|
|
|
srs_freepa(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4UserDataBox::nb_header()
|
|
|
|
{
|
|
|
|
return SrsMp4Box::nb_header()+nb_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4UserDataBox::encode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::encode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nb_data) {
|
|
|
|
buf->write_bytes((char*)data, nb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4UserDataBox::decode_header(SrsBuffer* buf)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if ((ret = SrsMp4Box::decode_header(buf)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
nb_data = left_space(buf);
|
|
|
|
if (nb_data) {
|
|
|
|
data = new uint8_t[nb_data];
|
|
|
|
buf->read_bytes((char*)data, nb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
SrsMp4Sample::SrsMp4Sample()
|
|
|
|
{
|
|
|
|
type = SrsCodecFlvTagForbidden;
|
|
|
|
offset = 0;
|
|
|
|
index = 0;
|
|
|
|
dts = pts = 0;
|
|
|
|
nb_data = 0;
|
|
|
|
data = NULL;
|
|
|
|
frame_type = SrsCodecVideoAVCFrameForbidden;
|
|
|
|
tbn = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Sample::~SrsMp4Sample()
|
|
|
|
{
|
|
|
|
srs_freepa(data);
|
|
|
|
}
|
|
|
|
|
2017-02-05 01:15:46 +00:00
|
|
|
uint32_t SrsMp4Sample::dts_ms()
|
2017-02-04 14:25:03 +00:00
|
|
|
{
|
|
|
|
return (uint32_t)(dts * 1000 / tbn);
|
|
|
|
}
|
|
|
|
|
2017-02-05 01:15:46 +00:00
|
|
|
uint32_t SrsMp4Sample::pts_ms()
|
2017-02-04 14:25:03 +00:00
|
|
|
{
|
|
|
|
return (uint32_t)(pts * 1000 / tbn);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleManager::SrsMp4SampleManager()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4SampleManager::~SrsMp4SampleManager()
|
|
|
|
{
|
|
|
|
vector<SrsMp4Sample*>::iterator it;
|
|
|
|
for (it = samples.begin(); it != samples.end(); ++it) {
|
|
|
|
SrsMp4Sample* sample = *it;
|
|
|
|
srs_freep(sample);
|
|
|
|
}
|
|
|
|
samples.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleManager::load(SrsMp4MovieBox* moov)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
map<uint64_t, SrsMp4Sample*> tses;
|
|
|
|
|
|
|
|
// Load samples from moov, merge to temp samples.
|
|
|
|
if ((ret = do_load(tses, moov)) != ERROR_SUCCESS) {
|
|
|
|
map<uint64_t, SrsMp4Sample*>::iterator it;
|
|
|
|
for (it = tses.begin(); it != tses.end(); ++it) {
|
|
|
|
SrsMp4Sample* sample = it->second;
|
|
|
|
srs_freep(sample);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dumps temp samples.
|
|
|
|
if (true) {
|
|
|
|
map<uint64_t, SrsMp4Sample*>::iterator it;
|
|
|
|
for (it = tses.begin(); it != tses.end(); ++it) {
|
|
|
|
SrsMp4Sample* sample = it->second;
|
|
|
|
samples.push_back(sample);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-05 01:15:46 +00:00
|
|
|
SrsMp4Sample* SrsMp4SampleManager::at(uint32_t index)
|
|
|
|
{
|
|
|
|
if (index >= samples.size() - 1) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return samples.at(index);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
int SrsMp4SampleManager::do_load(map<uint64_t, SrsMp4Sample*>& tses, SrsMp4MovieBox* moov)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
SrsMp4TrackBox* vide = moov->video();
|
|
|
|
if (vide) {
|
|
|
|
SrsMp4MediaHeaderBox* mdhd = vide->mdhd();
|
|
|
|
SrsMp4TrackType tt = vide->track_type();
|
|
|
|
SrsMp4ChunkOffsetBox* stco = vide->stco();
|
|
|
|
SrsMp4SampleSizeBox* stsz = vide->stsz();
|
|
|
|
SrsMp4Sample2ChunkBox* stsc = vide->stsc();
|
|
|
|
SrsMp4DecodingTime2SampleBox* stts = vide->stts();
|
|
|
|
// The composition time to sample table is optional and must only be present if DT and CT differ for any samples.
|
|
|
|
SrsMp4CompositionTime2SampleBox* ctts = vide->ctts();
|
|
|
|
// If the sync sample box is not present, every sample is a sync sample.
|
|
|
|
SrsMp4SyncSampleBox* stss = vide->stss();
|
|
|
|
|
|
|
|
if (!mdhd || !stco || !stsz || !stsc || !stts) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_TRACK;
|
|
|
|
srs_error("MP4 illegal track, empty mdhd/stco/stsz/stsc/stts, type=%d. ret=%d", tt, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = load_trak(tses, SrsCodecFlvTagVideo, mdhd, stco, stsz, stsc, stts, ctts, stss)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4TrackBox* soun = moov->audio();
|
|
|
|
if (soun) {
|
|
|
|
SrsMp4MediaHeaderBox* mdhd = soun->mdhd();
|
|
|
|
SrsMp4TrackType tt = soun->track_type();
|
|
|
|
SrsMp4ChunkOffsetBox* stco = soun->stco();
|
|
|
|
SrsMp4SampleSizeBox* stsz = soun->stsz();
|
|
|
|
SrsMp4Sample2ChunkBox* stsc = soun->stsc();
|
|
|
|
SrsMp4DecodingTime2SampleBox* stts = soun->stts();
|
|
|
|
|
|
|
|
if (!mdhd || !stco || !stsz || !stsc || !stts) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_TRACK;
|
|
|
|
srs_error("MP4 illegal track, empty mdhd/stco/stsz/stsc/stts, type=%d. ret=%d", tt, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = load_trak(tses, SrsCodecFlvTagAudio, mdhd, stco, stsz, stsc, stts, NULL, NULL)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4SampleManager::load_trak(map<uint64_t, SrsMp4Sample*>& tses, SrsCodecFlvTag tt,
|
|
|
|
SrsMp4MediaHeaderBox* mdhd, SrsMp4ChunkOffsetBox* stco, SrsMp4SampleSizeBox* stsz, SrsMp4Sample2ChunkBox* stsc,
|
|
|
|
SrsMp4DecodingTime2SampleBox* stts, SrsMp4CompositionTime2SampleBox* ctts, SrsMp4SyncSampleBox* stss)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
// Samples per chunk.
|
2017-02-04 14:45:52 +00:00
|
|
|
stsc->initialize_counter();
|
2017-02-04 14:25:03 +00:00
|
|
|
|
|
|
|
// DTS box.
|
|
|
|
if ((ret = stts->initialize_counter()) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// CTS/PTS box.
|
|
|
|
if (ctts && (ret = ctts->initialize_counter()) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Sample* previous = NULL;
|
|
|
|
|
|
|
|
// For each chunk offset.
|
2017-02-04 14:45:52 +00:00
|
|
|
for (uint32_t ci = 0; ci < stco->entry_count; ci++) {
|
2017-02-05 12:27:37 +00:00
|
|
|
// The sample offset relative in chunk.
|
|
|
|
uint32_t sample_relative_offset = 0;
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
// Find how many samples from stsc.
|
2017-02-04 14:45:52 +00:00
|
|
|
SrsMp4StscEntry* stsc_entry = stsc->on_chunk(ci);
|
|
|
|
for (uint32_t i = 0; i < stsc_entry->samples_per_chunk; i++) {
|
2017-02-04 14:25:03 +00:00
|
|
|
SrsMp4Sample* sample = new SrsMp4Sample();
|
|
|
|
sample->type = tt;
|
|
|
|
sample->index = (previous? previous->index+1:0);
|
|
|
|
sample->tbn = mdhd->timescale;
|
2017-02-05 12:27:37 +00:00
|
|
|
sample->offset = stco->entries[ci] + sample_relative_offset;
|
2017-02-04 14:25:03 +00:00
|
|
|
|
2017-02-04 14:45:52 +00:00
|
|
|
uint32_t sample_size = 0;
|
|
|
|
if ((ret = stsz->get_sample_size(sample->index, &sample_size)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
2017-02-04 14:25:03 +00:00
|
|
|
}
|
2017-02-05 12:27:37 +00:00
|
|
|
sample_relative_offset += sample_size;
|
2017-02-04 14:25:03 +00:00
|
|
|
|
|
|
|
SrsMp4SttsEntry* stts_entry = NULL;
|
|
|
|
if ((ret = stts->on_sample(sample->index, &stts_entry)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (previous) {
|
|
|
|
sample->pts = sample->dts = previous->dts + stts_entry->sample_delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4CttsEntry* ctts_entry = NULL;
|
|
|
|
if (ctts && (ret = ctts->on_sample(sample->index, &ctts_entry)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (ctts_entry) {
|
|
|
|
sample->pts = sample->dts + ctts_entry->sample_offset;
|
|
|
|
}
|
2017-02-04 14:45:52 +00:00
|
|
|
|
|
|
|
if (tt == SrsCodecFlvTagVideo) {
|
|
|
|
if (!stss || stss->is_sync(sample->index)) {
|
|
|
|
sample->frame_type = SrsCodecVideoAVCFrameKeyFrame;
|
|
|
|
} else {
|
|
|
|
sample->frame_type = SrsCodecVideoAVCFrameInterFrame;
|
|
|
|
}
|
|
|
|
}
|
2017-02-04 14:25:03 +00:00
|
|
|
|
2017-02-05 01:15:46 +00:00
|
|
|
// Only set the sample size, read data from io when needed.
|
|
|
|
sample->nb_data = sample_size;
|
|
|
|
sample->data = NULL;
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
previous = sample;
|
|
|
|
tses[sample->offset] = sample;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check total samples.
|
|
|
|
if (previous && previous->index + 1 != stsz->sample_count) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_SAMPLES;
|
|
|
|
srs_error("MP4 illegal samples count, expect=%d, actual=%d. ret=%d", stsz->sample_count, previous->index + 1, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2017-02-02 07:10:11 +00:00
|
|
|
|
2017-01-31 12:43:48 +00:00
|
|
|
SrsMp4Decoder::SrsMp4Decoder()
|
|
|
|
{
|
2017-02-03 14:49:19 +00:00
|
|
|
rsio = NULL;
|
|
|
|
brand = SrsMp4BoxBrandForbidden;
|
2017-02-02 07:10:11 +00:00
|
|
|
buf = new char[SRS_MP4_BUF_SIZE];
|
2017-02-01 13:57:32 +00:00
|
|
|
stream = new SrsSimpleStream();
|
2017-02-04 06:57:07 +00:00
|
|
|
vcodec = SrsCodecVideoForbidden;
|
|
|
|
acodec = SrsCodecAudioForbidden;
|
|
|
|
nb_asc = nb_avcc = 0;
|
|
|
|
pasc = pavcc = NULL;
|
|
|
|
asc_written = avcc_written = false;
|
|
|
|
sample_rate = SrsCodecAudioSampleRateForbidden;
|
|
|
|
sound_bits = SrsCodecAudioSampleSizeForbidden;
|
|
|
|
channels = SrsCodecAudioSoundTypeForbidden;
|
2017-02-04 14:25:03 +00:00
|
|
|
samples = new SrsMp4SampleManager();
|
2017-02-05 01:15:46 +00:00
|
|
|
current_index = 0;
|
|
|
|
current_offset = 0;
|
2017-01-31 12:43:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4Decoder::~SrsMp4Decoder()
|
|
|
|
{
|
2017-02-02 07:10:11 +00:00
|
|
|
srs_freepa(buf);
|
2017-02-01 13:57:32 +00:00
|
|
|
srs_freep(stream);
|
2017-02-04 06:57:07 +00:00
|
|
|
srs_freepa(pasc);
|
|
|
|
srs_freepa(pavcc);
|
2017-02-04 14:25:03 +00:00
|
|
|
srs_freep(samples);
|
2017-01-31 12:43:48 +00:00
|
|
|
}
|
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
int SrsMp4Decoder::initialize(ISrsReadSeeker* rs)
|
2017-01-31 12:43:48 +00:00
|
|
|
{
|
2017-01-31 13:36:51 +00:00
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
srs_assert(rs);
|
|
|
|
rsio = rs;
|
2017-01-31 12:43:48 +00:00
|
|
|
|
2017-02-04 06:57:07 +00:00
|
|
|
// For mdat before moov, we must reset the offset to the mdat.
|
2017-02-03 14:49:19 +00:00
|
|
|
off_t offset = -1;
|
|
|
|
|
|
|
|
while (true) {
|
2017-02-02 07:10:11 +00:00
|
|
|
SrsMp4Box* box = NULL;
|
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
if ((ret = load_next_box(&box, 0)) != ERROR_SUCCESS) {
|
2017-02-02 07:10:11 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
if (box->is_ftyp()) {
|
|
|
|
SrsMp4FileTypeBox* ftyp = dynamic_cast<SrsMp4FileTypeBox*>(box);
|
|
|
|
if ((ret = parse_ftyp(ftyp)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
2017-02-03 14:49:19 +00:00
|
|
|
} else if (box->is_mdat()) {
|
|
|
|
off_t cur = 0;
|
|
|
|
if ((ret = rsio->lseek(0, SEEK_CUR, &cur)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
offset = off_t(cur - box->sz());
|
|
|
|
} else if (box->is_moov()) {
|
|
|
|
SrsMp4MovieBox* moov = dynamic_cast<SrsMp4MovieBox*>(box);
|
|
|
|
if ((ret = parse_moov(moov)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
break;
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
2017-02-03 14:49:19 +00:00
|
|
|
|
|
|
|
srs_freep(box);
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
if (brand == SrsMp4BoxBrandForbidden) {
|
|
|
|
ret = ERROR_MP4_BOX_ILLEGAL_SCHEMA;
|
|
|
|
srs_error("MP4 missing ftyp. ret=%d", ret);
|
|
|
|
return ret;
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
2017-02-04 06:57:07 +00:00
|
|
|
// Set the offset to the mdat.
|
2017-02-03 14:49:19 +00:00
|
|
|
if (offset >= 0) {
|
2017-02-05 01:15:46 +00:00
|
|
|
return rsio->lseek(offset, SEEK_SET, ¤t_offset);
|
2017-02-03 14:49:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-04 06:57:07 +00:00
|
|
|
int SrsMp4Decoder::read_sample(SrsMp4HandlerType* pht,
|
|
|
|
uint16_t* pft, uint16_t* pct, uint32_t* pdts, uint32_t* ppts, uint8_t** psample, uint32_t* pnb_sample)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
if (!avcc_written && nb_avcc) {
|
|
|
|
avcc_written = true;
|
|
|
|
*pdts = *ppts = 0;
|
|
|
|
*pht = SrsMp4HandlerTypeVIDE;
|
|
|
|
|
|
|
|
uint32_t nb_sample = *pnb_sample = nb_avcc;
|
|
|
|
uint8_t* sample = *psample = new uint8_t[nb_sample];
|
|
|
|
memcpy(sample, pavcc, nb_sample);
|
|
|
|
|
|
|
|
*pft = SrsCodecVideoAVCFrameKeyFrame;
|
|
|
|
*pct = SrsCodecVideoAVCTypeSequenceHeader;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!asc_written && nb_asc) {
|
|
|
|
asc_written = true;
|
|
|
|
*pdts = *ppts = 0;
|
|
|
|
*pht = SrsMp4HandlerTypeSOUN;
|
|
|
|
|
|
|
|
uint32_t nb_sample = *pnb_sample = nb_asc;
|
|
|
|
uint8_t* sample = *psample = new uint8_t[nb_sample];
|
|
|
|
memcpy(sample, pasc, nb_sample);
|
|
|
|
|
|
|
|
*pft = 0x00;
|
|
|
|
*pct = SrsCodecAudioTypeSequenceHeader;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-05 01:15:46 +00:00
|
|
|
SrsMp4Sample* ps = samples->at(current_index++);
|
|
|
|
if (!ps) {
|
|
|
|
return ERROR_SYSTEM_FILE_EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ps->type == SrsCodecFlvTagVideo) {
|
|
|
|
*pht = SrsMp4HandlerTypeVIDE;
|
|
|
|
*pct = SrsCodecVideoAVCTypeNALU;
|
|
|
|
} else {
|
|
|
|
*pht = SrsMp4HandlerTypeSOUN;
|
|
|
|
*pct = SrsCodecAudioTypeRawData;
|
|
|
|
}
|
|
|
|
*pdts = ps->dts_ms();
|
|
|
|
*ppts = ps->pts_ms();
|
|
|
|
*pft = ps->frame_type;
|
|
|
|
|
|
|
|
// Read sample from io, for we never preload the samples(too large).
|
|
|
|
if (ps->offset != current_offset) {
|
|
|
|
if ((ret = rsio->lseek(ps->offset, SEEK_SET, ¤t_offset)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t nb_sample = ps->nb_data;
|
|
|
|
uint8_t* sample = new uint8_t[nb_sample];
|
|
|
|
// TODO: FIXME: Use fully read.
|
|
|
|
if ((ret = rsio->read(sample, nb_sample, NULL)) != ERROR_SUCCESS) {
|
|
|
|
srs_freepa(sample);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
*psample = sample;
|
|
|
|
*pnb_sample = nb_sample;
|
|
|
|
current_offset += nb_sample;
|
|
|
|
|
2017-02-04 06:57:07 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
int SrsMp4Decoder::parse_ftyp(SrsMp4FileTypeBox* ftyp)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
// File Type Box (ftyp)
|
|
|
|
bool legal_brand = false;
|
|
|
|
static SrsMp4BoxBrand legal_brands[] = {
|
|
|
|
SrsMp4BoxBrandISOM, SrsMp4BoxBrandISO2, SrsMp4BoxBrandAVC1, SrsMp4BoxBrandMP41
|
|
|
|
};
|
|
|
|
for (int i = 0; i < sizeof(legal_brands)/sizeof(SrsMp4BoxBrand); i++) {
|
|
|
|
if (ftyp->major_brand == legal_brands[i]) {
|
|
|
|
legal_brand = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!legal_brand) {
|
|
|
|
ret = ERROR_MP4_BOX_ILLEGAL_BRAND;
|
|
|
|
srs_error("MP4 brand is illegal, brand=%d. ret=%d", ftyp->major_brand, ret);
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
brand = ftyp->major_brand;
|
|
|
|
|
|
|
|
return ret;
|
2017-02-02 14:20:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Decoder::parse_moov(SrsMp4MovieBox* moov)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-03 06:57:28 +00:00
|
|
|
|
|
|
|
SrsMp4MovieHeaderBox* mvhd = moov->mvhd();
|
|
|
|
if (!mvhd) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_MOOV;
|
|
|
|
srs_error("MP4 missing mvhd. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsMp4TrackBox* vide = moov->video();
|
|
|
|
SrsMp4TrackBox* soun = moov->audio();
|
|
|
|
if (!vide && !soun) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_MOOV;
|
|
|
|
srs_error("MP4 missing audio and video track. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-04 06:57:07 +00:00
|
|
|
SrsMp4AudioSampleEntry* mp4a = soun? soun->mp4a():NULL;
|
|
|
|
if (mp4a) {
|
|
|
|
uint32_t sr = mp4a->samplerate>>16;
|
|
|
|
if (sr >= 44100) {
|
|
|
|
sample_rate = SrsCodecAudioSampleRate44100;
|
|
|
|
} else if (sr >= 22050) {
|
|
|
|
sample_rate = SrsCodecAudioSampleRate22050;
|
|
|
|
} else if (sr >= 11025) {
|
|
|
|
sample_rate = SrsCodecAudioSampleRate11025;
|
|
|
|
} else {
|
|
|
|
sample_rate = SrsCodecAudioSampleRate5512;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp4a->samplesize == 16) {
|
|
|
|
sound_bits = SrsCodecAudioSampleSize16bit;
|
|
|
|
} else {
|
|
|
|
sound_bits = SrsCodecAudioSampleSize8bit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp4a->channelcount == 2) {
|
|
|
|
channels = SrsCodecAudioSoundTypeStereo;
|
|
|
|
} else {
|
|
|
|
channels = SrsCodecAudioSoundTypeMono;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
SrsMp4AvccBox* avcc = vide? vide->avcc():NULL;
|
|
|
|
SrsMp4DecoderSpecificInfo* asc = soun? soun->asc():NULL;
|
|
|
|
if (vide && !avcc) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_MOOV;
|
|
|
|
srs_error("MP4 missing video sequence header. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (soun && !asc) {
|
|
|
|
ret = ERROR_MP4_ILLEGAL_MOOV;
|
|
|
|
srs_error("MP4 missing audio sequence header. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-04 06:57:07 +00:00
|
|
|
vcodec = vide?vide->vide_codec():SrsCodecVideoForbidden;
|
|
|
|
acodec = soun?soun->soun_codec():SrsCodecAudioForbidden;
|
|
|
|
|
|
|
|
if (avcc && avcc->nb_config) {
|
|
|
|
nb_avcc = avcc->nb_config;
|
|
|
|
pavcc = new uint8_t[nb_avcc];
|
|
|
|
memcpy(pavcc, avcc->avc_config, nb_avcc);
|
|
|
|
}
|
|
|
|
if (asc && asc->nb_asc) {
|
|
|
|
nb_asc = asc->nb_asc;
|
|
|
|
pasc = new uint8_t[nb_asc];
|
|
|
|
memcpy(pasc, asc->asc, nb_asc);
|
|
|
|
}
|
|
|
|
|
2017-02-04 14:25:03 +00:00
|
|
|
// Build the samples structure from moov.
|
|
|
|
if ((ret = samples->load(moov)) != ERROR_SUCCESS) {
|
|
|
|
srs_error("MP4 load samples failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-03 13:03:26 +00:00
|
|
|
stringstream ss;
|
|
|
|
ss << "dur=" << mvhd->duration() << "ms";
|
|
|
|
// video codec.
|
|
|
|
ss << ", vide=" << moov->nb_vide_tracks() << "("
|
2017-02-04 06:57:07 +00:00
|
|
|
<< srs_codec_video2str(vcodec) << "," << nb_avcc << "BSH"
|
|
|
|
<< ")";
|
2017-02-03 13:03:26 +00:00
|
|
|
// audio codec.
|
|
|
|
ss << ", soun=" << moov->nb_soun_tracks() << "("
|
2017-02-04 06:57:07 +00:00
|
|
|
<< srs_codec_audio2str(acodec) << "," << nb_asc << "BSH"
|
|
|
|
<< "," << srs_codec_audio_channels2str(channels)
|
|
|
|
<< "," << srs_codec_audio_samplesize2str(sound_bits)
|
|
|
|
<< "," << srs_codec_audio_samplerate2str(sample_rate)
|
|
|
|
<< ")";
|
2017-02-03 13:03:26 +00:00
|
|
|
|
|
|
|
srs_trace("MP4 moov %s", ss.str().c_str());
|
2017-02-03 06:57:28 +00:00
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
int SrsMp4Decoder::load_next_box(SrsMp4Box** ppbox, uint32_t required_box_type)
|
2017-02-01 13:57:32 +00:00
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
while (true) {
|
|
|
|
SrsMp4Box* box = NULL;
|
|
|
|
if ((ret = do_load_next_box(&box, required_box_type)) != ERROR_SUCCESS) {
|
|
|
|
srs_freep(box);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!required_box_type || box->type == required_box_type) {
|
|
|
|
*ppbox = box;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
srs_freep(box);
|
2017-02-01 13:57:32 +00:00
|
|
|
}
|
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SrsMp4Decoder::do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_type)
|
|
|
|
{
|
|
|
|
int ret = ERROR_SUCCESS;
|
2017-02-01 13:57:32 +00:00
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
SrsMp4Box* box = NULL;
|
2017-02-01 13:57:32 +00:00
|
|
|
while (true) {
|
2017-02-02 07:10:11 +00:00
|
|
|
uint64_t required = box? box->sz():4;
|
2017-02-01 13:57:32 +00:00
|
|
|
while (stream->length() < required) {
|
|
|
|
ssize_t nread;
|
2017-02-03 14:49:19 +00:00
|
|
|
if ((ret = rsio->read(buf, SRS_MP4_BUF_SIZE, &nread)) != ERROR_SUCCESS) {
|
2017-02-01 13:57:32 +00:00
|
|
|
srs_error("MP4 load failed, nread=%d, required=%d. ret=%d", nread, required, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
srs_assert(nread > 0);
|
|
|
|
stream->append(buf, (int)nread);
|
|
|
|
}
|
|
|
|
|
|
|
|
SrsBuffer* buffer = new SrsBuffer(stream->bytes(), stream->length());
|
|
|
|
SrsAutoFree(SrsBuffer, buffer);
|
|
|
|
|
|
|
|
// Discovery the box with basic header.
|
2017-02-02 07:10:11 +00:00
|
|
|
if (!box && (ret = SrsMp4Box::discovery(buffer, &box)) != ERROR_SUCCESS) {
|
2017-02-01 13:57:32 +00:00
|
|
|
if (ret == ERROR_MP4_BOX_REQUIRE_SPACE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
srs_error("MP4 load box failed. ret=%d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
// For mdat, skip the content.
|
|
|
|
if (box->is_mdat()) {
|
|
|
|
// Never load the mdat box content, instead we skip it, for it's too large.
|
|
|
|
// The demuxer use seeker to read sample one by one.
|
|
|
|
if (box->is_mdat()) {
|
|
|
|
int offset = (int)(box->sz() - stream->length());
|
|
|
|
if (offset < 0) {
|
|
|
|
stream->erase(stream->length() + offset);
|
|
|
|
} else {
|
|
|
|
stream->erase(stream->length());
|
|
|
|
}
|
|
|
|
if (offset > 0 && (ret = rsio->lseek(offset, SEEK_CUR, NULL)) != ERROR_SUCCESS) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Util we can demux the whole box.
|
|
|
|
if (!buffer->require((int)box->sz())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode the matched box or any box is matched.
|
|
|
|
if (!required_box_type || box->type == required_box_type) {
|
|
|
|
ret = box->decode(buffer);
|
|
|
|
}
|
2017-02-02 07:10:11 +00:00
|
|
|
|
2017-02-03 14:49:19 +00:00
|
|
|
// Remove the consumed bytes.
|
|
|
|
stream->erase((int)box->sz());
|
2017-02-02 07:10:11 +00:00
|
|
|
}
|
2017-02-01 13:57:32 +00:00
|
|
|
|
2017-02-02 07:10:11 +00:00
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
|
|
srs_freep(box);
|
|
|
|
} else {
|
|
|
|
*ppbox = box;
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:57:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-01-31 13:36:51 +00:00
|
|
|
return ret;
|
2017-01-31 12:43:48 +00:00
|
|
|
}
|
|
|
|
|