diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp index 53f183364..fe515f0ff 100644 --- a/trunk/src/app/srs_app_caster_flv.cpp +++ b/trunk/src/app/srs_app_caster_flv.cpp @@ -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 diff --git a/trunk/src/app/srs_app_caster_flv.hpp b/trunk/src/app/srs_app_caster_flv.hpp index d48900155..d1320546c 100644 --- a/trunk/src/app/srs_app_caster_flv.hpp +++ b/trunk/src/app/srs_app_caster_flv.hpp @@ -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 diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index a3739451d..77f7a8bf5 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -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; } diff --git a/trunk/src/app/srs_app_http_static.cpp b/trunk/src/app/srs_app_http_static.cpp index e039922ee..620cbbcb3 100644 --- a/trunk/src/app/srs_app_http_static.cpp +++ b/trunk/src/app/srs_app_http_static.cpp @@ -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) { diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 6528ce320..909aeef72 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -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. diff --git a/trunk/src/kernel/srs_kernel_file.cpp b/trunk/src/kernel/srs_kernel_file.cpp index 777b4f415..5c50b5ce9 100644 --- a/trunk/src/kernel/srs_kernel_file.cpp +++ b/trunk/src/kernel/srs_kernel_file.cpp @@ -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; +} + diff --git a/trunk/src/kernel/srs_kernel_file.hpp b/trunk/src/kernel/srs_kernel_file.hpp index bf34035f9..9b92bf62f 100644 --- a/trunk/src/kernel/srs_kernel_file.hpp +++ b/trunk/src/kernel/srs_kernel_file.hpp @@ -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 diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index ada330915..a25fdcd6d 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -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", diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp index 097c87af2..90ab7e816 100644 --- a/trunk/src/kernel/srs_kernel_flv.hpp +++ b/trunk/src/kernel/srs_kernel_flv.hpp @@ -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 diff --git a/trunk/src/kernel/srs_kernel_io.cpp b/trunk/src/kernel/srs_kernel_io.cpp index 5b604e79c..3f1c188eb 100644 --- a/trunk/src/kernel/srs_kernel_io.cpp +++ b/trunk/src/kernel/srs_kernel_io.cpp @@ -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() +{ +} + diff --git a/trunk/src/kernel/srs_kernel_io.hpp b/trunk/src/kernel/srs_kernel_io.hpp index f2a18d04a..0e62f4de3 100644 --- a/trunk/src/kernel/srs_kernel_io.hpp +++ b/trunk/src/kernel/srs_kernel_io.hpp @@ -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 diff --git a/trunk/src/kernel/srs_kernel_mp4.cpp b/trunk/src/kernel/srs_kernel_mp4.cpp index ad09dcf16..15c22b373 100644 --- a/trunk/src/kernel/srs_kernel_mp4.cpp +++ b/trunk/src/kernel/srs_kernel_mp4.cpp @@ -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(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(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(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(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 { diff --git a/trunk/src/kernel/srs_kernel_mp4.hpp b/trunk/src/kernel/srs_kernel_mp4.hpp index b05bf4406..b82b4c01f 100644 --- a/trunk/src/kernel/srs_kernel_mp4.hpp +++ b/trunk/src/kernel/srs_kernel_mp4.hpp @@ -35,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -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); }; diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 941d9d990..d15d1b670 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -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) diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 9609acea4..bfaac2a73 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -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); } diff --git a/trunk/src/utest/srs_utest_kernel.hpp b/trunk/src/utest/srs_utest_kernel.hpp index 85d89186f..9a1870b8d 100644 --- a/trunk/src/utest/srs_utest_kernel.hpp +++ b/trunk/src/utest/srs_utest_kernel.hpp @@ -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.