Home | History | Annotate | Download | only in vp8
      1 /*
      2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "testing/gtest/include/gtest/gtest.h"
     12 #include "webrtc/base/checks.h"
     13 #include "webrtc/base/scoped_ptr.h"
     14 #include "webrtc/common_video/include/video_image.h"
     15 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
     16 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
     17 #include "webrtc/system_wrappers/include/tick_util.h"
     18 #include "webrtc/test/testsupport/fileutils.h"
     19 #include "webrtc/test/testsupport/metrics/video_metrics.h"
     20 #include "webrtc/tools/simple_command_line_parser.h"
     21 #include "webrtc/video_frame.h"
     22 
     23 class Vp8SequenceCoderEncodeCallback : public webrtc::EncodedImageCallback {
     24  public:
     25   explicit Vp8SequenceCoderEncodeCallback(FILE* encoded_file)
     26       : encoded_file_(encoded_file), encoded_bytes_(0) {}
     27   ~Vp8SequenceCoderEncodeCallback();
     28   int Encoded(const webrtc::EncodedImage& encoded_image,
     29               const webrtc::CodecSpecificInfo* codecSpecificInfo,
     30               const webrtc::RTPFragmentationHeader*);
     31   // Returns the encoded image.
     32   webrtc::EncodedImage encoded_image() { return encoded_image_; }
     33   size_t encoded_bytes() { return encoded_bytes_; }
     34 
     35  private:
     36   webrtc::EncodedImage encoded_image_;
     37   FILE* encoded_file_;
     38   size_t encoded_bytes_;
     39 };
     40 
     41 Vp8SequenceCoderEncodeCallback::~Vp8SequenceCoderEncodeCallback() {
     42   delete[] encoded_image_._buffer;
     43   encoded_image_._buffer = NULL;
     44 }
     45 int Vp8SequenceCoderEncodeCallback::Encoded(
     46     const webrtc::EncodedImage& encoded_image,
     47     const webrtc::CodecSpecificInfo* codecSpecificInfo,
     48     const webrtc::RTPFragmentationHeader* fragmentation) {
     49   if (encoded_image_._size < encoded_image._size) {
     50     delete[] encoded_image_._buffer;
     51     encoded_image_._buffer = NULL;
     52     encoded_image_._buffer = new uint8_t[encoded_image._size];
     53     encoded_image_._size = encoded_image._size;
     54   }
     55   memcpy(encoded_image_._buffer, encoded_image._buffer, encoded_image._size);
     56   encoded_image_._length = encoded_image._length;
     57   if (encoded_file_ != NULL) {
     58     if (fwrite(encoded_image._buffer, 1, encoded_image._length,
     59                encoded_file_) != encoded_image._length) {
     60       return -1;
     61     }
     62   }
     63   encoded_bytes_ += encoded_image_._length;
     64   return 0;
     65 }
     66 
     67 // TODO(mikhal): Add support for varying the frame size.
     68 class Vp8SequenceCoderDecodeCallback : public webrtc::DecodedImageCallback {
     69  public:
     70   explicit Vp8SequenceCoderDecodeCallback(FILE* decoded_file)
     71       : decoded_file_(decoded_file) {}
     72   int32_t Decoded(webrtc::VideoFrame& frame) override;
     73   int32_t Decoded(webrtc::VideoFrame& frame, int64_t decode_time_ms) override {
     74     RTC_NOTREACHED();
     75     return -1;
     76   }
     77   bool DecodeComplete();
     78 
     79  private:
     80   FILE* decoded_file_;
     81 };
     82 
     83 int Vp8SequenceCoderDecodeCallback::Decoded(webrtc::VideoFrame& image) {
     84   EXPECT_EQ(0, webrtc::PrintVideoFrame(image, decoded_file_));
     85   return 0;
     86 }
     87 
     88 int SequenceCoder(webrtc::test::CommandLineParser* parser) {
     89   int width = strtol((parser->GetFlag("w")).c_str(), NULL, 10);
     90   int height = strtol((parser->GetFlag("h")).c_str(), NULL, 10);
     91   int framerate = strtol((parser->GetFlag("f")).c_str(), NULL, 10);
     92 
     93   if (width <= 0 || height <= 0 || framerate <= 0) {
     94     fprintf(stderr, "Error: Resolution cannot be <= 0!\n");
     95     return -1;
     96   }
     97   int target_bitrate = strtol((parser->GetFlag("b")).c_str(), NULL, 10);
     98   if (target_bitrate <= 0) {
     99     fprintf(stderr, "Error: Bit-rate cannot be <= 0!\n");
    100     return -1;
    101   }
    102 
    103   // SetUp
    104   // Open input file.
    105   std::string encoded_file_name = parser->GetFlag("encoded_file");
    106   FILE* encoded_file = fopen(encoded_file_name.c_str(), "wb");
    107   if (encoded_file == NULL) {
    108     fprintf(stderr, "Error: Cannot open encoded file\n");
    109     return -1;
    110   }
    111   std::string input_file_name = parser->GetFlag("input_file");
    112   FILE* input_file = fopen(input_file_name.c_str(), "rb");
    113   if (input_file == NULL) {
    114     fprintf(stderr, "Error: Cannot open input file\n");
    115     return -1;
    116   }
    117   // Open output file.
    118   std::string output_file_name = parser->GetFlag("output_file");
    119   FILE* output_file = fopen(output_file_name.c_str(), "wb");
    120   if (output_file == NULL) {
    121     fprintf(stderr, "Error: Cannot open output file\n");
    122     return -1;
    123   }
    124 
    125   // Get range of frames: will encode num_frames following start_frame).
    126   int start_frame = strtol((parser->GetFlag("start_frame")).c_str(), NULL, 10);
    127   int num_frames = strtol((parser->GetFlag("num_frames")).c_str(), NULL, 10);
    128 
    129   // Codec SetUp.
    130   webrtc::VideoCodec inst;
    131   memset(&inst, 0, sizeof(inst));
    132   webrtc::VP8Encoder* encoder = webrtc::VP8Encoder::Create();
    133   webrtc::VP8Decoder* decoder = webrtc::VP8Decoder::Create();
    134   inst.codecType = webrtc::kVideoCodecVP8;
    135   inst.codecSpecific.VP8.feedbackModeOn = false;
    136   inst.codecSpecific.VP8.denoisingOn = true;
    137   inst.maxFramerate = framerate;
    138   inst.startBitrate = target_bitrate;
    139   inst.maxBitrate = 8000;
    140   inst.width = width;
    141   inst.height = height;
    142 
    143   if (encoder->InitEncode(&inst, 1, 1440) < 0) {
    144     fprintf(stderr, "Error: Cannot initialize vp8 encoder\n");
    145     return -1;
    146   }
    147   EXPECT_EQ(0, decoder->InitDecode(&inst, 1));
    148   webrtc::VideoFrame input_frame;
    149   size_t length = webrtc::CalcBufferSize(webrtc::kI420, width, height);
    150   rtc::scoped_ptr<uint8_t[]> frame_buffer(new uint8_t[length]);
    151 
    152   int half_width = (width + 1) / 2;
    153   // Set and register callbacks.
    154   Vp8SequenceCoderEncodeCallback encoder_callback(encoded_file);
    155   encoder->RegisterEncodeCompleteCallback(&encoder_callback);
    156   Vp8SequenceCoderDecodeCallback decoder_callback(output_file);
    157   decoder->RegisterDecodeCompleteCallback(&decoder_callback);
    158   // Read->Encode->Decode sequence.
    159   // num_frames = -1 implies unlimited encoding (entire sequence).
    160   int64_t starttime = webrtc::TickTime::MillisecondTimestamp();
    161   int frame_cnt = 1;
    162   int frames_processed = 0;
    163   input_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
    164   while (!feof(input_file) &&
    165          (num_frames == -1 || frames_processed < num_frames)) {
    166     if (fread(frame_buffer.get(), 1, length, input_file) != length)
    167       continue;
    168     if (frame_cnt >= start_frame) {
    169       webrtc::ConvertToI420(webrtc::kI420, frame_buffer.get(), 0, 0, width,
    170                             height, 0, webrtc::kVideoRotation_0, &input_frame);
    171       encoder->Encode(input_frame, NULL, NULL);
    172       decoder->Decode(encoder_callback.encoded_image(), false, NULL);
    173       ++frames_processed;
    174     }
    175     ++frame_cnt;
    176   }
    177   printf("\nProcessed %d frames\n", frames_processed);
    178   int64_t endtime = webrtc::TickTime::MillisecondTimestamp();
    179   int64_t totalExecutionTime = endtime - starttime;
    180   printf("Total execution time: %.2lf ms\n",
    181          static_cast<double>(totalExecutionTime));
    182   double actual_bit_rate =
    183       8.0 * encoder_callback.encoded_bytes() / (frame_cnt / inst.maxFramerate);
    184   printf("Actual bitrate: %f kbps\n", actual_bit_rate / 1000);
    185   webrtc::test::QualityMetricsResult psnr_result, ssim_result;
    186   EXPECT_EQ(0, webrtc::test::I420MetricsFromFiles(
    187                    input_file_name.c_str(), output_file_name.c_str(),
    188                    inst.width, inst.height, &psnr_result, &ssim_result));
    189   printf("PSNR avg: %f[dB], min: %f[dB]\nSSIM avg: %f, min: %f\n",
    190          psnr_result.average, psnr_result.min, ssim_result.average,
    191          ssim_result.min);
    192   return frame_cnt;
    193 }
    194 
    195 int main(int argc, char** argv) {
    196   std::string program_name = argv[0];
    197   std::string usage =
    198       "Encode and decodes a video sequence, and writes"
    199       "results to a file.\n"
    200       "Example usage:\n" +
    201       program_name +
    202       " functionality"
    203       " --w=352 --h=288 --input_file=input.yuv --output_file=output.yuv "
    204       " Command line flags:\n"
    205       "  - width(int): The width of the input file. Default: 352\n"
    206       "  - height(int): The height of the input file. Default: 288\n"
    207       "  - input_file(string): The YUV file to encode."
    208       "      Default: foreman.yuv\n"
    209       "  - encoded_file(string): The vp8 encoded file (encoder output)."
    210       "      Default: vp8_encoded.vp8\n"
    211       "  - output_file(string): The yuv decoded file (decoder output)."
    212       "      Default: vp8_decoded.yuv\n."
    213       "  - start_frame - frame number in which encoding will begin. Default: 0"
    214       "  - num_frames - Number of frames to be processed. "
    215       "      Default: -1 (entire sequence).";
    216 
    217   webrtc::test::CommandLineParser parser;
    218 
    219   // Init the parser and set the usage message.
    220   parser.Init(argc, argv);
    221   parser.SetUsageMessage(usage);
    222 
    223   // Reset flags.
    224   parser.SetFlag("w", "352");
    225   parser.SetFlag("h", "288");
    226   parser.SetFlag("f", "30");
    227   parser.SetFlag("b", "500");
    228   parser.SetFlag("start_frame", "0");
    229   parser.SetFlag("num_frames", "-1");
    230   parser.SetFlag("output_file", webrtc::test::OutputPath() + "vp8_decoded.yuv");
    231   parser.SetFlag("encoded_file",
    232                  webrtc::test::OutputPath() + "vp8_encoded.vp8");
    233   parser.SetFlag("input_file",
    234                  webrtc::test::ResourcePath("foreman_cif", "yuv"));
    235   parser.SetFlag("help", "false");
    236 
    237   parser.ProcessFlags();
    238   if (parser.GetFlag("help") == "true") {
    239     parser.PrintUsageMessage();
    240     exit(EXIT_SUCCESS);
    241   }
    242   parser.PrintEnteredFlags();
    243 
    244   return SequenceCoder(&parser);
    245 }
    246