diff --git a/Makefile b/Makefile index 72cf0691..1d1aedde 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ H264ENC_INCLUDES = $(ENCODER_INCLUDES) -Icodec/console/enc/inc H264ENC_LDFLAGS = -L. -lencoder -lprocessing -lcommon H264ENC_DEPS = $(LIBPREFIX)encoder.$(LIBSUFFIX) $(LIBPREFIX)processing.$(LIBSUFFIX) $(LIBPREFIX)common.$(LIBSUFFIX) -CODEC_UNITTEST_LDFLAGS = -L. -lgtest -ldecoder -lcommon +CODEC_UNITTEST_LDFLAGS = -L. -lgtest -ldecoder -lcommon -lcrypto CODEC_UNITTEST_DEPS = $(LIBPREFIX)gtest.$(LIBSUFFIX) $(LIBPREFIX)decoder.$(LIBSUFFIX) $(LIBPREFIX)common.$(LIBSUFFIX) .PHONY: test diff --git a/res/test_vd_1d.264 b/res/test_vd_1d.264 new file mode 100644 index 00000000..ab7704ee Binary files /dev/null and b/res/test_vd_1d.264 differ diff --git a/res/test_vd_rc.264 b/res/test_vd_rc.264 new file mode 100644 index 00000000..07b8bb48 Binary files /dev/null and b/res/test_vd_rc.264 differ diff --git a/test/decoder_test.cpp b/test/decoder_test.cpp new file mode 100644 index 00000000..f6baee79 --- /dev/null +++ b/test/decoder_test.cpp @@ -0,0 +1,199 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "codec_def.h" +#include "codec_app_def.h" +#include "codec_api.h" + +#include "utils/BufferedData.h" + +static bool CompareHash(unsigned char(&digest)[SHA_DIGEST_LENGTH], + const char* hashStr) { + + char hashStrCmp[SHA_DIGEST_LENGTH * 2 + 1]; + for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) { + sprintf(&hashStrCmp[i*2], "%.2x", digest[i]); + } + hashStrCmp[SHA_DIGEST_LENGTH * 2] = '\0'; + return strncmp(hashStr, hashStrCmp, SHA_DIGEST_LENGTH * 2) == 0; +} + +static void UpdateHashFromPlane(SHA_CTX* ctx, const uint8_t* plane, + int width, int height, int stride) { + + for (int i = 0; i < height; i++) { + SHA1_Update(ctx, plane, width); + plane += stride; + } +} + +/** + * @return frame size (>= 0), or -1 for memory allocation error. + */ +static int ReadFrame(std::ifstream* file, BufferedData* buf) { + // start code of a frame is {0, 0, 0, 1} + int zeroCount = 0; + char b; + + while (file->read(&b, 1), file->gcount() == 1) { + if (!buf->Push(b)) { + return -1; + } + + if (buf->Length() <= 4) { + continue; + } + + if (zeroCount < 3) { + zeroCount = b != 0 ? 0 : zeroCount + 1; + } else { + if (b == 1) { + file->seekg(-4, file->cur); + return buf->Length() - 4; + } else if (b == 0) { + zeroCount = 3; + } else { + zeroCount = 0; + } + } + } + return buf->Length(); +} + +/** + * @return true if a frame is decoded successfully, otherwise false. + */ +static bool DecodeAndProcess(ISVCDecoder* decoder, const uint8_t* src, + int sliceSize, SHA_CTX* ctx) { + + void* data[3]; + SBufferInfo bufInfo; + memset(data, 0, sizeof(data)); + memset(&bufInfo, 0, sizeof(SBufferInfo)); + + DECODING_STATE rv = decoder->DecodeFrame(src, sliceSize, data, &bufInfo); + if (rv == dsErrorFree) { + if (bufInfo.iBufferStatus == 1) { + // y plane + UpdateHashFromPlane(ctx, static_cast(data[0]), + bufInfo.UsrData.sSystemBuffer.iWidth, + bufInfo.UsrData.sSystemBuffer.iHeight, + bufInfo.UsrData.sSystemBuffer.iStride[0]); + // u plane + UpdateHashFromPlane(ctx, static_cast(data[1]), + bufInfo.UsrData.sSystemBuffer.iWidth / 2, + bufInfo.UsrData.sSystemBuffer.iHeight / 2, + bufInfo.UsrData.sSystemBuffer.iStride[1]); + // v plane + UpdateHashFromPlane(ctx, static_cast(data[2]), + bufInfo.UsrData.sSystemBuffer.iWidth / 2, + bufInfo.UsrData.sSystemBuffer.iHeight / 2, + bufInfo.UsrData.sSystemBuffer.iStride[1]); + } + return true; + } else { + return false; + } +} + +static void CompareFileToHash(ISVCDecoder* decoder, + const char* fileName, const char* hashStr) { + + std::ifstream file(fileName, std::ios::in | std::ios::binary); + ASSERT_TRUE(file.is_open()); + + unsigned char digest[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + SHA1_Init(&ctx); + + BufferedData buf; + int sliceSize; + + while ((sliceSize = ReadFrame(&file, &buf)) > 0) { + if (DecodeAndProcess(decoder, buf.data(), sliceSize, &ctx)) { + buf.Clear(); + } else { + SHA1_Final(digest, &ctx); + FAIL() << "unable to decode frame"; + } + } + + if (sliceSize < 0) { + SHA1_Final(digest, &ctx); + FAIL() << "unable to allocate memory"; + } + + int32_t iEndOfStreamFlag = true; + decoder->SetOption(DECODER_OPTION_END_OF_STREAM, &iEndOfStreamFlag); + + // Get pending last frame + if (!DecodeAndProcess(decoder, NULL, 0, &ctx)) { + SHA1_Final(digest, &ctx); + FAIL() << "unable to decode last frame"; + } + + SHA1_Final(digest, &ctx); + ASSERT_TRUE(CompareHash(digest, hashStr)); +} + +class DecoderInitTest : public ::testing::Test { +public: + DecoderInitTest() : decoder_(NULL) {} + + virtual void SetUp() { + long rv = CreateDecoder(&decoder_); + ASSERT_EQ(0, rv); + ASSERT_TRUE(decoder_ != NULL); + + SDecodingParam decParam; + memset(&decParam, 0, sizeof(SDecodingParam)); + decParam.iOutputColorFormat = videoFormatI420; + decParam.uiTargetDqLayer = UCHAR_MAX; + decParam.uiEcActiveFlag = 1; + decParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT; + + rv = decoder_->Initialize(&decParam, INIT_TYPE_PARAMETER_BASED); + ASSERT_EQ(0, rv); + } + + virtual void TearDown() { + if (decoder_ != NULL) { + decoder_->Uninitialize(); + DestroyDecoder(decoder_); + } + } + +protected: + ISVCDecoder* decoder_; +}; + + +TEST_F(DecoderInitTest, JustInit) { +} + +struct FileParam { + const char* fileName; + const char* hashStr; +}; + +class DecoderOutputTest : public DecoderInitTest, + public ::testing::WithParamInterface { +}; + +TEST_P(DecoderOutputTest, CompareOutput) { + FileParam p = GetParam(); + CompareFileToHash(decoder_, p.fileName, p.hashStr); +} + +static const FileParam kFileParamArray[] = { + {"res/test_vd_1d.264", "5827d2338b79ff82cd091c707823e466197281d3"}, + {"res/test_vd_rc.264", "eea02e97bfec89d0418593a8abaaf55d02eaa1ca"} +}; + +INSTANTIATE_TEST_CASE_P(DecodeFile, DecoderOutputTest, + ::testing::ValuesIn(kFileParamArray)); diff --git a/test/simple_test.cpp b/test/simple_test.cpp index d923bf46..eb79e817 100644 --- a/test/simple_test.cpp +++ b/test/simple_test.cpp @@ -1,38 +1,4 @@ #include -#if defined (WIN32) -#include -#include -#else -#include -#endif -#include -#include -#include - -#include "codec_def.h" -#include "codec_app_def.h" -#include "codec_api.h" - -class CodecTest : public ::testing::Test { - public: - CodecTest() : decoder_ (NULL) {} - - ~CodecTest() { - if (decoder_) DestroyDecoder (decoder_); - } - - void SetUp() { - long rv = CreateDecoder (&decoder_); - ASSERT_EQ (0, rv); - ASSERT_TRUE (decoder_); - } - - protected: - ISVCDecoder* decoder_; -}; - -TEST_F (CodecTest, JustInit) { -} int main (int argc, char** argv) { testing::InitGoogleTest (&argc, argv); diff --git a/test/utils/BufferedData.h b/test/utils/BufferedData.h new file mode 100644 index 00000000..aaf32fb7 --- /dev/null +++ b/test/utils/BufferedData.h @@ -0,0 +1,65 @@ +#ifndef __BUFFEREDDATA_H__ +#define __BUFFEREDDATA_H__ + +#include +#include + +class BufferedData +{ +public: + BufferedData() : data_(NULL), capacity_(0), length_(0) { + } + + ~BufferedData() { + free(data_); + } + + bool Push(uint8_t c) { + if (!EnsureCapacity(length_ + 1)) { + return false; + } + data_[length_++] = c; + return true; + } + + void Clear() { + length_ = 0; + } + + void SetLength(size_t newLen) { + if (EnsureCapacity(newLen)) { + length_ = newLen; + } + } + + size_t Length() const { + return length_; + } + + uint8_t* data() { + return data_; + } + +private: + bool EnsureCapacity(size_t capacity) { + if (capacity > capacity_) { + size_t newsize = capacity * 2; + + uint8_t* data = static_cast(realloc(data_, newsize)); + + if (!data) + return false; + + data_ = data; + capacity_ = newsize; + return true; + } + return true; + } + + uint8_t* data_; + size_t capacity_; + size_t length_; +}; + +#endif //__BUFFEREDDATA_H__