Don't force key frame when decoding with errors

BUG=2241
R=stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/2036004

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@4597 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/modules/video_coding/codecs/test_framework/test.cc b/modules/video_coding/codecs/test_framework/test.cc
index 2d55f02..a66b539 100644
--- a/modules/video_coding/codecs/test_framework/test.cc
+++ b/modules/video_coding/codecs/test_framework/test.cc
@@ -150,8 +150,8 @@
     image._buffer = videoBuffer.Buffer();
     image._length = videoBuffer.Length();
     image._size = videoBuffer.Size();
-    //image._frameType = static_cast<VideoFrameType>
-    //  (videoBuffer.GetFrameType());
+    // image._frameType = static_cast<VideoFrameType>
+    //     (videoBuffer.GetFrameType());
     image._timeStamp = videoBuffer.TimeStamp();
     image._encodedWidth = videoBuffer.Width();
     image._encodedHeight = videoBuffer.Height();
diff --git a/modules/video_coding/codecs/test_framework/unit_test.cc b/modules/video_coding/codecs/test_framework/unit_test.cc
index 0fdd447..3b034e0 100644
--- a/modules/video_coding/codecs/test_framework/unit_test.cc
+++ b/modules/video_coding/codecs/test_framework/unit_test.cc
@@ -32,6 +32,7 @@
 _refDecFrame(NULL),
 _refEncFrameLength(0),
 _sourceFile(NULL),
+is_key_frame_(false),
 _encodeCompleteCallback(NULL),
 _decodeCompleteCallback(NULL)
 {
@@ -48,6 +49,7 @@
 _refDecFrame(NULL),
 _refEncFrameLength(0),
 _sourceFile(NULL),
+is_key_frame_(false),
 _encodeCompleteCallback(NULL),
 _decodeCompleteCallback(NULL)
 {
@@ -254,23 +256,27 @@
     ASSERT_FALSE(SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK);
 
     unsigned int frameLength = 0;
-    int i=0;
+    int i = 0;
     _inputVideoBuffer.CreateEmptyFrame(_inst.width, _inst.height, _inst.width,
                                        (_inst.width + 1) / 2,
                                        (_inst.width + 1) / 2);
     while (frameLength == 0)
     {
+         EncodedImage encodedImage;
         if (i > 0)
         {
-            // Insert yet another frame
+            // Insert yet another frame.
             ASSERT_TRUE(fread(_refFrame, 1, _lengthSourceFrame,
                 _sourceFile) == _lengthSourceFrame);
             EXPECT_EQ(0, ConvertToI420(kI420, _refFrame, 0, 0, _width, _height,
                           0, kRotateNone, &_inputVideoBuffer));
             _encoder->Encode(_inputVideoBuffer, NULL, NULL);
             ASSERT_TRUE(WaitForEncodedFrame() > 0);
+        } else {
+            // The first frame is always a key frame.
+            encodedImage._frameType = kKeyFrame;
         }
-        EncodedImage encodedImage;
+
         VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
         ASSERT_TRUE(_decoder->Decode(encodedImage, 0, NULL)
                                == WEBRTC_VIDEO_CODEC_OK);
@@ -332,6 +338,10 @@
     {
         return WEBRTC_VIDEO_CODEC_OK;
     }
+    if (is_key_frame_) {
+        encodedImage._frameType = kKeyFrame;
+    }
+
     int ret = _decoder->Decode(encodedImage, 0, NULL);
     unsigned int frameLength = WaitForDecodedFrame();
     assert(ret == WEBRTC_VIDEO_CODEC_OK && (frameLength == 0 || frameLength
@@ -526,6 +536,10 @@
         memset(tmpBuf, 0, _refEncFrameLength);
         _encodedVideoBuffer.CopyFrame(_refEncFrameLength, tmpBuf);
         VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+        if (i == 0) {
+            // First frame is a key frame.
+            is_key_frame_ = true;
+        }
         ret = _decoder->Decode(encodedImage, false, NULL);
         EXPECT_TRUE(ret <= 0);
         if (ret == 0)
@@ -543,6 +557,8 @@
     ASSERT_FALSE(SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK);
     frameLength = 0;
     VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage);
+    // first frame is a key frame.
+    encodedImage._frameType = kKeyFrame;
     while (frameLength == 0)
     {
         _decoder->Decode(encodedImage, false, NULL);
@@ -686,6 +702,10 @@
         encTimeStamp = _encodedVideoBuffer.TimeStamp();
         EXPECT_TRUE(_inputVideoBuffer.timestamp() ==
                 static_cast<unsigned>(encTimeStamp));
+        if (frames == 0) {
+            // First frame is always a key frame.
+            is_key_frame_ = true;
+        }
 
         frameLength = Decode();
         if (frameLength == 0)
diff --git a/modules/video_coding/codecs/test_framework/unit_test.h b/modules/video_coding/codecs/test_framework/unit_test.h
index 34d4878..4e2fea0 100644
--- a/modules/video_coding/codecs/test_framework/unit_test.h
+++ b/modules/video_coding/codecs/test_framework/unit_test.h
@@ -63,6 +63,7 @@
     unsigned char* _refDecFrame;
     unsigned int _refEncFrameLength;
     FILE* _sourceFile;
+    bool is_key_frame_;
 
     UnitTestEncodeCompleteCallback* _encodeCompleteCallback;
     UnitTestDecodeCompleteCallback* _decodeCompleteCallback;
diff --git a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
index 4a1a2ca..4f7b276 100644
--- a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
+++ b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
@@ -106,6 +106,43 @@
         Vp8UnitTestDecodeCompleteCallback(&decoded_video_frame_));
     encoder_->RegisterEncodeCompleteCallback(encode_complete_callback_.get());
     decoder_->RegisterDecodeCompleteCallback(decode_complete_callback_.get());
+    // Using a QCIF image (aligned stride (u,v planes) > width).
+    // Processing only one frame.
+    const VideoSource source(test::ResourcePath("paris_qcif", "yuv"), kQCIF);
+    length_source_frame_ = source.GetFrameLength();
+    source_buffer_.reset(new uint8_t[length_source_frame_]);
+    source_file_ = fopen(source.GetFileName().c_str(), "rb");
+    ASSERT_TRUE(source_file_ != NULL);
+    // Set input frame.
+    ASSERT_EQ(fread(source_buffer_.get(), 1, length_source_frame_,
+        source_file_), length_source_frame_);
+    codec_inst_.width = source.GetWidth();
+    codec_inst_.height = source.GetHeight();
+    codec_inst_.maxFramerate = source.GetFrameRate();
+    // Setting aligned stride values.
+    int stride_uv = 0;
+    int stride_y = 0;
+    Calc16ByteAlignedStride(codec_inst_.width, &stride_y, &stride_uv);
+    EXPECT_EQ(stride_y, 176);
+    EXPECT_EQ(stride_uv, 96);
+
+    input_frame_.CreateEmptyFrame(codec_inst_.width, codec_inst_.height,
+                                  stride_y, stride_uv, stride_uv);
+    // Using ConvertToI420 to add stride to the image.
+    EXPECT_EQ(0, ConvertToI420(kI420, source_buffer_.get(), 0, 0,
+                               codec_inst_.width, codec_inst_.height,
+                               0, kRotateNone, &input_frame_));
+  }
+
+  void SetUpEncodeDecode() {
+    codec_inst_.startBitrate = 300;
+    codec_inst_.maxBitrate = 4000;
+    codec_inst_.qpMax = 56;
+    codec_inst_.codecSpecific.VP8.denoisingOn = true;
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+        encoder_->InitEncode(&codec_inst_, 1, 1440));
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->InitDecode(&codec_inst_, 1));
   }
 
   int WaitForEncodedFrame() const {
@@ -143,6 +180,7 @@
   scoped_ptr<Vp8UnitTestDecodeCompleteCallback> decode_complete_callback_;
   scoped_array<uint8_t> source_buffer_;
   FILE* source_file_;
+  I420VideoFrame input_frame_;
   scoped_ptr<VideoEncoder> encoder_;
   scoped_ptr<VideoDecoder> decoder_;
   VideoFrame encoded_video_frame_;
@@ -190,49 +228,38 @@
 }
 
 TEST_F(TestVp8Impl, DISABLED_ON_ANDROID(AlignedStrideEncodeDecode)) {
-  // Using a QCIF image (aligned stride (u,v planse) > width).
-  // Processing only one frame.
-  const VideoSource source(test::ResourcePath("paris_qcif", "yuv"), kQCIF);
-  length_source_frame_ = source.GetFrameLength();
-  source_buffer_.reset(new uint8_t[length_source_frame_]);
-  source_file_ = fopen(source.GetFileName().c_str(), "rb");
-  ASSERT_TRUE(source_file_ != NULL);
-  codec_inst_.maxFramerate = source.GetFrameRate();
-  codec_inst_.startBitrate = 300;
-  codec_inst_.maxBitrate = 4000;
-  codec_inst_.qpMax = 56;
-  codec_inst_.width = source.GetWidth();
-  codec_inst_.height = source.GetHeight();
-  codec_inst_.codecSpecific.VP8.denoisingOn = true;
-
-  // Get input frame.
-  ASSERT_EQ(fread(source_buffer_.get(), 1, length_source_frame_, source_file_),
-            length_source_frame_);
-  // Setting aligned stride values.
-  int stride_uv = 0;
-  int stride_y = 0;
-  Calc16ByteAlignedStride(codec_inst_.width, &stride_y, &stride_uv);
-  EXPECT_EQ(stride_y, 176);
-  EXPECT_EQ(stride_uv, 96);
-
-  I420VideoFrame input_frame;
-  input_frame.CreateEmptyFrame(codec_inst_.width, codec_inst_.height,
-                               stride_y, stride_uv, stride_uv);
-  // Using ConvertToI420 to add stride to the image.
-  EXPECT_EQ(0, ConvertToI420(kI420, source_buffer_.get(), 0, 0,
-                             codec_inst_.width, codec_inst_.height,
-                             0, kRotateNone, &input_frame));
-
-  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codec_inst_, 1, 1440));
-  encoder_->Encode(input_frame, NULL, NULL);
+  SetUpEncodeDecode();
+  encoder_->Encode(input_frame_, NULL, NULL);
   EXPECT_GT(WaitForEncodedFrame(), 0);
-  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->InitDecode(&codec_inst_, 1));
   EncodedImage encodedImage;
   VideoFrameToEncodedImage(encoded_video_frame_, encodedImage);
+  // First frame should be a key frame.
+  encodedImage._frameType = kKeyFrame;
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encodedImage, false, NULL));
   EXPECT_GT(WaitForDecodedFrame(), 0);
   // Compute PSNR on all planes (faster than SSIM).
-  EXPECT_GT(I420PSNR(&input_frame, &decoded_video_frame_), 36);
+  EXPECT_GT(I420PSNR(&input_frame_, &decoded_video_frame_), 36);
+}
+
+TEST_F(TestVp8Impl, DecodeWithACompleteKeyFrame) {
+  SetUpEncodeDecode();
+  encoder_->Encode(input_frame_, NULL, NULL);
+  EXPECT_GT(WaitForEncodedFrame(), 0);
+  EncodedImage encodedImage;
+  VideoFrameToEncodedImage(encoded_video_frame_, encodedImage);
+  // Setting complete to false -> should return an error.
+  encodedImage._completeFrame = false;
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
+      decoder_->Decode(encodedImage, false, NULL));
+  // Setting complete back to true.
+  encodedImage._completeFrame = true;
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
+      decoder_->Decode(encodedImage, false, NULL));
+  // Now setting a key frame.
+  encodedImage._frameType = kKeyFrame;
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+      decoder_->Decode(encodedImage, false, NULL));
+  EXPECT_GT(I420PSNR(&input_frame_, &decoded_video_frame_), 36);
 }
 
 }  // namespace webrtc
diff --git a/modules/video_coding/codecs/vp8/vp8_impl.cc b/modules/video_coding/codecs/vp8/vp8_impl.cc
index 488d951..c988498 100644
--- a/modules/video_coding/codecs/vp8/vp8_impl.cc
+++ b/modules/video_coding/codecs/vp8/vp8_impl.cc
@@ -502,8 +502,8 @@
       image_format_(VPX_IMG_FMT_NONE),
       ref_frame_(NULL),
       propagation_cnt_(-1),
-      latest_keyframe_complete_(false),
-      mfqe_enabled_(false) {
+      mfqe_enabled_(false),
+      key_frame_required_(true) {
   memset(&codec_, 0, sizeof(codec_));
 }
 
@@ -518,7 +518,6 @@
   }
   InitDecode(&codec_, 1);
   propagation_cnt_ = -1;
-  latest_keyframe_complete_ = false;
   mfqe_enabled_ = false;
   return WEBRTC_VIDEO_CODEC_OK;
 }
@@ -571,9 +570,12 @@
   }
 
   propagation_cnt_ = -1;
-  latest_keyframe_complete_ = false;
 
   inited_ = true;
+
+  // Always start with a complete key frame.
+  key_frame_required_ = true;
+
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
@@ -615,6 +617,18 @@
   }
 #endif
 
+
+  // Always start with a complete key frame.
+  if (key_frame_required_) {
+    if (input_image._frameType != kKeyFrame)
+      return WEBRTC_VIDEO_CODEC_ERROR;
+    // We have a key frame - is it complete?
+    if (input_image._completeFrame) {
+      key_frame_required_ = false;
+    } else {
+      return WEBRTC_VIDEO_CODEC_ERROR;
+    }
+  }
   // Restrict error propagation using key frame requests. Disabled when
   // the feedback mode is enabled (RPS).
   // Reset on a key frame refresh.
@@ -708,9 +722,7 @@
     // Whenever we receive an incomplete key frame all reference buffers will
     // be corrupt. If that happens we must request new key frames until we
     // decode a complete.
-    if (input_image._frameType == kKeyFrame)
-      latest_keyframe_complete_ = input_image._completeFrame;
-    if (!latest_keyframe_complete_)
+    if (input_image._frameType == kKeyFrame && !input_image._completeFrame)
       return WEBRTC_VIDEO_CODEC_ERROR;
 
     // Check for reference updates and last reference buffer corruption and
diff --git a/modules/video_coding/codecs/vp8/vp8_impl.h b/modules/video_coding/codecs/vp8/vp8_impl.h
index d420ba0..26dd52e 100644
--- a/modules/video_coding/codecs/vp8/vp8_impl.h
+++ b/modules/video_coding/codecs/vp8/vp8_impl.h
@@ -226,8 +226,8 @@
   int image_format_;
   vpx_ref_frame_t* ref_frame_;
   int propagation_cnt_;
-  bool latest_keyframe_complete_;
   bool mfqe_enabled_;
+  bool key_frame_required_;
 };  // end of VP8Decoder class
 }  // namespace webrtc
 
diff --git a/modules/video_coding/main/source/codec_database.cc b/modules/video_coding/main/source/codec_database.cc
index 6f50ddd..e41c6b4 100644
--- a/modules/video_coding/main/source/codec_database.cc
+++ b/modules/video_coding/main/source/codec_database.cc
@@ -597,8 +597,7 @@
   }
 
   if (ptr_decoder->InitDecode(decoder_item->settings.get(),
-                              decoder_item->number_of_cores,
-                              decoder_item->require_key_frame) < 0) {
+                              decoder_item->number_of_cores) < 0) {
     ReleaseDecoder(ptr_decoder);
     return NULL;
   }
diff --git a/modules/video_coding/main/source/generic_decoder.cc b/modules/video_coding/main/source/generic_decoder.cc
index 12464d1..50b1eda 100644
--- a/modules/video_coding/main/source/generic_decoder.cc
+++ b/modules/video_coding/main/source/generic_decoder.cc
@@ -133,9 +133,7 @@
 _nextFrameInfoIdx(0),
 _decoder(decoder),
 _codecType(kVideoCodecUnknown),
-_isExternal(isExternal),
-_requireKeyFrame(false),
-_keyFrameDecoded(false)
+_isExternal(isExternal)
 {
 }
 
@@ -144,11 +142,8 @@
 }
 
 int32_t VCMGenericDecoder::InitDecode(const VideoCodec* settings,
-                                            int32_t numberOfCores,
-                                            bool requireKeyFrame)
+                                      int32_t numberOfCores)
 {
-    _requireKeyFrame = requireKeyFrame;
-    _keyFrameDecoded = false;
     _codecType = settings->codecType;
 
     return _decoder.InitDecode(settings, numberOfCores);
@@ -157,15 +152,6 @@
 int32_t VCMGenericDecoder::Decode(const VCMEncodedFrame& frame,
                                         int64_t nowMs)
 {
-    if (_requireKeyFrame &&
-        !_keyFrameDecoded &&
-        frame.FrameType() != kVideoFrameKey &&
-        frame.FrameType() != kVideoFrameGolden)
-    {
-        // Require key frame is enabled, meaning that one key frame must be decoded
-        // before we can decode delta frames.
-        return VCM_CODEC_ERROR;
-    }
     _frameInfos[_nextFrameInfoIdx].decodeStartTimeMs = nowMs;
     _frameInfos[_nextFrameInfoIdx].renderTimeMs = frame.RenderTimeMs();
     _callback->Map(frame.TimeStamp(), &_frameInfos[_nextFrameInfoIdx]);
@@ -194,22 +180,17 @@
         // No output
         _callback->Pop(frame.TimeStamp());
     }
-    // Update the key frame decoded variable so that we know whether or not we've decoded a key frame since reset.
-    _keyFrameDecoded = (_keyFrameDecoded ||
-        frame.FrameType() == kVideoFrameKey);
     return ret;
 }
 
 int32_t
 VCMGenericDecoder::Release()
 {
-    _keyFrameDecoded = false;
     return _decoder.Release();
 }
 
 int32_t VCMGenericDecoder::Reset()
 {
-    _keyFrameDecoded = false;
     return _decoder.Reset();
 }
 
diff --git a/modules/video_coding/main/source/generic_decoder.h b/modules/video_coding/main/source/generic_decoder.h
index 7144a09..e1993fb 100644
--- a/modules/video_coding/main/source/generic_decoder.h
+++ b/modules/video_coding/main/source/generic_decoder.h
@@ -70,8 +70,7 @@
     *	Initialize the decoder with the information from the VideoCodec
     */
     int32_t InitDecode(const VideoCodec* settings,
-                             int32_t numberOfCores,
-                             bool requireKeyFrame);
+                             int32_t numberOfCores);
 
     /**
     *	Decode to a raw I420 frame,
@@ -115,7 +114,6 @@
     VideoDecoder&               _decoder;
     VideoCodecType              _codecType;
     bool                        _isExternal;
-    bool                        _requireKeyFrame;
     bool                        _keyFrameDecoded;
 
 };
diff --git a/modules/video_coding/main/source/jitter_buffer.cc b/modules/video_coding/main/source/jitter_buffer.cc
index 4953b28..fbfe852 100644
--- a/modules/video_coding/main/source/jitter_buffer.cc
+++ b/modules/video_coding/main/source/jitter_buffer.cc
@@ -508,19 +508,13 @@
     return false;
   }
   VCMFrameBuffer* oldest_frame = decodable_frames_.Front();
-
   // If we have exactly one frame in the buffer, release it only if it is
-  // complete. We know decodable_frames_ is  not empty due to the prevoius
+  // complete. We know decodable_frames_ is  not empty due to the previous
   // check.
   if (decodable_frames_.size() == 1 && incomplete_frames_.empty()
       && oldest_frame->GetState() != kStateComplete) {
     return false;
   }
-  // Always start with a complete key frame.
-  if (last_decoded_state_.in_initial_state() &&
-      oldest_frame->FrameType() != kVideoFrameKey) {
-    return false;
-  }
 
   *timestamp = oldest_frame->TimeStamp();
   return true;
diff --git a/video_engine/test/libvietest/testbed/tb_I420_codec.cc b/video_engine/test/libvietest/testbed/tb_I420_codec.cc
index bf81ebb..5ed5e46 100644
--- a/video_engine/test/libvietest/testbed/tb_I420_codec.cc
+++ b/video_engine/test/libvietest/testbed/tb_I420_codec.cc
@@ -260,12 +260,19 @@
         return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
     }
 
+    // Only send complete frames.
+    if (static_cast<int>(inputImage._length) !=
+        webrtc::CalcBufferSize(webrtc::kI420,_width,_height)) {
+      return WEBRTC_VIDEO_CODEC_ERROR;
+    }
+
     int ret = ConvertToI420(webrtc::kI420, inputImage._buffer, 0, 0,
                            _width, _height,
                            0, webrtc::kRotateNone, &_decodedImage);
 
     if (ret < 0)
       return WEBRTC_VIDEO_CODEC_ERROR;
+
     _decodedImage.set_timestamp(inputImage._timeStamp);
 
     _decodeCompleteCallback->Decoded(_decodedImage);