for #738, use reader and seeker for mp4 demuxer to seek for general mp4(ftyp-mdat-moov).

pull/763/head
winlin 8 years ago
parent bbee16e4db
commit 9d21a8bb33

@ -280,7 +280,7 @@ void SrsHttpFileReader::skip(int64_t /*size*/)
{
}
int64_t SrsHttpFileReader::lseek(int64_t offset)
int64_t SrsHttpFileReader::seek2(int64_t offset)
{
return offset;
}
@ -324,4 +324,10 @@ int SrsHttpFileReader::read(void* buf, size_t count, ssize_t* pnread)
return ret;
}
int SrsHttpFileReader::lseek(off_t offset, int whence, off_t* seeked)
{
// TODO: FIXME: Use HTTP range for seek.
return ERROR_SUCCESS;
}
#endif

@ -120,14 +120,11 @@ public:
virtual bool is_open();
virtual int64_t tellg();
virtual void skip(int64_t size);
virtual int64_t lseek(int64_t offset);
virtual int64_t seek2(int64_t offset);
virtual int64_t filesize();
public:
/**
* read from file.
* @param pnread the output nb_read, NULL to ignore.
*/
virtual int read(void* buf, size_t count, ssize_t* pnread);
virtual int lseek(off_t offset, int whence, off_t* seeked);
};
#endif

@ -370,7 +370,7 @@ int SrsFlvSegment::update_flv_metadata()
}
// update the flesize.
fs->lseek(filesize_offset);
fs->seek2(filesize_offset);
if ((ret = fs->write(buf, SrsAmf0Size::number(), NULL)) != ERROR_SUCCESS) {
return ret;
}
@ -385,13 +385,13 @@ int SrsFlvSegment::update_flv_metadata()
}
// update the duration
fs->lseek(duration_offset);
fs->seek2(duration_offset);
if ((ret = fs->write(buf, SrsAmf0Size::number(), NULL)) != ERROR_SUCCESS) {
return ret;
}
// reset the offset.
fs->lseek(cur);
fs->seek2(cur);
return ret;
}

@ -131,7 +131,7 @@ int SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r
}
// write body.
if ((ret = ffd.lseek(offset)) != ERROR_SUCCESS) {
if ((ret = ffd.seek2(offset)) != ERROR_SUCCESS) {
return ret;
}
@ -186,7 +186,7 @@ int SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r
w->header()->set("Content-Range", content_range.str());
// write body.
fs.lseek(start);
fs.seek2(start);
// send data
if ((ret = copy(w, &fs, r, (int)left)) != ERROR_SUCCESS) {

@ -248,9 +248,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_MP4_BOX_ILLEGAL_SCHEMA 3072
#define ERROR_MP4_BOX_STRING 3073
#define ERROR_MP4_BOX_ILLEGAL_BRAND 3074
#define ERROR_MP4_NOT_NON_SEEKABLE 3075
#define ERROR_MP4_ESDS_SL_Config 3076
#define ERROR_MP4_ILLEGAL_MOOV 3077
#define ERROR_MP4_ESDS_SL_Config 3075
#define ERROR_MP4_ILLEGAL_MOOV 3076
///////////////////////////////////////////////////////
// HTTP/StreamCaster/KAFKA protocol error.

@ -117,7 +117,7 @@ bool SrsFileWriter::is_open()
return fd > 0;
}
void SrsFileWriter::lseek(int64_t offset)
void SrsFileWriter::seek2(int64_t offset)
{
::lseek(fd, (off_t)offset, SEEK_SET);
}
@ -167,6 +167,19 @@ int SrsFileWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwrite)
return ret;
}
int SrsFileWriter::lseek(off_t offset, int whence, off_t* seeked)
{
off_t sk = ::lseek(fd, offset, whence);
if (sk < 0) {
return ERROR_SYSTEM_FILE_SEEK;
}
if (seeked) {
*seeked = sk;
}
return ERROR_SUCCESS;
}
SrsFileReader::SrsFileReader()
{
fd = -1;
@ -231,7 +244,7 @@ void SrsFileReader::skip(int64_t size)
::lseek(fd, (off_t)size, SEEK_CUR);
}
int64_t SrsFileReader::lseek(int64_t offset)
int64_t SrsFileReader::seek2(int64_t offset)
{
return (int64_t)::lseek(fd, (off_t)offset, SEEK_SET);
}
@ -268,3 +281,16 @@ int SrsFileReader::read(void* buf, size_t count, ssize_t* pnread)
return ret;
}
int SrsFileReader::lseek(off_t offset, int whence, off_t* seeked)
{
off_t sk = ::lseek(fd, offset, whence);
if (sk < 0) {
return ERROR_SYSTEM_FILE_SEEK;
}
if (seeked) {
*seeked = sk;
}
return ERROR_SUCCESS;
}

@ -41,7 +41,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* file writer, to write to file.
*/
class SrsFileWriter : public ISrsWriter
class SrsFileWriter : public ISrsWriteSeeker
{
private:
std::string path;
@ -67,25 +67,19 @@ public:
virtual void close();
public:
virtual bool is_open();
virtual void lseek(int64_t offset);
virtual void seek2(int64_t offset);
virtual int64_t tellg();
// Interface ISrsWriteSeeker
public:
/**
* write to file.
* @param pnwrite the output nb_write, NULL to ignore.
*/
virtual int write(void* buf, size_t count, ssize_t* pnwrite);
/**
* for the HTTP FLV, to writev to improve performance.
* @see https://github.com/ossrs/srs/issues/405
*/
virtual int writev(const iovec* iov, int iovcnt, ssize_t* pnwrite);
virtual int lseek(off_t offset, int whence, off_t* seeked);
};
/**
* file reader, to read from file.
*/
class SrsFileReader : public ISrsReader
class SrsFileReader : public ISrsReadSeeker
{
private:
std::string path;
@ -109,14 +103,12 @@ public:
virtual bool is_open();
virtual int64_t tellg();
virtual void skip(int64_t size);
virtual int64_t lseek(int64_t offset);
virtual int64_t seek2(int64_t offset);
virtual int64_t filesize();
// Interface ISrsReadSeeker
public:
/**
* read from file.
* @param pnread the output nb_read, NULL to ignore.
*/
virtual int read(void* buf, size_t count, ssize_t* pnread);
virtual int lseek(off_t offset, int whence, off_t* seeked);
};
#endif

@ -949,7 +949,7 @@ int SrsFlvVodStreamDecoder::read_sequence_header_summary(int64_t* pstart, int* p
// seek to the sequence header start offset.
if (av_sequence_offset_start > 0) {
reader->lseek(av_sequence_offset_start);
reader->seek2(av_sequence_offset_start);
*pstart = av_sequence_offset_start;
*psize = (int)(av_sequence_offset_end - av_sequence_offset_start);
}
@ -957,7 +957,7 @@ int SrsFlvVodStreamDecoder::read_sequence_header_summary(int64_t* pstart, int* p
return ret;
}
int SrsFlvVodStreamDecoder::lseek(int64_t offset)
int SrsFlvVodStreamDecoder::seek2(int64_t offset)
{
int ret = ERROR_SUCCESS;
@ -969,7 +969,7 @@ int SrsFlvVodStreamDecoder::lseek(int64_t offset)
return ret;
}
if (reader->lseek(offset) < 0) {
if (reader->seek2(offset) < 0) {
ret = ERROR_SYSTEM_FILE_SEEK;
srs_warn("flv fast decoder seek error, "
"size=%"PRId64", offset=%"PRId64", ret=%d",

@ -597,7 +597,7 @@ public:
/**
* for start offset, seed to this position and response flv stream.
*/
virtual int lseek(int64_t offset);
virtual int seek2(int64_t offset);
};
#endif

@ -31,6 +31,22 @@ ISrsReader::~ISrsReader()
{
}
ISrsSeeker::ISrsSeeker()
{
}
ISrsSeeker::~ISrsSeeker()
{
}
ISrsReadSeeker::ISrsReadSeeker()
{
}
ISrsReadSeeker::~ISrsReadSeeker()
{
}
ISrsStreamWriter::ISrsStreamWriter()
{
}
@ -55,3 +71,11 @@ ISrsWriter::~ISrsWriter()
{
}
ISrsWriteSeeker::ISrsWriteSeeker()
{
}
ISrsWriteSeeker::~ISrsWriteSeeker()
{
}

@ -51,6 +51,37 @@ public:
virtual int read(void* buf, size_t size, ssize_t* nread) = 0;
};
/**
* The seeker to seek with a device.
*/
class ISrsSeeker
{
public:
ISrsSeeker();
virtual ~ISrsSeeker();
public:
/**
* The lseek() function repositions the offset of the file descriptor fildes to the argument offset, according to the
* directive whence. lseek() repositions the file pointer fildes as follows:
* If whence is SEEK_SET, the offset is set to offset bytes.
* If whence is SEEK_CUR, the offset is set to its current location plus offset bytes.
* If whence is SEEK_END, the offset is set to the size of the file plus offset bytes.
* @param seeked Upon successful completion, lseek() returns the resulting offset location as measured in bytes from
* the beginning of the file. NULL to ignore.
*/
virtual int lseek(off_t offset, int whence, off_t* seeked) = 0;
};
/**
* The reader and seeker.
*/
class ISrsReadSeeker : virtual public ISrsReader, virtual public ISrsSeeker
{
public:
ISrsReadSeeker();
virtual ~ISrsReadSeeker();
};
/**
* The writer to write stream data to channel.
*/
@ -79,6 +110,8 @@ public:
/**
* write iov over writer.
* @nwrite the actual written bytes. NULL to ignore.
* @remark for the HTTP FLV, to writev to improve performance.
* @see https://github.com/ossrs/srs/issues/405
*/
virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite) = 0;
};
@ -93,5 +126,15 @@ public:
virtual ~ISrsWriter();
};
/**
* The writer and seeker.
*/
class ISrsWriteSeeker : virtual public ISrsWriter, virtual public ISrsSeeker
{
public:
ISrsWriteSeeker();
virtual ~ISrsWriteSeeker();
};
#endif

@ -2959,7 +2959,8 @@ int SrsMp4UserDataBox::decode_header(SrsBuffer* buf)
SrsMp4Decoder::SrsMp4Decoder()
{
reader = NULL;
rsio = NULL;
brand = SrsMp4BoxBrandForbidden;
buf = new char[SRS_MP4_BUF_SIZE];
stream = new SrsSimpleStream();
}
@ -2970,66 +2971,83 @@ SrsMp4Decoder::~SrsMp4Decoder()
srs_freep(stream);
}
int SrsMp4Decoder::initialize(ISrsReader* r)
int SrsMp4Decoder::initialize(ISrsReadSeeker* rs)
{
int ret = ERROR_SUCCESS;
srs_assert(r);
reader = r;
srs_assert(rs);
rsio = rs;
// File Type Box (ftyp)
if (true) {
// For mdat before moov, we must reset the io.
off_t offset = -1;
while (true) {
SrsMp4Box* box = NULL;
SrsAutoFree(SrsMp4Box, box);
if ((ret = load_next_box(&box, SrsMp4BoxTypeFTYP)) != ERROR_SUCCESS) {
if ((ret = load_next_box(&box, 0)) != ERROR_SUCCESS) {
return ret;
}
SrsMp4FileTypeBox* ftyp = dynamic_cast<SrsMp4FileTypeBox*>(box);
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 (box->is_ftyp()) {
SrsMp4FileTypeBox* ftyp = dynamic_cast<SrsMp4FileTypeBox*>(box);
if ((ret = parse_ftyp(ftyp)) != ERROR_SUCCESS) {
return ret;
}
} 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;
}
if (!legal_brand) {
ret = ERROR_MP4_BOX_ILLEGAL_BRAND;
srs_error("MP4 brand is illegal, brand=%d. ret=%d", ftyp->major_brand, ret);
return ret;
}
srs_freep(box);
}
// Media Data Box (mdat) or Movie Box (moov)
SrsMp4Box* box = NULL;
SrsAutoFree(SrsMp4Box, box);
while (true) {
if ((ret = load_next_box(&box, 0)) != ERROR_SUCCESS) {
return ret;
}
if (!box->is_mdat() && !box->is_moov()) {
srs_freep(box);
continue;
}
break;
if (brand == SrsMp4BoxBrandForbidden) {
ret = ERROR_MP4_BOX_ILLEGAL_SCHEMA;
srs_error("MP4 missing ftyp. ret=%d", ret);
return ret;
}
// Only support non-seek mp4, that is, mdat should never before moov.
// @see https://github.com/ossrs/srs/issues/738#issuecomment-276343669
if (box->is_mdat()) {
ret = ERROR_MP4_NOT_NON_SEEKABLE;
srs_error("MP4 is not non-seekable. ret=%d", ret);
// Reset the io to the start to reparse the general MP4.
if (offset >= 0) {
return rsio->lseek(offset, SEEK_SET, NULL);
}
return ret;
}
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);
return ret;
}
// Parse the Movie Header Box(moov).
SrsMp4MovieBox* moov = dynamic_cast<SrsMp4MovieBox*>(box);
return parse_moov(moov);
brand = ftyp->major_brand;
return ret;
}
int SrsMp4Decoder::parse_moov(SrsMp4MovieBox* moov)
@ -3110,7 +3128,7 @@ int SrsMp4Decoder::do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_typ
uint64_t required = box? box->sz():4;
while (stream->length() < required) {
ssize_t nread;
if ((ret = reader->read(buf, SRS_MP4_BUF_SIZE, &nread)) != ERROR_SUCCESS) {
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;
}
@ -3131,18 +3149,36 @@ int SrsMp4Decoder::do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_typ
return ret;
}
// Decode util we can demux the whole box.
if (!buffer->require((int)box->sz())) {
continue;
}
// 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);
}
if (!required_box_type || box->type == required_box_type) {
ret = box->decode(buffer);
// Remove the consumed bytes.
stream->erase((int)box->sz());
}
// Remove the consumed bytes.
stream->erase((int)box->sz());
if (ret != ERROR_SUCCESS) {
srs_freep(box);
} else {

@ -35,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <string>
#include <vector>
class ISrsReader;
class ISrsReadSeeker;
class SrsMp4TrackBox;
class SrsMp4MediaBox;
class SrsSimpleStream;
@ -1342,8 +1342,12 @@ protected:
class SrsMp4Decoder
{
private:
// Underlayer reader.
ISrsReader* reader;
// The major brand of decoder, parse from ftyp.
SrsMp4BoxBrand brand;
private:
// Underlayer reader and seeker.
// @remark The demuxer must use seeker for general MP4 to seek the moov.
ISrsReadSeeker* rsio;
// The stream used to demux the boxes.
// TODO: FIXME: refine for performance issue.
SrsSimpleStream* stream;
@ -1358,13 +1362,15 @@ public:
* @param r The underlayer io reader, user must manage it for decoder never open/free it,
* the decoder just read data from the reader.
*/
virtual int initialize(ISrsReader* r);
virtual int initialize(ISrsReadSeeker* rs);
private:
virtual int parse_moov(SrsMp4MovieBox* box);
virtual int parse_ftyp(SrsMp4FileTypeBox* ftyp);
virtual int parse_moov(SrsMp4MovieBox* moov);
private:
// Load the next box from reader.
// @param required_box_type The box type required, 0 for any box.
virtual int load_next_box(SrsMp4Box** ppbox, uint32_t required_box_type);
// @remark Never load the mdat box content, for it's too large.
virtual int do_load_next_box(SrsMp4Box** ppbox, uint32_t required_box_type);
};

@ -1755,7 +1755,7 @@ int64_t srs_flv_tellg(srs_flv_t flv)
void srs_flv_lseek(srs_flv_t flv, int64_t offset)
{
FlvContext* context = (FlvContext*)flv;
context->reader.lseek(offset);
context->reader.seek2(offset);
}
srs_bool srs_flv_is_eof(int error_code)

@ -131,7 +131,7 @@ void MockSrsFileReader::skip(int64_t _size)
offset += _size;
}
int64_t MockSrsFileReader::lseek(int64_t _offset)
int64_t MockSrsFileReader::seek2(int64_t _offset)
{
offset = (int)_offset;
return offset;
@ -163,6 +163,12 @@ int MockSrsFileReader::read(void* buf, size_t count, ssize_t* pnread)
return ret;
}
int MockSrsFileReader::lseek(off_t _offset, int /*whence*/, off_t* /*seeked*/)
{
offset = (int)_offset;
return ERROR_SUCCESS;
}
void MockSrsFileReader::mock_append_data(const char* _data, int _size)
{
int s = srs_min(MAX_MOCK_DATA_SIZE - offset, _size);
@ -952,10 +958,10 @@ VOID TEST(KernelFlvTest, FlvVSDecoderSeek)
fs.mock_append_data(tag_header, 11);
EXPECT_TRUE(11 == fs.offset);
EXPECT_TRUE(ERROR_SUCCESS == dec.lseek(0));
EXPECT_TRUE(ERROR_SUCCESS == dec.seek2(0));
EXPECT_TRUE(0 == fs.offset);
EXPECT_TRUE(ERROR_SUCCESS == dec.lseek(5));
EXPECT_TRUE(ERROR_SUCCESS == dec.seek2(5));
EXPECT_TRUE(5 == fs.offset);
}

@ -81,10 +81,11 @@ public:
virtual bool is_open();
virtual int64_t tellg();
virtual void skip(int64_t size);
virtual int64_t lseek(int64_t offset);
virtual int64_t seek2(int64_t offset);
virtual int64_t filesize();
public:
virtual int read(void* buf, size_t count, ssize_t* pnread);
virtual int lseek(off_t offset, int whence, off_t* seeked);
// for mock
public:
// append data to current offset, modify the offset and size.

Loading…
Cancel
Save