diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index ad2954663..7751f1959 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -304,7 +304,7 @@ int SrsHttpVhost::response_flv_file2(SrsSocket* skt, SrsHttpMessage* req, string return ret; } - SrsFlvFastDecoder ffd; + SrsFlvVodStreamDecoder ffd; // open fast decoder if ((ret = ffd.initialize(&fs)) != ERROR_SUCCESS) { diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index 55521ae60..6f737b881 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -225,167 +225,6 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s return ret; } -SrsFlvFastDecoder::SrsFlvFastDecoder() -{ - _fs = NULL; - tag_stream = new SrsStream(); -} - -SrsFlvFastDecoder::~SrsFlvFastDecoder() -{ - srs_freep(tag_stream); -} - -int SrsFlvFastDecoder::initialize(SrsFileReader* fs) -{ - int ret = ERROR_SUCCESS; - - _fs = fs; - - return ret; -} - -int SrsFlvFastDecoder::read_header(char** pdata, int* psize) -{ - *pdata = NULL; - *psize = 0; - - int ret = ERROR_SUCCESS; - - srs_assert(_fs); - - // 9bytes header and 4bytes first previous-tag-size - int size = 13; - char* buf = new char[size]; - - if ((ret = _fs->read(buf, size, NULL)) != ERROR_SUCCESS) { - return ret; - } - - *pdata = buf; - *psize = size; - - return ret; -} - -int SrsFlvFastDecoder::read_sequence_header(int64_t* pstart, int* psize) -{ - *pstart = 0; - *psize = 0; - - int ret = ERROR_SUCCESS; - - srs_assert(_fs); - - // simply, the first video/audio must be the sequence header. - // and must be a sequence video and audio. - - // 11bytes tag header - static char tag_header[] = { - (char)0x00, // TagType UB [5], 9 = video, 8 = audio, 18 = script data - (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. - (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. - (char)0x00, // TimestampExtended UI8 - (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. - }; - - // discovery the sequence header video and audio. - // @remark, maybe no video or no audio. - bool got_video = false; - bool got_audio = false; - // audio/video sequence and data offset. - int64_t av_sequence_offset_start = -1; - int64_t av_sequence_offset_end = -1; - for (;;) { - if ((ret = _fs->read(tag_header, SRS_FLV_TAG_HEADER_SIZE, NULL)) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = tag_stream->initialize(tag_header, SRS_FLV_TAG_HEADER_SIZE)) != ERROR_SUCCESS) { - return ret; - } - - int8_t tag_type = tag_stream->read_1bytes(); - int32_t data_size = tag_stream->read_3bytes(); - - bool is_video = tag_type == 0x09; - bool is_audio = tag_type == 0x08; - bool is_not_av = !is_video && !is_audio; - if (is_not_av) { - // skip body and tag size. - _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); - continue; - } - - // if video duplicated, no audio - if (is_video && got_video) { - break; - } - // if audio duplicated, no video - if (is_audio && got_audio) { - break; - } - - // video - if (is_video) { - srs_assert(!got_video); - got_video = true; - - if (av_sequence_offset_start < 0) { - av_sequence_offset_start = _fs->tellg() - SRS_FLV_TAG_HEADER_SIZE; - } - av_sequence_offset_end = _fs->tellg() + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; - _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); - } - - // audio - if (is_audio) { - srs_assert(!got_audio); - got_audio = true; - - if (av_sequence_offset_start < 0) { - av_sequence_offset_start = _fs->tellg() - SRS_FLV_TAG_HEADER_SIZE; - } - av_sequence_offset_end = _fs->tellg() + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; - _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); - } - } - - // seek to the sequence header start offset. - if (av_sequence_offset_start > 0) { - _fs->lseek(av_sequence_offset_start); - *pstart = av_sequence_offset_start; - *psize = (int)(av_sequence_offset_end - av_sequence_offset_start); - } - - return ret; -} - -int SrsFlvFastDecoder::lseek(int64_t offset) -{ - int ret = ERROR_SUCCESS; - - srs_assert(_fs); - - if (offset >= _fs->filesize()) { - ret = ERROR_SYSTEM_FILE_EOF; - srs_warn("flv fast decoder seek overflow file, " - "size=%"PRId64", offset=%"PRId64", ret=%d", - _fs->filesize(), offset, ret); - return ret; - } - - if (_fs->lseek(offset) < 0) { - ret = ERROR_SYSTEM_FILE_SEEK; - srs_warn("flv fast decoder seek error, " - "size=%"PRId64", offset=%"PRId64", ret=%d", - _fs->filesize(), offset, ret); - return ret; - } - - return ret; -} - SrsFlvDecoder::SrsFlvDecoder() { _fs = NULL; @@ -491,3 +330,164 @@ int SrsFlvDecoder::read_previous_tag_size(char ts[4]) return ret; } +SrsFlvVodStreamDecoder::SrsFlvVodStreamDecoder() +{ + _fs = NULL; + tag_stream = new SrsStream(); +} + +SrsFlvVodStreamDecoder::~SrsFlvVodStreamDecoder() +{ + srs_freep(tag_stream); +} + +int SrsFlvVodStreamDecoder::initialize(SrsFileReader* fs) +{ + int ret = ERROR_SUCCESS; + + _fs = fs; + + return ret; +} + +int SrsFlvVodStreamDecoder::read_header(char** pdata, int* psize) +{ + *pdata = NULL; + *psize = 0; + + int ret = ERROR_SUCCESS; + + srs_assert(_fs); + + // 9bytes header and 4bytes first previous-tag-size + int size = 13; + char* buf = new char[size]; + + if ((ret = _fs->read(buf, size, NULL)) != ERROR_SUCCESS) { + return ret; + } + + *pdata = buf; + *psize = size; + + return ret; +} + +int SrsFlvVodStreamDecoder::read_sequence_header(int64_t* pstart, int* psize) +{ + *pstart = 0; + *psize = 0; + + int ret = ERROR_SUCCESS; + + srs_assert(_fs); + + // simply, the first video/audio must be the sequence header. + // and must be a sequence video and audio. + + // 11bytes tag header + static char tag_header[] = { + (char)0x00, // TagType UB [5], 9 = video, 8 = audio, 18 = script data + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. + (char)0x00, // TimestampExtended UI8 + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. + }; + + // discovery the sequence header video and audio. + // @remark, maybe no video or no audio. + bool got_video = false; + bool got_audio = false; + // audio/video sequence and data offset. + int64_t av_sequence_offset_start = -1; + int64_t av_sequence_offset_end = -1; + for (;;) { + if ((ret = _fs->read(tag_header, SRS_FLV_TAG_HEADER_SIZE, NULL)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = tag_stream->initialize(tag_header, SRS_FLV_TAG_HEADER_SIZE)) != ERROR_SUCCESS) { + return ret; + } + + int8_t tag_type = tag_stream->read_1bytes(); + int32_t data_size = tag_stream->read_3bytes(); + + bool is_video = tag_type == 0x09; + bool is_audio = tag_type == 0x08; + bool is_not_av = !is_video && !is_audio; + if (is_not_av) { + // skip body and tag size. + _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); + continue; + } + + // if video duplicated, no audio + if (is_video && got_video) { + break; + } + // if audio duplicated, no video + if (is_audio && got_audio) { + break; + } + + // video + if (is_video) { + srs_assert(!got_video); + got_video = true; + + if (av_sequence_offset_start < 0) { + av_sequence_offset_start = _fs->tellg() - SRS_FLV_TAG_HEADER_SIZE; + } + av_sequence_offset_end = _fs->tellg() + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; + _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); + } + + // audio + if (is_audio) { + srs_assert(!got_audio); + got_audio = true; + + if (av_sequence_offset_start < 0) { + av_sequence_offset_start = _fs->tellg() - SRS_FLV_TAG_HEADER_SIZE; + } + av_sequence_offset_end = _fs->tellg() + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; + _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); + } + } + + // seek to the sequence header start offset. + if (av_sequence_offset_start > 0) { + _fs->lseek(av_sequence_offset_start); + *pstart = av_sequence_offset_start; + *psize = (int)(av_sequence_offset_end - av_sequence_offset_start); + } + + return ret; +} + +int SrsFlvVodStreamDecoder::lseek(int64_t offset) +{ + int ret = ERROR_SUCCESS; + + srs_assert(_fs); + + if (offset >= _fs->filesize()) { + ret = ERROR_SYSTEM_FILE_EOF; + srs_warn("flv fast decoder seek overflow file, " + "size=%"PRId64", offset=%"PRId64", ret=%d", + _fs->filesize(), offset, ret); + return ret; + } + + if (_fs->lseek(offset) < 0) { + ret = ERROR_SYSTEM_FILE_SEEK; + srs_warn("flv fast decoder seek error, " + "size=%"PRId64", offset=%"PRId64", ret=%d", + _fs->filesize(), offset, ret); + return ret; + } + + return ret; +} + diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp index b085c5b20..c22c14d72 100644 --- a/trunk/src/kernel/srs_kernel_flv.hpp +++ b/trunk/src/kernel/srs_kernel_flv.hpp @@ -49,8 +49,9 @@ public: virtual ~SrsFlvEncoder(); public: /** - * initialize the underlayer file stream, - * user can initialize multiple times to encode multiple flv files. + * initialize the underlayer file stream. + * @remark user can initialize multiple times to encode multiple flv files. + * @remark, user must free the fs, flv encoder never close/free it. */ virtual int initialize(SrsFileWriter* fs); public: @@ -85,21 +86,50 @@ private: }; /** -* decode flv fast by only decoding the header and tag. +* decode flv file. */ -class SrsFlvFastDecoder +class SrsFlvDecoder { private: SrsFileReader* _fs; private: SrsStream* tag_stream; public: - SrsFlvFastDecoder(); - virtual ~SrsFlvFastDecoder(); + SrsFlvDecoder(); + virtual ~SrsFlvDecoder(); public: /** - * initialize the underlayer file stream, - * user can initialize multiple times to encode multiple flv files. + * initialize the underlayer file stream + * @remark user can initialize multiple times to decode multiple flv files. + * @remark, user must free the fs, flv decoder never close/free it. + */ + virtual int initialize(SrsFileReader* fs); +public: + virtual int read_header(char header[9]); + virtual int read_tag_header(char* ptype, int32_t* pdata_size, u_int32_t* ptime); + virtual int read_tag_data(char* data, int32_t size); + virtual int read_previous_tag_size(char ts[4]); +}; + +/** +* decode flv fast by only decoding the header and tag. +* used for vod flv stream to read the header and sequence header, +* then seek to specified offset. +*/ +class SrsFlvVodStreamDecoder +{ +private: + SrsFileReader* _fs; +private: + SrsStream* tag_stream; +public: + SrsFlvVodStreamDecoder(); + virtual ~SrsFlvVodStreamDecoder(); +public: + /** + * initialize the underlayer file stream + * @remark user can initialize multiple times to decode multiple flv files. + * @remark, user must free the fs, flv decoder never close/free it. */ virtual int initialize(SrsFileReader* fs); public: @@ -118,29 +148,4 @@ public: virtual int lseek(int64_t offset); }; -/** -* decode flv file. -*/ -class SrsFlvDecoder -{ -private: - SrsFileReader* _fs; -private: - SrsStream* tag_stream; -public: - SrsFlvDecoder(); - virtual ~SrsFlvDecoder(); -public: - /** - * initialize the underlayer file stream, - * user can initialize multiple times to decode multiple flv files. - */ - virtual int initialize(SrsFileReader* fs); -public: - virtual int read_header(char header[9]); - virtual int read_tag_header(char* ptype, int32_t* pdata_size, u_int32_t* ptime); - virtual int read_tag_data(char* data, int32_t size); - virtual int read_previous_tag_size(char ts[4]); -}; - #endif diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index f3420eac1..8c9e45729 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -26,88 +26,7 @@ using namespace std; #include #include - -VOID TEST(KernelCodecTest, IsKeyFrame) -{ - int8_t data; - - data = 0x10; - EXPECT_TRUE(SrsFlvCodec::video_is_keyframe(&data, 1)); - EXPECT_FALSE(SrsFlvCodec::video_is_keyframe(&data, 0)); - - data = 0x20; - EXPECT_FALSE(SrsFlvCodec::video_is_keyframe(&data, 1)); -} - -VOID TEST(KernelCodecTest, IsH264) -{ - int8_t data; - - EXPECT_FALSE(SrsFlvCodec::video_is_h264(&data, 0)); - - data = 0x17; - EXPECT_TRUE(SrsFlvCodec::video_is_h264(&data, 1)); - - data = 0x07; - EXPECT_TRUE(SrsFlvCodec::video_is_h264(&data, 1)); - - data = 0x08; - EXPECT_FALSE(SrsFlvCodec::video_is_h264(&data, 1)); -} - -VOID TEST(KernelCodecTest, IsSequenceHeader) -{ - int16_t data; - char* pp = (char*)&data; - - EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 0)); - EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 1)); - - pp[0] = 0x17; - pp[1] = 0x00; - EXPECT_TRUE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); - pp[0] = 0x18; - EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); - pp[0] = 0x27; - EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); - pp[0] = 0x17; - pp[1] = 0x01; - EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); -} - -VOID TEST(KernelCodecTest, IsAAC) -{ - int8_t data; - - EXPECT_FALSE(SrsFlvCodec::audio_is_aac(&data, 0)); - - data = 0xa0; - EXPECT_TRUE(SrsFlvCodec::audio_is_aac(&data, 1)); - - data = 0xa7; - EXPECT_TRUE(SrsFlvCodec::audio_is_aac(&data, 1)); - - data = 0x00; - EXPECT_FALSE(SrsFlvCodec::audio_is_aac(&data, 1)); -} - -VOID TEST(KernelCodecTest, IsAudioSequenceHeader) -{ - int16_t data; - char* pp = (char*)&data; - - EXPECT_FALSE(SrsFlvCodec::audio_is_sequence_header((int8_t*)pp, 0)); - EXPECT_FALSE(SrsFlvCodec::audio_is_sequence_header((int8_t*)pp, 1)); - - pp[0] = 0xa0; - pp[1] = 0x00; - EXPECT_TRUE(SrsFlvCodec::audio_is_sequence_header((int8_t*)pp, 2)); - pp[0] = 0x00; - EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); - pp[0] = 0xa0; - pp[1] = 0x01; - EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); -} +#include MockSrsFileWriter::MockSrsFileWriter() { @@ -194,3 +113,92 @@ int MockSrsFileReader::read(void* buf, size_t count, ssize_t* pnread) int ret = ERROR_SUCCESS; return ret; } + +VOID TEST(KernelCodecTest, IsKeyFrame) +{ + int8_t data; + + data = 0x10; + EXPECT_TRUE(SrsFlvCodec::video_is_keyframe(&data, 1)); + EXPECT_FALSE(SrsFlvCodec::video_is_keyframe(&data, 0)); + + data = 0x20; + EXPECT_FALSE(SrsFlvCodec::video_is_keyframe(&data, 1)); +} + +VOID TEST(KernelCodecTest, IsH264) +{ + int8_t data; + + EXPECT_FALSE(SrsFlvCodec::video_is_h264(&data, 0)); + + data = 0x17; + EXPECT_TRUE(SrsFlvCodec::video_is_h264(&data, 1)); + + data = 0x07; + EXPECT_TRUE(SrsFlvCodec::video_is_h264(&data, 1)); + + data = 0x08; + EXPECT_FALSE(SrsFlvCodec::video_is_h264(&data, 1)); +} + +VOID TEST(KernelCodecTest, IsSequenceHeader) +{ + int16_t data; + char* pp = (char*)&data; + + EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 0)); + EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 1)); + + pp[0] = 0x17; + pp[1] = 0x00; + EXPECT_TRUE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); + pp[0] = 0x18; + EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); + pp[0] = 0x27; + EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); + pp[0] = 0x17; + pp[1] = 0x01; + EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); +} + +VOID TEST(KernelCodecTest, IsAAC) +{ + int8_t data; + + EXPECT_FALSE(SrsFlvCodec::audio_is_aac(&data, 0)); + + data = 0xa0; + EXPECT_TRUE(SrsFlvCodec::audio_is_aac(&data, 1)); + + data = 0xa7; + EXPECT_TRUE(SrsFlvCodec::audio_is_aac(&data, 1)); + + data = 0x00; + EXPECT_FALSE(SrsFlvCodec::audio_is_aac(&data, 1)); +} + +VOID TEST(KernelCodecTest, IsAudioSequenceHeader) +{ + int16_t data; + char* pp = (char*)&data; + + EXPECT_FALSE(SrsFlvCodec::audio_is_sequence_header((int8_t*)pp, 0)); + EXPECT_FALSE(SrsFlvCodec::audio_is_sequence_header((int8_t*)pp, 1)); + + pp[0] = 0xa0; + pp[1] = 0x00; + EXPECT_TRUE(SrsFlvCodec::audio_is_sequence_header((int8_t*)pp, 2)); + pp[0] = 0x00; + EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); + pp[0] = 0xa0; + pp[1] = 0x01; + EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); +} + +VOID TEST(KernelFlvTest, IsAudioSequenceHeader) +{ + MockSrsFileWriter fs; + SrsFlvEncoder enc; + ASSERT_TRUE(ERROR_SUCCESS == enc.initialize(&fs)); +}