Skip to content

Commit

Permalink
Refine mp4, extract mp4 box reader.
Browse files Browse the repository at this point in the history
  • Loading branch information
winlinvip committed Apr 16, 2017
1 parent c9bed5a commit 6ee85ae
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 60 deletions.
178 changes: 120 additions & 58 deletions trunk/src/kernel/srs_kernel_mp4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3911,11 +3911,109 @@ int SrsMp4SampleManager::load_trak(map<uint64_t, SrsMp4Sample*>& tses, SrsFrameT
return ret;
}

SrsMp4BoxReader::SrsMp4BoxReader()
{
rsio = NULL;
buf = new char[SRS_MP4_BUF_SIZE];
}

SrsMp4BoxReader::~SrsMp4BoxReader()
{
srs_freepa(buf);
}

int SrsMp4BoxReader::initialize(ISrsReadSeeker* rs)
{
rsio = rs;

return ERROR_SUCCESS;
}

int SrsMp4BoxReader::read(SrsSimpleStream* stream, SrsMp4Box** ppbox)
{
int ret = ERROR_SUCCESS;

SrsMp4Box* box = NULL;
while (true) {
// For the first time to read the box, maybe it's a basic box which is only 4bytes header.
// When we disconvery the real box, we know the size of the whole box, then read again and decode it.
uint64_t required = box? box->sz():4;

// For mdat box, we only requires to decode the header.
if (box && box->is_mdat()) {
required = box->sz_header();
}

// Fill the stream util we can discovery box.
while (stream->length() < (int)required) {
ssize_t nread;
if ((ret = rsio->read(buf, SRS_MP4_BUF_SIZE, &nread)) != ERROR_SUCCESS) {
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.
if (!box && (ret = SrsMp4Box::discovery(buffer, &box)) != ERROR_SUCCESS) {
if (ret == ERROR_MP4_BOX_REQUIRE_SPACE) {
continue;
}
srs_error("MP4 load box failed. ret=%d", ret);
return ret;
}

// When box is discoveried, check whether we can demux the whole box.
// For mdat, only the header is required.
required = (box->is_mdat()? box->sz_header():box->sz());
if (!buffer->require((int)required)) {
continue;
}

if (ret != ERROR_SUCCESS) {
srs_freep(box);
} else {
*ppbox = box;
}

break;
}

return ret;
}

int SrsMp4BoxReader::skip(SrsMp4Box* box, SrsSimpleStream* stream)
{
int ret = ERROR_SUCCESS;

// For mdat, always skip the content.
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 {
// Remove the consumed bytes.
stream->erase((int)box->sz());
}

return ret;
}

SrsMp4Decoder::SrsMp4Decoder()
{
rsio = NULL;
brand = SrsMp4BoxBrandForbidden;
buf = new char[SRS_MP4_BUF_SIZE];
stream = new SrsSimpleStream();
vcodec = SrsVideoCodecIdForbidden;
acodec = SrsAudioCodecIdForbidden;
Expand All @@ -3924,13 +4022,14 @@ SrsMp4Decoder::SrsMp4Decoder()
sound_bits = SrsAudioSampleBitsForbidden;
channels = SrsAudioChannelsForbidden;
samples = new SrsMp4SampleManager();
br = new SrsMp4BoxReader();
current_index = 0;
current_offset = 0;
}

SrsMp4Decoder::~SrsMp4Decoder()
{
srs_freepa(buf);
srs_freep(br);
srs_freep(stream);
srs_freep(samples);
}
Expand All @@ -3942,6 +4041,10 @@ int SrsMp4Decoder::initialize(ISrsReadSeeker* rs)
srs_assert(rs);
rsio = rs;

if ((ret = br->initialize(rs)) != ERROR_SUCCESS) {
return ret;
}

// For mdat before moov, we must reset the offset to the mdat.
off_t offset = -1;

Expand Down Expand Up @@ -3997,7 +4100,7 @@ int SrsMp4Decoder::read_sample(SrsMp4HandlerType* pht, uint16_t* pft, uint16_t*
*pdts = *ppts = 0;
*pht = SrsMp4HandlerTypeVIDE;

uint32_t nb_sample = *pnb_sample = pavcc.size();
uint32_t nb_sample = *pnb_sample = (uint32_t)pavcc.size();
uint8_t* sample = *psample = new uint8_t[nb_sample];
memcpy(sample, &pavcc[0], nb_sample);

Expand All @@ -4012,7 +4115,7 @@ int SrsMp4Decoder::read_sample(SrsMp4HandlerType* pht, uint16_t* pft, uint16_t*
*pdts = *ppts = 0;
*pht = SrsMp4HandlerTypeSOUN;

uint32_t nb_sample = *pnb_sample = pasc.size();
uint32_t nb_sample = *pnb_sample = (uint32_t)pasc.size();
uint8_t* sample = *psample = new uint8_t[nb_sample];
memcpy(sample, &pasc[0], nb_sample);

Expand Down Expand Up @@ -4165,15 +4268,15 @@ int SrsMp4Decoder::parse_moov(SrsMp4MovieBox* moov)
ss << "dur=" << mvhd->duration() << "ms";
// video codec.
ss << ", vide=" << moov->nb_vide_tracks() << "("
<< srs_video_codec_id2str(vcodec) << "," << pavcc.size() << "BSH"
<< ")";
<< srs_video_codec_id2str(vcodec) << "," << pavcc.size() << "BSH"
<< ")";
// audio codec.
ss << ", soun=" << moov->nb_soun_tracks() << "("
<< srs_audio_codec_id2str(acodec) << "," << pasc.size() << "BSH"
<< "," << srs_audio_channels2str(channels)
<< "," << srs_audio_sample_bits2str(sound_bits)
<< "," << srs_audio_sample_rate2str(sample_rate)
<< ")";
<< srs_audio_codec_id2str(acodec) << "," << pasc.size() << "BSH"
<< "," << srs_audio_channels2str(channels)
<< "," << srs_audio_sample_bits2str(sound_bits)
<< "," << srs_audio_sample_rate2str(sample_rate)
<< ")";

srs_trace("MP4 moov %s", ss.str().c_str());

Expand Down Expand Up @@ -4205,44 +4308,15 @@ int SrsMp4Decoder::do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_typ
{
int ret = ERROR_SUCCESS;

SrsMp4Box* box = NULL;
while (true) {
// For the first time to read the box, maybe it's a basic box which is only 4bytes header.
// When we disconvery the real box, we know the size of the whole box, then read again and decode it.
uint64_t required = box? box->sz():4;
// For mdat box, we only requires to decode the header.
if (box && box->is_mdat()) {
required = box->sz_header();
}
while (stream->length() < (int)required) {
ssize_t nread;
if ((ret = rsio->read(buf, SRS_MP4_BUF_SIZE, &nread)) != ERROR_SUCCESS) {
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);
SrsMp4Box* box = NULL;

// Discovery the box with basic header.
if (!box && (ret = SrsMp4Box::discovery(buffer, &box)) != ERROR_SUCCESS) {
if (ret == ERROR_MP4_BOX_REQUIRE_SPACE) {
continue;
}
srs_error("MP4 load box failed. ret=%d", ret);
if ((ret = br->read(stream, &box)) != ERROR_SUCCESS) {
return ret;
}

// When box is discoveried, check whether we can demux the whole box.
// For mdat, only the header is required.
required = (box->is_mdat()? box->sz_header():box->sz());
if (!buffer->require((int)required)) {
continue;
}
SrsBuffer* buffer = new SrsBuffer(stream->bytes(), stream->length());
SrsAutoFree(SrsBuffer, buffer);

// Decode the box:
// 1. Any box, when no box type is required.
Expand All @@ -4252,20 +4326,8 @@ int SrsMp4Decoder::do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_typ
ret = box->decode(buffer);
}

// For mdat, always skip the content.
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 {
// Remove the consumed bytes.
stream->erase((int)box->sz());
if (ret == ERROR_SUCCESS) {
ret = br->skip(box, stream);
}

if (ret != ERROR_SUCCESS) {
Expand Down
26 changes: 24 additions & 2 deletions trunk/src/kernel/srs_kernel_mp4.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,28 @@ class SrsMp4SampleManager
SrsMp4DecodingTime2SampleBox* stts, SrsMp4CompositionTime2SampleBox* ctts, SrsMp4SyncSampleBox* stss);
};

/**
* The MP4 box reader, to get the RAW boxes without decode.
* @remark For mdat box, we only decode the header, then skip the data.
*/
class SrsMp4BoxReader
{
private:
ISrsReadSeeker* rsio;
// The temporary buffer to read from buffer.
char* buf;
public:
SrsMp4BoxReader();
virtual ~SrsMp4BoxReader();
public:
virtual int initialize(ISrsReadSeeker* rs);
public:
// Read a MP4 box to pbox, the stream is fill with the bytes of box to decode.
virtual int read(SrsSimpleStream* stream, SrsMp4Box** ppbox);
// Skip the box from stream, and skip in file if need.
virtual int skip(SrsMp4Box* box, SrsSimpleStream* stream);
};

/**
* The MP4 demuxer.
*/
Expand Down Expand Up @@ -1676,11 +1698,11 @@ class SrsMp4Decoder
// Underlayer reader and seeker.
// @remark The demuxer must use seeker for general MP4 to seek the moov.
ISrsReadSeeker* rsio;
// The MP4 box reader.
SrsMp4BoxReader* br;
// The stream used to demux the boxes.
// TODO: FIXME: refine for performance issue.
SrsSimpleStream* stream;
// The temporary buffer to read from buffer.
char* buf;
public:
SrsMp4Decoder();
virtual ~SrsMp4Decoder();
Expand Down

0 comments on commit 6ee85ae

Please sign in to comment.