shithub: openh264

Download patch

ref: 8247eef9bb0962608f70ec217c3891d2018d8714
parent: db6ce9c3d8692b414f2e0ace8843578b91d58385
author: jwwang <jwwang@mozilla.com>
date: Tue Jan 14 10:48:20 EST 2014

refactor and add decode_encode_test to pipeline decoder outoput to encoder test

--- /dev/null
+++ b/test/BaseDecoderTest.cpp
@@ -1,0 +1,128 @@
+#include <fstream>
+#include <gtest/gtest.h>
+#include "codec_def.h"
+#include "codec_app_def.h"
+#include "utils/BufferedData.h"
+#include "BaseDecoderTest.h"
+
+static void ReadFrame(std::ifstream* file, BufferedData* buf) {
+  // start code of a frame is {0, 0, 0, 1}
+  int zeroCount = 0;
+  char b;
+
+  buf->Clear();
+  for (;;) {
+    file->read(&b, 1);
+    if (file->gcount() != 1) { // end of file
+      return;
+    }
+    if (!buf->Push(b)) {
+      FAIL() << "unable to allocate memory";
+    }
+
+    if (buf->Length() <= 4) {
+      continue;
+    }
+
+    if (zeroCount < 3) {
+      zeroCount = b != 0 ? 0 : zeroCount + 1;
+    } else {
+      if (b == 1) {
+        if (file->seekg(-4, file->cur).good()) {
+          buf->SetLength(buf->Length() - 4);
+          return;
+        } else {
+          FAIL() << "unable to seek file";
+        }
+      } else if (b == 0) {
+        zeroCount = 3;
+      } else {
+        zeroCount = 0;
+      }
+    }
+  }
+}
+
+BaseDecoderTest::BaseDecoderTest() : decoder_(NULL) {}
+
+void BaseDecoderTest::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);
+}
+
+void BaseDecoderTest::TearDown() {
+  if (decoder_ != NULL) {
+    decoder_->Uninitialize();
+    DestroyDecoder(decoder_);
+  }
+}
+
+
+void BaseDecoderTest::DecodeFrame(const uint8_t* src, int sliceSize, Callback* cbk) {
+  void* data[3];
+  SBufferInfo bufInfo;
+  memset(data, 0, sizeof(data));
+  memset(&bufInfo, 0, sizeof(SBufferInfo));
+
+  DECODING_STATE rv = decoder_->DecodeFrame(src, sliceSize, data, &bufInfo);
+  ASSERT_TRUE(rv == dsErrorFree);
+
+  if (bufInfo.iBufferStatus == 1 && cbk != NULL) {
+    const Frame frame = {
+        { // y plane
+            static_cast<uint8_t*>(data[0]),
+            bufInfo.UsrData.sSystemBuffer.iWidth,
+            bufInfo.UsrData.sSystemBuffer.iHeight,
+            bufInfo.UsrData.sSystemBuffer.iStride[0]
+        },
+        { // u plane
+            static_cast<uint8_t*>(data[1]),
+            bufInfo.UsrData.sSystemBuffer.iWidth / 2,
+            bufInfo.UsrData.sSystemBuffer.iHeight / 2,
+            bufInfo.UsrData.sSystemBuffer.iStride[1]
+        },
+        { // v plane
+            static_cast<uint8_t*>(data[2]),
+            bufInfo.UsrData.sSystemBuffer.iWidth / 2,
+            bufInfo.UsrData.sSystemBuffer.iHeight / 2,
+            bufInfo.UsrData.sSystemBuffer.iStride[1]
+        },
+    };
+    cbk->onDecodeFrame(frame);
+  }
+}
+void BaseDecoderTest::DecodeFile(const char* fileName, Callback* cbk) {
+  std::ifstream file(fileName, std::ios::in | std::ios::binary);
+  ASSERT_TRUE(file.is_open());
+
+  for (BufferedData buf;;) {
+    ReadFrame(&file, &buf);
+    if (::testing::Test::HasFatalFailure()) {
+      return;
+    }
+    if (buf.Length() == 0) {
+      break;
+    }
+    DecodeFrame(buf.data(), buf.Length(), cbk);
+    if (::testing::Test::HasFatalFailure()) {
+      return;
+    }
+  }
+
+  int32_t iEndOfStreamFlag = 1;
+  decoder_->SetOption(DECODER_OPTION_END_OF_STREAM, &iEndOfStreamFlag);
+
+  // Get pending last frame
+  DecodeFrame(NULL, 0, cbk);
+}
--- /dev/null
+++ b/test/BaseDecoderTest.h
@@ -1,0 +1,38 @@
+#ifndef __BASEDECODERTEST_H__
+#define __BASEDECODERTEST_H__
+
+#include <stdint.h>
+#include <limits.h>
+#include "codec_api.h"
+
+class BaseDecoderTest {
+ public:
+  struct Plane {
+    const uint8_t* data;
+    int width;
+    int height;
+    int stride;
+  };
+
+  struct Frame {
+    Plane y;
+    Plane u;
+    Plane v;
+  };
+
+  struct Callback {
+    virtual void onDecodeFrame(const Frame& frame) = 0;
+  };
+
+  BaseDecoderTest();
+  void SetUp();
+  void TearDown();
+  void DecodeFile(const char* fileName, Callback* cbk);
+
+ private:
+  void DecodeFrame(const uint8_t* src, int sliceSize, Callback* cbk);
+
+  ISVCDecoder* decoder_;
+};
+
+#endif //__BASEDECODERTEST_H__
--- /dev/null
+++ b/test/BaseEncoderTest.cpp
@@ -1,0 +1,79 @@
+#include <fstream>
+#include <gtest/gtest.h>
+#include "codec_def.h"
+#include "utils/BufferedData.h"
+#include "BaseEncoderTest.h"
+
+static int InitWithParam(ISVCEncoder* encoder, int width,
+    int height, float frameRate) {
+  SVCEncodingParam param;
+  memset (&param, 0, sizeof(SVCEncodingParam));
+
+  param.sSpatialLayers[0].iVideoWidth  = width;
+  param.sSpatialLayers[0].iVideoHeight = height;
+  param.sSpatialLayers[0].fFrameRate = frameRate;
+  param.sSpatialLayers[0].iQualityLayerNum = 1;
+  param.sSpatialLayers[0].iSpatialBitrate = 600000;
+
+  SSliceConfig* sliceCfg = &param.sSpatialLayers[0].sSliceCfg;
+  sliceCfg->sSliceArgument.uiSliceNum = 1;
+  sliceCfg->sSliceArgument.uiSliceSizeConstraint = 1500;
+  sliceCfg->sSliceArgument.uiSliceMbNum[0] = 960;
+
+  param.fFrameRate = param.sSpatialLayers[0].fFrameRate;
+  param.iPicWidth = param.sSpatialLayers[0].iVideoWidth;
+  param.iPicHeight = param.sSpatialLayers[0].iVideoHeight;
+  param.iTargetBitrate = 5000000;
+  param.iTemporalLayerNum = 3;
+  param.iSpatialLayerNum = 1;
+  param.bEnableBackgroundDetection = true;
+  param.bEnableLongTermReference = true;
+  param.iLtrMarkPeriod = 30;
+  param.iInputCsp = videoFormatI420;
+  param.bEnableSpsPpsIdAddition = true;
+
+  return encoder->Initialize(&param, INIT_TYPE_PARAMETER_BASED);
+}
+
+BaseEncoderTest::BaseEncoderTest() : encoder_(NULL) {}
+
+void BaseEncoderTest::SetUp() {
+  int rv = CreateSVCEncoder(&encoder_);
+  ASSERT_EQ(0, rv);
+  ASSERT_TRUE(encoder_ != NULL);
+}
+
+void BaseEncoderTest::TearDown() {
+  if (encoder_) {
+    encoder_->Uninitialize();
+    DestroySVCEncoder(encoder_);
+  }
+}
+
+void BaseEncoderTest::EncodeFile(const char* fileName, int width, int height,
+    float frameRate, Callback* cbk) {
+  std::ifstream file(fileName, std::ios::in | std::ios::binary);
+  ASSERT_TRUE(file.is_open());
+
+  int rv = InitWithParam(encoder_, width, height, frameRate);
+  ASSERT_TRUE(rv == cmResultSuccess);
+
+  // I420: 1(Y) + 1/4(U) + 1/4(V)
+  int frameSize = width * height * 3 / 2;
+
+  BufferedData buf;
+  buf.SetLength(frameSize);
+  ASSERT_TRUE(buf.Length() == frameSize);
+  char* data = reinterpret_cast<char*>(buf.data());
+
+  SFrameBSInfo info;
+  memset(&info, 0, sizeof(SFrameBSInfo));
+
+  while (file.read(data, frameSize), file.gcount() == frameSize) {
+    rv = encoder_->EncodeFrame(buf.data(), &info);
+    ASSERT_TRUE(rv != videoFrameTypeInvalid);
+    if (rv != videoFrameTypeSkip && cbk != NULL) {
+      cbk->onEncodeFrame(info);
+    }
+  }
+}
--- /dev/null
+++ b/test/BaseEncoderTest.h
@@ -1,0 +1,22 @@
+#ifndef __BASEENCODERTEST_H__
+#define __BASEENCODERTEST_H__
+
+#include "codec_api.h"
+#include "codec_app_def.h"
+
+class BaseEncoderTest {
+ public:
+  struct Callback {
+    virtual void onEncodeFrame(const SFrameBSInfo& frameInfo) = 0;
+  };
+
+  BaseEncoderTest();
+  void SetUp();
+  void TearDown();
+  void EncodeFile(const char* fileName, int width, int height, float frameRate, Callback* cbk);
+
+ private:
+  ISVCEncoder* encoder_;
+};
+
+#endif //__BASEENCODERTEST_H__
--- /dev/null
+++ b/test/decode_encode_test.cpp
@@ -1,0 +1,108 @@
+#include <cstdio>
+#include <gtest/gtest.h>
+#include "codec_def.h"
+#include "utils/HashFunctions.h"
+#include "BaseDecoderTest.h"
+#include "BaseEncoderTest.h"
+
+static void UpdateHashFromFrame(const SFrameBSInfo& info, SHA_CTX* ctx) {
+  for (int i = 0; i < info.iLayerNum; ++i) {
+    const SLayerBSInfo& layerInfo = info.sLayerInfo[i];
+    int layerSize = 0;
+    for (int j = 0; j < layerInfo.iNalCount; ++j) {
+      layerSize += layerInfo.iNalLengthInByte[j];
+    }
+    SHA1_Update(ctx, layerInfo.pBsBuf, layerSize);
+  }
+}
+
+static void WritePlaneToFile(FILE* file, const uint8_t* plane,
+    int width, int height, int stride) {
+  for (int i = 0; i < height; i++) {
+    fwrite(plane, 1, width, file);
+    plane += stride;
+  }
+}
+
+struct DecodeEncodeFileParam {
+  const char* fileName;
+  const char* hashStr;
+  int width;
+  int height;
+  float frameRate;
+};
+
+class DecodeEncodeTest : public ::testing::TestWithParam<DecodeEncodeFileParam>,
+    public BaseDecoderTest, public BaseDecoderTest::Callback,
+    public BaseEncoderTest , public BaseEncoderTest::Callback {
+ public:
+  DecodeEncodeTest() : tmpFileName_(NULL), tmpFile_(NULL) {}
+
+  virtual void SetUp() {
+    BaseDecoderTest::SetUp();
+    if (HasFatalFailure()) {
+      return;
+    }
+    BaseEncoderTest::SetUp();
+    if (HasFatalFailure()) {
+      return;
+    }
+
+    tmpFileName_ = tmpnam(NULL);
+    tmpFile_ = fopen(tmpFileName_, "wb");
+    ASSERT_TRUE(tmpFile_ != NULL);
+
+    SHA1_Init(&ctx_);
+  }
+
+  virtual void TearDown() {
+    if (tmpFile_ != NULL) {
+      fclose(tmpFile_);
+      remove(tmpFileName_);
+    }
+    BaseDecoderTest::TearDown();
+    BaseEncoderTest::TearDown();
+  }
+
+  virtual void onDecodeFrame(const Frame& frame) {
+    const Plane& y = frame.y;
+    const Plane& u = frame.u;
+    const Plane& v = frame.v;
+    WritePlaneToFile(tmpFile_, y.data, y.width, y.height, y.stride);
+    WritePlaneToFile(tmpFile_, u.data, u.width, u.height, u.stride);
+    WritePlaneToFile(tmpFile_, v.data, v.width, v.height, v.stride);
+  }
+
+  virtual void onEncodeFrame(const SFrameBSInfo& frameInfo) {
+    UpdateHashFromFrame(frameInfo, &ctx_);
+  }
+
+ protected:
+  SHA_CTX ctx_;
+  const char* tmpFileName_;
+  FILE* tmpFile_;
+};
+
+TEST_P(DecodeEncodeTest, CompareOutput) {
+  DecodeEncodeFileParam p = GetParam();
+
+  DecodeFile(p.fileName, this);
+  if (HasFatalFailure()) {
+    return;
+  }
+
+  EncodeFile(tmpFileName_, p.width, p.height, p.frameRate, this);
+  unsigned char digest[SHA_DIGEST_LENGTH];
+  SHA1_Final(digest, &ctx_);
+  if (!HasFatalFailure()) {
+    ASSERT_TRUE(CompareHash(digest, p.hashStr));
+  }
+}
+
+static const DecodeEncodeFileParam kFileParamArray[] = {
+  {"res/test_vd_1d.264", "c17a760c3bc5d8682aae7f49dddebd2593071711", 320, 192, 12.0f},
+  {"res/test_vd_rc.264", "41e3e90f159fade7a89a4540c4fb9f8ac8f4d7da", 320, 192, 12.0f},
+};
+
+INSTANTIATE_TEST_CASE_P(DecodeEncodeFile, DecodeEncodeTest,
+    ::testing::ValuesIn(kFileParamArray));
--- a/test/decoder_test.cpp
+++ b/test/decoder_test.cpp
@@ -1,12 +1,6 @@
 #include <gtest/gtest.h>
-#include <stdint.h>
-#include <limits.h>
-#include <fstream>
-
-#include "codec_api.h"
-
-#include "utils/BufferedData.h"
 #include "utils/HashFunctions.h"
+#include "BaseDecoderTest.h"
 
 static void UpdateHashFromPlane(SHA_CTX* ctx, const uint8_t* plane,
     int width, int height, int stride) {
@@ -16,168 +10,55 @@
   }
 }
 
-/**
- * @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;
 
-  for (;;) {
-    file->read(&b, 1);
-    if (file->gcount() != 1) {
-      break;
-    }
-    if (!buf->Push(b)) {
-      return -1;
-    }
-
-    if (buf->Length() <= 4) {
-      continue;
-    }
-
-    if (zeroCount < 3) {
-      zeroCount = b != 0 ? 0 : zeroCount + 1;
-    } else {
-      if (b == 1) {
-        if (file->seekg(-4, file->cur).good()) {
-          return buf->Length() - 4;
-        } else {
-          // seeking fails
-          return -1;
-        }
-      } 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->DecodeFrame2(src, sliceSize, data, &bufInfo);
-  if (rv != dsErrorFree) {
-    return false;
-  }
-
-  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;
-}
-
-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 = 1;
-  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 {
+class DecoderInitTest : public ::testing::Test, public BaseDecoderTest {
  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);
+    BaseDecoderTest::SetUp();
   }
-
   virtual void TearDown() {
-    if (decoder_ != NULL) {
-      decoder_->Uninitialize();
-      DestroyDecoder(decoder_);
-    }
+    BaseDecoderTest::TearDown();
   }
-
- protected:
-  ISVCDecoder* decoder_;
 };
 
+TEST_F(DecoderInitTest, JustInit) {}
 
-TEST_F(DecoderInitTest, JustInit) {
-}
-
 struct FileParam {
   const char* fileName;
   const char* hashStr;
 };
 
-class DecoderOutputTest : public DecoderInitTest,
-    public ::testing::WithParamInterface<FileParam> {
+class DecoderOutputTest : public ::testing::WithParamInterface<FileParam>,
+    public DecoderInitTest, public BaseDecoderTest::Callback {
+ public:
+  virtual void SetUp() {
+    DecoderInitTest::SetUp();
+    if (HasFatalFailure()) {
+      return;
+    }
+    SHA1_Init(&ctx_);
+  }
+  virtual void onDecodeFrame(const Frame& frame) {
+    const Plane& y = frame.y;
+    const Plane& u = frame.u;
+    const Plane& v = frame.v;
+    UpdateHashFromPlane(&ctx_, y.data, y.width, y.height, y.stride);
+    UpdateHashFromPlane(&ctx_, u.data, u.width, u.height, u.stride);
+    UpdateHashFromPlane(&ctx_, v.data, v.width, v.height, v.stride);
+  }
+ protected:
+  SHA_CTX ctx_;
 };
 
 TEST_P(DecoderOutputTest, CompareOutput) {
   FileParam p = GetParam();
-  CompareFileToHash(decoder_, p.fileName, p.hashStr);
+  DecodeFile(p.fileName, this);
+
+  unsigned char digest[SHA_DIGEST_LENGTH];
+  SHA1_Final(digest, &ctx_);
+  if (!HasFatalFailure()) {
+    ASSERT_TRUE(CompareHash(digest, p.hashStr));
+  }
 }
 
 static const FileParam kFileParamArray[] = {
--- a/test/encoder_test.cpp
+++ b/test/encoder_test.cpp
@@ -1,43 +1,7 @@
 #include <gtest/gtest.h>
-#include <stdint.h>
-#include <fstream>
-
-#include "codec_api.h"
-
-#include "utils/BufferedData.h"
 #include "utils/HashFunctions.h"
+#include "BaseEncoderTest.h"
 
-static int InitWithParam(ISVCEncoder* encoder, int width,
-    int height, float frameRate) {
-  SVCEncodingParam param;
-  memset (&param, 0, sizeof(SVCEncodingParam));
-
-  param.sSpatialLayers[0].iVideoWidth  = width;
-  param.sSpatialLayers[0].iVideoHeight = height;
-  param.sSpatialLayers[0].fFrameRate = frameRate;
-  param.sSpatialLayers[0].iQualityLayerNum = 1;
-  param.sSpatialLayers[0].iSpatialBitrate = 600000;
-
-  SSliceConfig* sliceCfg = &param.sSpatialLayers[0].sSliceCfg;
-  sliceCfg->sSliceArgument.uiSliceNum = 1;
-  sliceCfg->sSliceArgument.uiSliceSizeConstraint = 1500;
-  sliceCfg->sSliceArgument.uiSliceMbNum[0] = 960;
-
-  param.fFrameRate = param.sSpatialLayers[0].fFrameRate;
-  param.iPicWidth = param.sSpatialLayers[0].iVideoWidth;
-  param.iPicHeight = param.sSpatialLayers[0].iVideoHeight;
-  param.iTargetBitrate = 5000000;
-  param.iTemporalLayerNum = 3;
-  param.iSpatialLayerNum = 1;
-  param.bEnableBackgroundDetection = true;
-  param.bEnableLongTermReference = true;
-  param.iLtrMarkPeriod = 30;
-  param.iInputCsp = videoFormatI420;
-  param.bEnableSpsPpsIdAddition = true;
-
-  return encoder->Initialize(&param, INIT_TYPE_PARAMETER_BASED);
-}
-
 static void UpdateHashFromFrame(const SFrameBSInfo& info, SHA_CTX* ctx) {
   for (int i = 0; i < info.iLayerNum; ++i) {
     const SLayerBSInfo& layerInfo = info.sLayerInfo[i];
@@ -49,68 +13,17 @@
   }
 }
 
-static void CompareFileToHash(ISVCEncoder* encoder,
-    const char* fileName, const char* hashStr,
-    int width, int height, float frameRate) {
-  std::ifstream file(fileName, std::ios::in | std::ios::binary);
-  ASSERT_TRUE(file.is_open());
-
-  int rv = InitWithParam(encoder, width, height, frameRate);
-  ASSERT_TRUE(rv == cmResultSuccess);
-
-  // I420: 1(Y) + 1/4(U) + 1/4(V)
-  int frameSize = width * height * 3 / 2;
-
-  BufferedData buf;
-  buf.SetLength(frameSize);
-  ASSERT_TRUE(buf.Length() == frameSize);
-  char* data = reinterpret_cast<char*>(buf.data());
-
-  SFrameBSInfo info;
-  memset(&info, 0, sizeof(SFrameBSInfo));
-
-  unsigned char digest[SHA_DIGEST_LENGTH];
-  SHA_CTX ctx;
-  SHA1_Init(&ctx);
-
-  while (file.read(data, frameSize), file.gcount() == frameSize) {
-    rv = encoder->EncodeFrame(buf.data(), &info);
-    if (rv == videoFrameTypeInvalid) {
-      SHA1_Final(digest, &ctx);
-      FAIL() << "unable to encode frame";
-    }
-    if (rv != videoFrameTypeSkip) {
-      UpdateHashFromFrame(info, &ctx);
-    }
-  }
-
-  SHA1_Final(digest, &ctx);
-  ASSERT_TRUE(CompareHash(digest, hashStr));
-}
-
-class EncoderBaseTest : public ::testing::Test {
+class EncoderInitTest : public ::testing::Test, public BaseEncoderTest {
  public:
-  EncoderBaseTest() : encoder_(NULL) {}
-
   virtual void SetUp() {
-    int rv = CreateSVCEncoder(&encoder_);
-    ASSERT_EQ(0, rv);
-    ASSERT_TRUE(encoder_ != NULL);
+    BaseEncoderTest::SetUp();
   }
-
   virtual void TearDown() {
-    if (encoder_) {
-      encoder_->Uninitialize();
-      DestroySVCEncoder(encoder_);
-    }
+    BaseEncoderTest::TearDown();
   }
-
- protected:
-  ISVCEncoder* encoder_;
 };
 
-TEST_F(EncoderBaseTest, JustInit) {
-}
+TEST_F(EncoderInitTest, JustInit) {}
 
 struct EncodeFileParam {
   const char* fileName;
@@ -120,14 +33,33 @@
   float frameRate;
 };
 
-class EncoderOutputTest : public EncoderBaseTest ,
-    public ::testing::WithParamInterface<EncodeFileParam> {
+class EncoderOutputTest : public ::testing::WithParamInterface<EncodeFileParam>,
+    public EncoderInitTest , public BaseEncoderTest::Callback {
+ public:
+  virtual void SetUp() {
+    EncoderInitTest::SetUp();
+    if (HasFatalFailure()) {
+      return;
+    }
+    SHA1_Init(&ctx_);
+  }
+  virtual void onEncodeFrame(const SFrameBSInfo& frameInfo) {
+    UpdateHashFromFrame(frameInfo, &ctx_);
+  }
+ protected:
+  SHA_CTX ctx_;
 };
 
 
 TEST_P(EncoderOutputTest, CompareOutput) {
   EncodeFileParam p = GetParam();
-  CompareFileToHash(encoder_, p.fileName, p.hashStr, p.width, p.height, p.frameRate);
+  EncodeFile(p.fileName, p.width, p.height, p.frameRate, this);
+
+  unsigned char digest[SHA_DIGEST_LENGTH];
+  SHA1_Final(digest, &ctx_);
+  if (!HasFatalFailure()) {
+    ASSERT_TRUE(CompareHash(digest, p.hashStr));
+  }
 }
 
 static const EncodeFileParam kFileParamArray[] = {
--- a/test/targets.mk
+++ b/test/targets.mk
@@ -1,8 +1,11 @@
 CODEC_UNITTEST_SRCDIR=test
 CODEC_UNITTEST_CPP_SRCS=\
-	$(CODEC_UNITTEST_SRCDIR)/./decoder_test.cpp\
+	$(CODEC_UNITTEST_SRCDIR)/./BaseDecoderTest.cpp\
 	$(CODEC_UNITTEST_SRCDIR)/./encoder_test.cpp\
+	$(CODEC_UNITTEST_SRCDIR)/./decode_encode_test.cpp\
 	$(CODEC_UNITTEST_SRCDIR)/./simple_test.cpp\
+	$(CODEC_UNITTEST_SRCDIR)/./decoder_test.cpp\
+	$(CODEC_UNITTEST_SRCDIR)/./BaseEncoderTest.cpp\
 
 CODEC_UNITTEST_OBJS += $(CODEC_UNITTEST_CPP_SRCS:.cpp=.o)
 OBJS += $(CODEC_UNITTEST_OBJS)
--- a/test/utils/BufferedData.h
+++ b/test/utils/BufferedData.h
@@ -1,6 +1,9 @@
 #ifndef __BUFFEREDDATA_H__
 #define __BUFFEREDDATA_H__
 
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
 
 class BufferedData {
  public:
--- a/test/utils/HashFunctions.h
+++ b/test/utils/HashFunctions.h
@@ -1,6 +1,8 @@
 #ifndef __HASHFUNCTIONS_H__
 #define __HASHFUNCTIONS_H__
 
+#include <stdio.h>
+#include <string.h>
 #include <openssl/sha.h>
 
 static bool CompareHash(const unsigned char* digest, const char* hashStr) {