diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 7751f1959..e056d58c5 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -312,14 +312,12 @@ int SrsHttpVhost::response_flv_file2(SrsSocket* skt, SrsHttpMessage* req, string } // save header, send later. - char* flv_header = NULL; - int flv_size = 0; + char flv_header[13]; // send flv header - if ((ret = ffd.read_header(&flv_header, &flv_size)) != ERROR_SUCCESS) { + if ((ret = ffd.read_header_ext(flv_header)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(char, flv_header); // save sequence header, send later char* sh_data = NULL; @@ -328,7 +326,7 @@ int SrsHttpVhost::response_flv_file2(SrsSocket* skt, SrsHttpMessage* req, string if (true) { // send sequence header int64_t start = 0; - if ((ret = ffd.read_sequence_header(&start, &sh_size)) != ERROR_SUCCESS) { + if ((ret = ffd.read_sequence_header_summary(&start, &sh_size)) != ERROR_SUCCESS) { return ret; } if (sh_size <= 0) { @@ -350,7 +348,7 @@ int SrsHttpVhost::response_flv_file2(SrsSocket* skt, SrsHttpMessage* req, string std::stringstream ss; res_status_line(ss)->res_content_type_flv(ss) - ->res_content_length(ss, (int)(flv_size + sh_size + left)); + ->res_content_length(ss, (int)(sizeof(flv_header) + sh_size + left)); if (req->requires_crossdomain()) { res_enable_crossdomain(ss); @@ -363,7 +361,7 @@ int SrsHttpVhost::response_flv_file2(SrsSocket* skt, SrsHttpMessage* req, string return ret; } - if (flv_size > 0 && (ret = skt->write(flv_header, flv_size, NULL)) != ERROR_SUCCESS) { + if ((ret = skt->write(flv_header, sizeof(flv_header), NULL)) != ERROR_SUCCESS) { return ret; } if (sh_size > 0 && (ret = skt->write(sh_data, sh_size, NULL)) != ERROR_SUCCESS) { diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp old mode 100755 new mode 100644 index 5ac15c89b..2bd9eac5b --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -1,99 +1,99 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 winlin - -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. -*/ - -#ifndef SRS_CORE_HPP -#define SRS_CORE_HPP - -/* -#include -*/ - -// current release version -#define VERSION_MAJOR "0" -#define VERSION_MINOR "9" -#define VERSION_REVISION "146" -#define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION -// server info. -#define RTMP_SIG_SRS_KEY "SRS" -#define RTMP_SIG_SRS_ROLE "origin/edge server" -#define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(Simple RTMP Server)" -#define RTMP_SIG_SRS_URL_SHORT "github.com/winlinvip/simple-rtmp-server" -#define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT -#define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" -#define RTMP_SIG_SRS_EMAIL "winlin@vip.126.com" -#define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" -#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2014 winlin" -#define RTMP_SIG_SRS_PRIMARY_AUTHROS "winlin,wenjie.zhao" -#define RTMP_SIG_SRS_CONTRIBUTORS_URL RTMP_SIG_SRS_URL"/blob/master/AUTHORS.txt" - -/** -* the core provides the common defined macros, utilities, -* user must include the srs_core.hpp before any header, or maybe -* build failed. -*/ - -// for 32bit os, 2G big file limit for unistd io, -// ie. read/write/lseek to use 64bits size for huge file. -#ifndef _FILE_OFFSET_BITS - #define _FILE_OFFSET_BITS 64 -#endif - -// for int64_t print using PRId64 format. -#ifndef __STDC_FORMAT_MACROS - #define __STDC_FORMAT_MACROS -#endif -#include - -#include -#define srs_assert(expression) assert(expression) - -#include -#include - -// generated by configure. -#include - -// free the p and set to NULL. -// p must be a T*. -#define srs_freep(p) \ - if (p) { \ - delete p; \ - p = NULL; \ - } \ - (void)0 -// sometimes, the freepa is useless, -// it's recomments to free each elem explicit. -// so we remove the srs_freepa utility. - -/** -* disable copy constructor of class -*/ -#define disable_default_copy(className)\ - private:\ - /** \ - * disable the copy constructor and operator=, donot allow directly copy. \ - */ \ - className(const className&); \ - className& operator= (const className&) - -#endif +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +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. +*/ + +#ifndef SRS_CORE_HPP +#define SRS_CORE_HPP + +/* +#include +*/ + +// current release version +#define VERSION_MAJOR "0" +#define VERSION_MINOR "9" +#define VERSION_REVISION "147" +#define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION +// server info. +#define RTMP_SIG_SRS_KEY "SRS" +#define RTMP_SIG_SRS_ROLE "origin/edge server" +#define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(Simple RTMP Server)" +#define RTMP_SIG_SRS_URL_SHORT "github.com/winlinvip/simple-rtmp-server" +#define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT +#define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" +#define RTMP_SIG_SRS_EMAIL "winlin@vip.126.com" +#define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" +#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2014 winlin" +#define RTMP_SIG_SRS_PRIMARY_AUTHROS "winlin,wenjie.zhao" +#define RTMP_SIG_SRS_CONTRIBUTORS_URL RTMP_SIG_SRS_URL"/blob/master/AUTHORS.txt" + +/** +* the core provides the common defined macros, utilities, +* user must include the srs_core.hpp before any header, or maybe +* build failed. +*/ + +// for 32bit os, 2G big file limit for unistd io, +// ie. read/write/lseek to use 64bits size for huge file. +#ifndef _FILE_OFFSET_BITS + #define _FILE_OFFSET_BITS 64 +#endif + +// for int64_t print using PRId64 format. +#ifndef __STDC_FORMAT_MACROS + #define __STDC_FORMAT_MACROS +#endif +#include + +#include +#define srs_assert(expression) assert(expression) + +#include +#include + +// generated by configure. +#include + +// free the p and set to NULL. +// p must be a T*. +#define srs_freep(p) \ + if (p) { \ + delete p; \ + p = NULL; \ + } \ + (void)0 +// sometimes, the freepa is useless, +// it's recomments to free each elem explicit. +// so we remove the srs_freepa utility. + +/** +* disable copy constructor of class +*/ +#define disable_default_copy(className)\ + private:\ + /** \ + * disable the copy constructor and operator=, donot allow directly copy. \ + */ \ + className(const className&); \ + className& operator= (const className&) + +#endif diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index edf09e085..7a195d5e6 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -119,8 +119,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_SYSTEM_FILE_RENAME 429 #define ERROR_SYSTEM_CREATE_PIPE 430 #define ERROR_SYSTEM_FILE_SEEK 431 -#define ERROR_SYSTEM_FLV_HEADER 432 -#define ERROR_SYSTEM_IO_INVALID 433 +#define ERROR_SYSTEM_IO_INVALID 432 // see librtmp. // failed when open ssl create the dh @@ -189,12 +188,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_HTTP_FLV_SEQUENCE_HEADER 806 #define ERROR_HTTP_FLV_OFFSET_OVERFLOW 807 +#define ERROR_KERNEL_FLV_HEADER 900 +#define ERROR_KERNEL_FLV_STREAM_CLOSED 901 + // system control message, // not an error, but special control logic. // sys ctl: rtmp close stream, support replay. -#define ERROR_CONTROL_RTMP_CLOSE 900 +#define ERROR_CONTROL_RTMP_CLOSE 2000 // FMLE stop publish and republish. -#define ERROR_CONTROL_REPUBLISH 901 +#define ERROR_CONTROL_REPUBLISH 2001 /** * whether the error code is an system control error. diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index 6f737b881..50071429c 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -53,6 +53,12 @@ int SrsFlvEncoder::initialize(SrsFileWriter* fs) { int ret = ERROR_SUCCESS; + if (!fs->is_open()) { + ret = ERROR_KERNEL_FLV_STREAM_CLOSED; + srs_warn("stream is not open for decoder. ret=%d", ret); + return ret; + } + _fs = fs; return ret; @@ -92,7 +98,7 @@ int SrsFlvEncoder::write_header(char flv_header[9]) return ret; } - char pts[] = { 0x00, 0x00, 0x00, 0x00 }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)0x00 }; if ((ret = _fs->write(pts, 4, NULL)) != ERROR_SUCCESS) { return ret; } @@ -104,6 +110,7 @@ int SrsFlvEncoder::write_metadata(char* data, int size) { int ret = ERROR_SUCCESS; + // 11 bytes tag header static char tag_header[] = { (char)18, // TagType UB [5], 18 = script data (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. @@ -240,6 +247,12 @@ int SrsFlvDecoder::initialize(SrsFileReader* fs) { int ret = ERROR_SUCCESS; + if (!fs->is_open()) { + ret = ERROR_KERNEL_FLV_STREAM_CLOSED; + srs_warn("stream is not open for decoder. ret=%d", ret); + return ret; + } + _fs = fs; return ret; @@ -255,7 +268,7 @@ int SrsFlvDecoder::read_header(char header[9]) char* h = header; if (h[0] != 'F' || h[1] != 'L' || h[2] != 'V') { - ret = ERROR_SYSTEM_FLV_HEADER; + ret = ERROR_KERNEL_FLV_HEADER; srs_warn("flv header must start with FLV. ret=%d", ret); return ret; } @@ -345,35 +358,37 @@ int SrsFlvVodStreamDecoder::initialize(SrsFileReader* fs) { int ret = ERROR_SUCCESS; + if (!fs->is_open()) { + ret = ERROR_KERNEL_FLV_STREAM_CLOSED; + srs_warn("stream is not open for decoder. ret=%d", ret); + return ret; + } + _fs = fs; return ret; } -int SrsFlvVodStreamDecoder::read_header(char** pdata, int* psize) +int SrsFlvVodStreamDecoder::read_header_ext(char header[13]) { - *pdata = NULL; - *psize = 0; - int ret = ERROR_SUCCESS; srs_assert(_fs); + // @remark, always false, for sizeof(char[13]) equals to sizeof(char*) + //srs_assert(13 == sizeof(header)); + // 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) { + if ((ret = _fs->read(header, size, NULL)) != ERROR_SUCCESS) { return ret; } - *pdata = buf; - *psize = size; - return ret; } -int SrsFlvVodStreamDecoder::read_sequence_header(int64_t* pstart, int* psize) +int SrsFlvVodStreamDecoder::read_sequence_header_summary(int64_t* pstart, int* psize) { *pstart = 0; *psize = 0; diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp index c22c14d72..015250d15 100644 --- a/trunk/src/kernel/srs_kernel_flv.hpp +++ b/trunk/src/kernel/srs_kernel_flv.hpp @@ -66,7 +66,7 @@ public: virtual int write_header(char flv_header[9]); /** * write flv metadata. - * serialize from: + * @param data, the amf0 metadata which serialize from: * AMF0 string: onMetaData, * AMF0 object: the metadata object. */ @@ -76,6 +76,7 @@ public: */ virtual int write_audio(int64_t timestamp, char* data, int size); virtual int write_video(int64_t timestamp, char* data, int size); +public: /** * get the tag size, * including the tag header, body, and 4bytes previous tag size. @@ -134,13 +135,17 @@ public: virtual int initialize(SrsFileReader* fs); public: /** - * read the flv header and size. + * read the flv header and its size. + * @param header, fill it 13bytes(9bytes header, 4bytes previous tag size). */ - virtual int read_header(char** pdata, int* psize); + virtual int read_header_ext(char header[13]); /** - * read the sequence header and size. + * read the sequence header tags offset and its size. + * @param pstart, the start offset of sequence header. + * @param psize, output the size, (tag header)+(tag body)+(4bytes previous tag size). + * @remark we think the first audio/video is sequence header. */ - virtual int read_sequence_header(int64_t* pstart, int* psize); + virtual int read_sequence_header_summary(int64_t* pstart, int* psize); public: /** * for start offset, seed to this position and response flv stream. diff --git a/trunk/src/rtmp/srs_protocol_handshake.cpp b/trunk/src/rtmp/srs_protocol_handshake.cpp index 2098ff0ce..d30190104 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.cpp +++ b/trunk/src/rtmp/srs_protocol_handshake.cpp @@ -557,31 +557,6 @@ namespace srs return bytes; } - /** - * compare the memory in bytes. - */ - bool srs_bytes_equals(void* pa, void* pb, int size) - { - u_int8_t* a = (u_int8_t*)pa; - u_int8_t* b = (u_int8_t*)pb; - - if (!a && !b) { - return true; - } - - if (!a || !b) { - return false; - } - - for(int i = 0; i < size; i++){ - if(a[i] != b[i]){ - return false; - } - } - - return true; - } - c2s2::c2s2() { srs_random_generate(random, 1504); diff --git a/trunk/src/rtmp/srs_protocol_handshake.hpp b/trunk/src/rtmp/srs_protocol_handshake.hpp index d4ed01cd9..2f2041155 100644 --- a/trunk/src/rtmp/srs_protocol_handshake.hpp +++ b/trunk/src/rtmp/srs_protocol_handshake.hpp @@ -167,11 +167,6 @@ namespace srs */ char* srs_bytes_join_schema1(int32_t time, int32_t version, digest_block* digest, key_block* key); - /** - * compare the memory in bytes. - */ - bool srs_bytes_equals(void* pa, void* pb, int size); - /** * c1s1 schema0 * time: 4bytes diff --git a/trunk/src/rtmp/srs_protocol_utility.cpp b/trunk/src/rtmp/srs_protocol_utility.cpp index 86448312d..f85c06b57 100644 --- a/trunk/src/rtmp/srs_protocol_utility.cpp +++ b/trunk/src/rtmp/srs_protocol_utility.cpp @@ -126,3 +126,28 @@ string srs_generate_tc_url(string ip, string vhost, string app, string port) return tcUrl; } + +/** +* compare the memory in bytes. +*/ +bool srs_bytes_equals(void* pa, void* pb, int size) +{ + u_int8_t* a = (u_int8_t*)pa; + u_int8_t* b = (u_int8_t*)pb; + + if (!a && !b) { + return true; + } + + if (!a || !b) { + return false; + } + + for(int i = 0; i < size; i++){ + if(a[i] != b[i]){ + return false; + } + } + + return true; +} diff --git a/trunk/src/rtmp/srs_protocol_utility.hpp b/trunk/src/rtmp/srs_protocol_utility.hpp index 2def50324..06007df40 100644 --- a/trunk/src/rtmp/srs_protocol_utility.hpp +++ b/trunk/src/rtmp/srs_protocol_utility.hpp @@ -58,4 +58,9 @@ extern void srs_random_generate(char* bytes, int size); // generate the tcUrl. extern std::string srs_generate_tc_url(std::string ip, std::string vhost, std::string app, std::string port); +/** +* compare the memory in bytes. +*/ +extern bool srs_bytes_equals(void* pa, void* pb, int size); + #endif diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 8c9e45729..9800614a0 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -27,93 +27,155 @@ using namespace std; #include #include #include +#include +#include + +#define MAX_MOCK_DATA_SIZE 1024 * 1024 MockSrsFileWriter::MockSrsFileWriter() { + data = new char[MAX_MOCK_DATA_SIZE]; + offset = -1; } MockSrsFileWriter::~MockSrsFileWriter() { + srs_freep(data); } -int MockSrsFileWriter::open(string file) +int MockSrsFileWriter::open(string /*file*/) { int ret = ERROR_SUCCESS; + + offset = 0; + return ret; } void MockSrsFileWriter::close() { - int ret = ERROR_SUCCESS; - return; + offset = 0; } bool MockSrsFileWriter::is_open() { - return true; + return offset >= 0; } int64_t MockSrsFileWriter::tellg() { - return 0; + return offset; } int MockSrsFileWriter::write(void* buf, size_t count, ssize_t* pnwrite) { int ret = ERROR_SUCCESS; + + int size = srs_min(MAX_MOCK_DATA_SIZE - offset, count); + + memcpy(data + offset, buf, size); + + if (pnwrite) { + *pnwrite = size; + } + + offset += size; + return ret; } +void MockSrsFileWriter::mock_reset_offset() +{ + offset = 0; +} + MockSrsFileReader::MockSrsFileReader() { + data = new char[MAX_MOCK_DATA_SIZE]; + size = 0; + offset = -1; } MockSrsFileReader::~MockSrsFileReader() { + srs_freep(data); } -int MockSrsFileReader::open(string file) +int MockSrsFileReader::open(string /*file*/) { int ret = ERROR_SUCCESS; + + offset = 0; + return ret; } void MockSrsFileReader::close() { - int ret = ERROR_SUCCESS; - return; + offset = 0; } bool MockSrsFileReader::is_open() { - return true; + return offset >= 0; } int64_t MockSrsFileReader::tellg() { - return 0; + return offset; } -void MockSrsFileReader::skip(int64_t size) +void MockSrsFileReader::skip(int64_t _size) { + offset += _size; } -int64_t MockSrsFileReader::lseek(int64_t offset) +int64_t MockSrsFileReader::lseek(int64_t _offset) { + offset = (int)_offset; return offset; } int64_t MockSrsFileReader::filesize() { - return 0; + return size; } int MockSrsFileReader::read(void* buf, size_t count, ssize_t* pnread) { int ret = ERROR_SUCCESS; + + int s = srs_min(size - offset, (int)count); + + if (s <= 0) { + return ret; + } + + memcpy(buf, data + offset, s); + + if (pnread) { + *pnread = s; + } + + offset += s; + return ret; } +void MockSrsFileReader::mock_append_data(const char* _data, int _size) +{ + int s = srs_min(MAX_MOCK_DATA_SIZE - offset, _size); + memcpy(data + offset, _data, s); + + offset += s; + size += s; +} + +void MockSrsFileReader::mock_reset_offset() +{ + offset = 0; +} + VOID TEST(KernelCodecTest, IsKeyFrame) { int8_t data; @@ -196,9 +258,431 @@ VOID TEST(KernelCodecTest, IsAudioSequenceHeader) EXPECT_FALSE(SrsFlvCodec::video_is_sequence_header((int8_t*)pp, 2)); } -VOID TEST(KernelFlvTest, IsAudioSequenceHeader) +VOID TEST(KernelFlvTest, FlvEncoderStreamClosed) +{ + MockSrsFileWriter fs; + SrsFlvEncoder enc; + ASSERT_TRUE(ERROR_SUCCESS != enc.initialize(&fs)); +} + +VOID TEST(KernelFlvTest, FlvEncoderWriteHeader) +{ + MockSrsFileWriter fs; + SrsFlvEncoder enc; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == enc.initialize(&fs)); + + // write header, 9bytes + char flv_header[] = { + 'F', 'L', 'V', // Signatures "FLV" + (char)0x01, // File version (for example, 0x01 for FLV version 1) + (char)0x00, // 4, audio; 1, video; 5 audio+video. + (char)0x00, (char)0x00, (char)0x00, (char)0x09 // DataOffset UI32 The length of this header in bytes + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)0x00 }; + + EXPECT_TRUE(ERROR_SUCCESS == enc.write_header()); + ASSERT_TRUE(9 + 4 == fs.offset); + + EXPECT_TRUE(srs_bytes_equals(flv_header, fs.data, 9)); + EXPECT_TRUE(srs_bytes_equals(pts, fs.data + 9, 4)); + + // customer header + flv_header[3] = 0xF0; + flv_header[4] = 0xF1; + flv_header[5] = 0x01; + + fs.mock_reset_offset(); + + EXPECT_TRUE(ERROR_SUCCESS == enc.write_header(flv_header)); + ASSERT_TRUE(9 + 4 == fs.offset); + + EXPECT_TRUE(srs_bytes_equals(flv_header, fs.data, 9)); + EXPECT_TRUE(srs_bytes_equals(pts, fs.data + 9, 4)); +} + +VOID TEST(KernelFlvTest, FlvEncoderWriteMetadata) +{ + MockSrsFileWriter fs; + EXPECT_TRUE(ERROR_SUCCESS == fs.open("")); + SrsFlvEncoder enc; + ASSERT_TRUE(ERROR_SUCCESS == enc.initialize(&fs)); + + // 11 bytes tag header + char tag_header[] = { + (char)18, // TagType UB [5], 18 = script data + (char)0x00, (char)0x00, (char)0x08, // 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. + }; + char md[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + + ASSERT_TRUE(ERROR_SUCCESS == enc.write_metadata(md, 8)); + ASSERT_TRUE(11 + 8 + 4 == fs.offset); + + EXPECT_TRUE(srs_bytes_equals(tag_header, fs.data, 11)); + EXPECT_TRUE(srs_bytes_equals(md, fs.data + 11, 8)); + EXPECT_TRUE(true); // donot know why, if not add it, the print is disabled. + EXPECT_TRUE(srs_bytes_equals(pts, fs.data + 19, 4)); +} + +VOID TEST(KernelFlvTest, FlvEncoderWriteAudio) +{ + MockSrsFileWriter fs; + SrsFlvEncoder enc; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == enc.initialize(&fs)); + + // 11bytes tag header + char tag_header[] = { + (char)8, // TagType UB [5], 8 = audio + (char)0x00, (char)0x00, (char)0x08, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x30, // 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. + }; + char audio[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + + ASSERT_TRUE(ERROR_SUCCESS == enc.write_audio(0x30, audio, 8)); + ASSERT_TRUE(11 + 8 + 4 == fs.offset); + + EXPECT_TRUE(srs_bytes_equals(tag_header, fs.data, 11)); + EXPECT_TRUE(srs_bytes_equals(audio, fs.data + 11, 8)); + EXPECT_TRUE(true); // donot know why, if not add it, the print is disabled. + EXPECT_TRUE(srs_bytes_equals(pts, fs.data + 11 + 8, 4)); +} + +VOID TEST(KernelFlvTest, FlvEncoderWriteVideo) { MockSrsFileWriter fs; SrsFlvEncoder enc; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); ASSERT_TRUE(ERROR_SUCCESS == enc.initialize(&fs)); + + // 11bytes tag header + char tag_header[] = { + (char)9, // TagType UB [5], 9 = video + (char)0x00, (char)0x00, (char)0x08, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x30, // 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. + }; + char video[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + + ASSERT_TRUE(ERROR_SUCCESS == enc.write_video(0x30, video, 8)); + ASSERT_TRUE(11 + 8 + 4 == fs.offset); + + EXPECT_TRUE(srs_bytes_equals(tag_header, fs.data, 11)); + EXPECT_TRUE(srs_bytes_equals(video, fs.data + 11, 8)); + EXPECT_TRUE(true); // donot know why, if not add it, the print is disabled. + EXPECT_TRUE(srs_bytes_equals(pts, fs.data + 11 + 8, 4)); +} + +VOID TEST(KernelFlvTest, FlvEncoderSizeTag) +{ + EXPECT_EQ(11+4+10, SrsFlvEncoder::size_tag(10)); + EXPECT_EQ(11+4+0, SrsFlvEncoder::size_tag(0)); +} + +VOID TEST(KernelFlvTest, FlvDecoderStreamClosed) +{ + MockSrsFileReader fs; + SrsFlvDecoder dec; + ASSERT_TRUE(ERROR_SUCCESS != dec.initialize(&fs)); +} + +VOID TEST(KernelFlvTest, FlvDecoderHeader) +{ + MockSrsFileReader fs; + SrsFlvDecoder dec; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == dec.initialize(&fs)); + + // 9bytes + char flv_header[] = { + 'F', 'L', 'V', // Signatures "FLV" + (char)0x01, // File version (for example, 0x01 for FLV version 1) + (char)0x00, // 4, audio; 1, video; 5 audio+video. + (char)0x00, (char)0x00, (char)0x00, (char)0x09 // DataOffset UI32 The length of this header in bytes + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)0x00 }; + fs.mock_append_data(flv_header, 9); + fs.mock_append_data(pts, 4); + + char data[1024]; + fs.mock_reset_offset(); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_header(data)); + EXPECT_TRUE(srs_bytes_equals(flv_header, data, 9)); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_previous_tag_size(data)); + EXPECT_TRUE(srs_bytes_equals(pts, data, 4)); +} + +VOID TEST(KernelFlvTest, FlvDecoderMetadata) +{ + MockSrsFileReader fs; + SrsFlvDecoder dec; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == dec.initialize(&fs)); + + // 11 bytes tag header + char tag_header[] = { + (char)18, // TagType UB [5], 18 = script data + (char)0x00, (char)0x00, (char)0x08, // 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. + }; + char md[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + fs.mock_append_data(tag_header, 11); + fs.mock_append_data(md, 8); + fs.mock_append_data(pts, 4); + + char type = 0; + int32_t size = 0; + u_int32_t time = 0; + char data[1024]; + fs.mock_reset_offset(); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_tag_header(&type, &size, &time)); + EXPECT_TRUE(18 == type); + EXPECT_TRUE(8 == size); + EXPECT_TRUE(0 == time); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_tag_data(data, size)); + EXPECT_TRUE(srs_bytes_equals(md, data, 8)); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_previous_tag_size(data)); + EXPECT_TRUE(srs_bytes_equals(pts, data, 4)); +} + +VOID TEST(KernelFlvTest, FlvDecoderAudio) +{ + MockSrsFileReader fs; + SrsFlvDecoder dec; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == dec.initialize(&fs)); + + // 11bytes tag header + char tag_header[] = { + (char)8, // TagType UB [5], 8 = audio + (char)0x00, (char)0x00, (char)0x08, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x30, // 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. + }; + char audio[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + fs.mock_append_data(tag_header, 11); + fs.mock_append_data(audio, 8); + fs.mock_append_data(pts, 4); + + char type = 0; + int32_t size = 0; + u_int32_t time = 0; + char data[1024]; + fs.mock_reset_offset(); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_tag_header(&type, &size, &time)); + EXPECT_TRUE(8 == type); + EXPECT_TRUE(8 == size); + EXPECT_TRUE(0x30 == time); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_tag_data(data, size)); + EXPECT_TRUE(srs_bytes_equals(audio, data, 8)); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_previous_tag_size(data)); + EXPECT_TRUE(srs_bytes_equals(pts, data, 4)); +} + +VOID TEST(KernelFlvTest, FlvDecoderVideo) +{ + MockSrsFileReader fs; + SrsFlvDecoder dec; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == dec.initialize(&fs)); + + // 11bytes tag header + char tag_header[] = { + (char)9, // TagType UB [5], 9 = video + (char)0x00, (char)0x00, (char)0x08, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x30, // 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. + }; + char video[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + fs.mock_append_data(tag_header, 11); + fs.mock_append_data(video, 8); + fs.mock_append_data(pts, 4); + + char type = 0; + int32_t size = 0; + u_int32_t time = 0; + char data[1024]; + fs.mock_reset_offset(); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_tag_header(&type, &size, &time)); + EXPECT_TRUE(9 == type); + EXPECT_TRUE(8 == size); + EXPECT_TRUE(0x30 == time); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_tag_data(data, size)); + EXPECT_TRUE(srs_bytes_equals(video, data, 8)); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_previous_tag_size(data)); + EXPECT_TRUE(srs_bytes_equals(pts, data, 4)); +} + +VOID TEST(KernelFlvTest, FlvVSDecoderStreamClosed) +{ + MockSrsFileReader fs; + SrsFlvVodStreamDecoder dec; + ASSERT_TRUE(ERROR_SUCCESS != dec.initialize(&fs)); +} + +VOID TEST(KernelFlvTest, FlvVSDecoderHeader) +{ + MockSrsFileReader fs; + SrsFlvVodStreamDecoder dec; + + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == dec.initialize(&fs)); + + // 9bytes + char flv_header[] = { + 'F', 'L', 'V', // Signatures "FLV" + (char)0x01, // File version (for example, 0x01 for FLV version 1) + (char)0x00, // 4, audio; 1, video; 5 audio+video. + (char)0x00, (char)0x00, (char)0x00, (char)0x09 // DataOffset UI32 The length of this header in bytes + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)0x00 }; + fs.mock_append_data(flv_header, 9); + fs.mock_append_data(pts, 4); + + char data[1024]; + fs.mock_reset_offset(); + + EXPECT_TRUE(ERROR_SUCCESS == dec.read_header_ext(data)); + EXPECT_TRUE(srs_bytes_equals(flv_header, data, 9)); +} + +VOID TEST(KernelFlvTest, FlvVSDecoderSequenceHeader) +{ + MockSrsFileReader fs; + SrsFlvVodStreamDecoder dec; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == dec.initialize(&fs)); + + if (true) { + // 11 bytes tag header + char tag_header[] = { + (char)18, // TagType UB [5], 18 = script data + (char)0x00, (char)0x00, (char)0x08, // 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. + }; + char md[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + fs.mock_append_data(tag_header, 11); + fs.mock_append_data(md, 8); + fs.mock_append_data(pts, 4); + } + if (true) { + // 11bytes tag header + char tag_header[] = { + (char)8, // TagType UB [5], 8 = audio + (char)0x00, (char)0x00, (char)0x08, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x30, // 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. + }; + char audio[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + fs.mock_append_data(tag_header, 11); + fs.mock_append_data(audio, 8); + fs.mock_append_data(pts, 4); + } + if (true) { + // 11bytes tag header + char tag_header[] = { + (char)9, // TagType UB [5], 9 = video + (char)0x00, (char)0x00, (char)0x08, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x30, // 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. + }; + char video[] = { + (char)0x01, (char)0x02, (char)0x03, (char)0x04, + (char)0x04, (char)0x03, (char)0x02, (char)0x01 + }; + char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)19 }; + fs.mock_append_data(tag_header, 11); + fs.mock_append_data(video, 8); + fs.mock_append_data(pts, 4); + } + + fs.mock_reset_offset(); + + int64_t start = 0; + int size = 0; + EXPECT_TRUE(ERROR_SUCCESS == dec.read_sequence_header_summary(&start, &size)); + EXPECT_TRUE(23 == start); + EXPECT_TRUE(46 == size); +} + +VOID TEST(KernelFlvTest, FlvVSDecoderSeek) +{ + MockSrsFileReader fs; + SrsFlvVodStreamDecoder dec; + ASSERT_TRUE(ERROR_SUCCESS == fs.open("")); + ASSERT_TRUE(ERROR_SUCCESS == dec.initialize(&fs)); + + // 11bytes tag header + char tag_header[] = { + (char)8, // TagType UB [5], 8 = audio + (char)0x00, (char)0x00, (char)0x08, // DataSize UI24 Length of the message. + (char)0x00, (char)0x00, (char)0x30, // 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. + }; + fs.mock_append_data(tag_header, 11); + EXPECT_TRUE(11 == fs.offset); + + EXPECT_TRUE(ERROR_SUCCESS == dec.lseek(0)); + EXPECT_TRUE(0 == fs.offset); + + EXPECT_TRUE(ERROR_SUCCESS == dec.lseek(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 3e6723f69..0038d5ed8 100644 --- a/trunk/src/utest/srs_utest_kernel.hpp +++ b/trunk/src/utest/srs_utest_kernel.hpp @@ -34,6 +34,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class MockSrsFileWriter : public SrsFileWriter { +public: + char* data; + int offset; public: MockSrsFileWriter(); virtual ~MockSrsFileWriter(); @@ -45,10 +48,17 @@ public: virtual int64_t tellg(); public: virtual int write(void* buf, size_t count, ssize_t* pnwrite); +// for mock +public: + void mock_reset_offset(); }; class MockSrsFileReader : public SrsFileReader { +public: + char* data; + int size; + int offset; public: MockSrsFileReader(); virtual ~MockSrsFileReader(); @@ -63,6 +73,11 @@ public: virtual int64_t filesize(); public: virtual int read(void* buf, size_t count, ssize_t* pnread); +// for mock +public: + // append data to current offset, modify the offset and size. + void mock_append_data(const char* _data, int _size); + void mock_reset_offset(); }; #endif