shithub: openh264

Download patch

ref: cb0053f8608a61a52f155ed7fe4c39093ae3f816
parent: 61f1bac9ff383ba5958c838b7921f846d2f4277c
author: jwwang <jwwang@mozilla.com>
date: Tue Dec 24 11:25:30 EST 2013

Add decoder test

--- a/Makefile
+++ b/Makefile
@@ -70,7 +70,7 @@
 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
binary files /dev/null b/res/test_vd_1d.264 differ
binary files /dev/null b/res/test_vd_rc.264 differ
--- /dev/null
+++ b/test/decoder_test.cpp
@@ -1,0 +1,199 @@
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <limits.h>
+#include <fstream>
+#include <openssl/sha.h>
+
+#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<uint8_t*>(data[0]),
+          bufInfo.UsrData.sSystemBuffer.iWidth,
+          bufInfo.UsrData.sSystemBuffer.iHeight,
+          bufInfo.UsrData.sSystemBuffer.iStride[0]);
+      // u plane
+      UpdateHashFromPlane(ctx, static_cast<uint8_t*>(data[1]),
+          bufInfo.UsrData.sSystemBuffer.iWidth / 2,
+          bufInfo.UsrData.sSystemBuffer.iHeight / 2,
+          bufInfo.UsrData.sSystemBuffer.iStride[1]);
+      // v plane
+      UpdateHashFromPlane(ctx, static_cast<uint8_t*>(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<FileParam> {
+};
+
+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));
--- a/test/simple_test.cpp
+++ b/test/simple_test.cpp
@@ -1,38 +1,4 @@
 #include <gtest/gtest.h>
-#if defined (WIN32)
-#include <windows.h>
-#include <tchar.h>
-#else
-#include <string.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#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);
--- /dev/null
+++ b/test/utils/BufferedData.h
@@ -1,0 +1,65 @@
+#ifndef __BUFFEREDDATA_H__
+#define __BUFFEREDDATA_H__
+
+#include <stddef.h>
+#include <stdlib.h>
+
+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<uint8_t*>(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__