Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/memory/scoped_ptr.h" | 5 #include "base/memory/scoped_ptr.h" |
| 6 #include "base/message_loop.h" | 6 #include "base/message_loop.h" |
| 7 #include "media/base/data_buffer.h" | 7 #include "media/base/data_buffer.h" |
| 8 #include "media/base/mock_ffmpeg.h" | |
| 9 #include "media/base/mock_task.h" | 8 #include "media/base/mock_task.h" |
| 10 #include "media/base/pipeline.h" | 9 #include "media/base/pipeline.h" |
| 10 #include "media/base/test_data_util.h" | |
| 11 #include "media/filters/ffmpeg_glue.h" | |
| 11 #include "media/video/ffmpeg_video_decode_engine.h" | 12 #include "media/video/ffmpeg_video_decode_engine.h" |
| 12 #include "testing/gmock/include/gmock/gmock.h" | 13 #include "testing/gmock/include/gmock/gmock.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
| 14 | 15 |
| 15 using ::testing::_; | 16 using ::testing::_; |
| 16 using ::testing::DoAll; | 17 using ::testing::DoAll; |
| 17 using ::testing::Return; | 18 using ::testing::Return; |
| 18 using ::testing::ReturnNull; | 19 using ::testing::ReturnNull; |
| 20 using ::testing::SaveArg; | |
| 19 using ::testing::SetArgumentPointee; | 21 using ::testing::SetArgumentPointee; |
| 20 using ::testing::StrictMock; | 22 using ::testing::StrictMock; |
| 21 | 23 |
| 22 namespace media { | 24 namespace media { |
| 23 | 25 |
| 24 static const int kWidth = 320; | 26 static const size_t kWidth = 320; |
| 25 static const int kHeight = 240; | 27 static const size_t kHeight = 240; |
| 26 static const int kSurfaceWidth = 522; | 28 static const int kSurfaceWidth = 522; |
| 27 static const int kSurfaceHeight = 288; | 29 static const int kSurfaceHeight = 288; |
| 28 static const AVRational kFrameRate = { 100, 1 }; | 30 static const AVRational kFrameRate = { 100, 1 }; |
| 29 | 31 |
| 30 static void InitializeFrame(uint8_t* data, int width, AVFrame* frame) { | |
| 31 frame->data[0] = data; | |
| 32 frame->data[1] = data; | |
| 33 frame->data[2] = data; | |
| 34 frame->linesize[0] = width; | |
| 35 frame->linesize[1] = width / 2; | |
| 36 frame->linesize[2] = width / 2; | |
| 37 } | |
| 38 | |
| 39 ACTION_P(DecodeComplete, decoder) { | |
| 40 decoder->set_video_frame(arg0); | |
| 41 } | |
| 42 | |
| 43 ACTION_P2(DemuxComplete, engine, buffer) { | 32 ACTION_P2(DemuxComplete, engine, buffer) { |
| 44 engine->ConsumeVideoSample(buffer); | 33 engine->ConsumeVideoSample(buffer); |
| 45 } | 34 } |
| 46 | 35 |
| 47 ACTION_P(SaveInitializeResult, engine) { | |
| 48 engine->set_video_codec_info(arg0); | |
| 49 } | |
| 50 | |
| 51 class FFmpegVideoDecodeEngineTest | 36 class FFmpegVideoDecodeEngineTest |
| 52 : public testing::Test, | 37 : public testing::Test, |
| 53 public VideoDecodeEngine::EventHandler { | 38 public VideoDecodeEngine::EventHandler { |
| 54 public: | 39 public: |
| 55 FFmpegVideoDecodeEngineTest() | 40 FFmpegVideoDecodeEngineTest() |
| 56 : config_(kCodecH264, kWidth, kHeight, kSurfaceWidth, kSurfaceHeight, | 41 : config_(kCodecVP8, kWidth, kHeight, kSurfaceWidth, kSurfaceHeight, |
| 57 kFrameRate.num, kFrameRate.den, NULL, 0) { | 42 kFrameRate.num, kFrameRate.den, NULL, 0) { |
| 43 CHECK(FFmpegGlue::GetInstance()); | |
| 58 | 44 |
| 59 // Setup FFmpeg structures. | 45 // Setup FFmpeg structures. |
| 60 frame_buffer_.reset(new uint8[kWidth * kHeight]); | 46 frame_buffer_.reset(new uint8[kWidth * kHeight]); |
| 61 memset(&yuv_frame_, 0, sizeof(yuv_frame_)); | |
| 62 InitializeFrame(frame_buffer_.get(), kWidth, &yuv_frame_); | |
| 63 | |
| 64 memset(&codec_context_, 0, sizeof(codec_context_)); | |
| 65 memset(&codec_, 0, sizeof(codec_)); | |
| 66 | |
| 67 buffer_ = new DataBuffer(1); | |
| 68 | 47 |
| 69 test_engine_.reset(new FFmpegVideoDecodeEngine()); | 48 test_engine_.reset(new FFmpegVideoDecodeEngine()); |
| 70 | 49 |
| 71 video_frame_ = VideoFrame::CreateFrame(VideoFrame::YV12, | 50 ReadTestDataFile("vp8-I-frame-320x240", &i_frame_buffer_); |
| 72 kWidth, | 51 |
| 73 kHeight, | 52 // Create a corrupt version of |i_frame_buffer_|. |
|
scherkus (not reviewing)
2011/08/23 14:42:08
couldn't this be a file?
| |
| 74 kNoTimestamp, | 53 corrupt_i_frame_buffer_ = new DataBuffer(i_frame_buffer_->GetDataSize()); |
| 75 kNoTimestamp); | 54 corrupt_i_frame_buffer_->SetDataSize(i_frame_buffer_->GetDataSize()); |
| 55 | |
| 56 uint8* buf = corrupt_i_frame_buffer_->GetWritableData(); | |
| 57 memcpy(buf, i_frame_buffer_->GetData(), | |
| 58 corrupt_i_frame_buffer_->GetDataSize()); | |
| 59 | |
| 60 // Corrupt bytes by flipping bits w/ xor. | |
| 61 for (size_t i = 0; i < corrupt_i_frame_buffer_->GetDataSize(); i++) | |
| 62 buf[i] ^= 0xA5; | |
| 63 | |
| 64 end_of_stream_buffer_ = new DataBuffer(0); | |
| 76 } | 65 } |
| 77 | 66 |
| 78 ~FFmpegVideoDecodeEngineTest() { | 67 ~FFmpegVideoDecodeEngineTest() { |
| 79 test_engine_.reset(); | 68 test_engine_.reset(); |
| 80 } | 69 } |
| 81 | 70 |
| 82 void Initialize() { | 71 void Initialize() { |
| 83 EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) | 72 VideoCodecInfo info; |
| 84 .WillOnce(Return(&codec_context_)); | |
| 85 EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) | |
| 86 .WillOnce(Return(&codec_)); | |
| 87 EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) | |
| 88 .WillOnce(Return(&yuv_frame_)); | |
| 89 EXPECT_CALL(mock_ffmpeg_, AVCodecOpen(&codec_context_, &codec_)) | |
| 90 .WillOnce(Return(0)); | |
| 91 EXPECT_CALL(mock_ffmpeg_, AVCodecClose(&codec_context_)) | |
| 92 .WillOnce(Return(0)); | |
| 93 EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) | |
| 94 .Times(1); | |
| 95 EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) | |
| 96 .Times(1); | |
| 97 | |
| 98 EXPECT_CALL(*this, OnInitializeComplete(_)) | 73 EXPECT_CALL(*this, OnInitializeComplete(_)) |
| 99 .WillOnce(SaveInitializeResult(this)); | 74 .WillOnce(SaveArg<0>(&info)); |
| 100 test_engine_->Initialize(MessageLoop::current(), this, NULL, config_); | 75 test_engine_->Initialize(MessageLoop::current(), this, NULL, config_); |
| 101 EXPECT_TRUE(info_.success); | 76 EXPECT_TRUE(info.success); |
| 102 } | 77 } |
| 103 | 78 |
| 104 void Decode() { | 79 // Decodes the single compressed frame in |buffer| and writes the |
| 105 EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_)); | 80 // uncompressed output to |video_frame|. This method works with single |
| 106 EXPECT_CALL(mock_ffmpeg_, | 81 // and multithreaded decoders. End of stream buffers are used to trigger |
| 107 AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _)) | 82 // the frame to be returned in the multithreaded decoder case. |
| 108 .WillOnce(DoAll(SetArgumentPointee<2>(1), // Simulate 1 byte frame. | 83 void DecodeASingleFrame(const scoped_refptr<Buffer>& buffer, |
| 109 Return(0))); | 84 scoped_refptr<VideoFrame>* video_frame) { |
| 85 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
| 86 .WillOnce(DemuxComplete(test_engine_.get(), buffer)) | |
| 87 .WillRepeatedly(DemuxComplete(test_engine_.get(), | |
| 88 end_of_stream_buffer_)); | |
| 89 | |
| 90 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | |
| 91 .WillOnce(SaveArg<0>(video_frame)); | |
| 92 CallProduceVideoFrame(); | |
| 93 } | |
| 94 | |
| 95 // Decodes |i_frame_buffer_| and then decodes the data contained in | |
| 96 // the file named |test_file_name|. This function expects both buffers | |
| 97 // to decode to frames that are the same size. | |
| 98 void DecodeIFrameThenTestFile(const std::string& test_file_name) { | |
| 99 Initialize(); | |
| 100 | |
| 101 scoped_refptr<VideoFrame> video_frame_a; | |
| 102 scoped_refptr<VideoFrame> video_frame_b; | |
| 103 | |
| 104 scoped_refptr<Buffer> buffer; | |
| 105 ReadTestDataFile(test_file_name, &buffer); | |
| 110 | 106 |
| 111 EXPECT_CALL(*this, ProduceVideoSample(_)) | 107 EXPECT_CALL(*this, ProduceVideoSample(_)) |
| 112 .WillOnce(DemuxComplete(test_engine_.get(), buffer_)); | 108 .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)) |
| 109 .WillOnce(DemuxComplete(test_engine_.get(), buffer)) | |
| 110 .WillRepeatedly(DemuxComplete(test_engine_.get(), | |
| 111 end_of_stream_buffer_)); | |
| 112 | |
| 113 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | 113 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) |
| 114 .WillOnce(DecodeComplete(this)); | 114 .WillOnce(SaveArg<0>(&video_frame_a)) |
| 115 test_engine_->ProduceVideoFrame(video_frame_); | 115 .WillOnce(SaveArg<0>(&video_frame_b)); |
| 116 } | 116 CallProduceVideoFrame(); |
| 117 CallProduceVideoFrame(); | |
| 117 | 118 |
| 118 void ChangeDimensions(int width, int height) { | 119 EXPECT_EQ(kWidth, video_frame_a->width()); |
| 119 frame_buffer_.reset(new uint8[width * height]); | 120 EXPECT_EQ(kHeight, video_frame_a->height()); |
| 120 InitializeFrame(frame_buffer_.get(), width, &yuv_frame_); | 121 EXPECT_EQ(kWidth, video_frame_b->width()); |
| 121 codec_context_.width = width; | 122 EXPECT_EQ(kHeight, video_frame_b->height()); |
| 122 codec_context_.height = height; | |
| 123 } | 123 } |
| 124 | 124 |
| 125 // VideoDecodeEngine::EventHandler implementation. | 125 // VideoDecodeEngine::EventHandler implementation. |
| 126 MOCK_METHOD2(ConsumeVideoFrame, | 126 MOCK_METHOD2(ConsumeVideoFrame, |
| 127 void(scoped_refptr<VideoFrame> video_frame, | 127 void(scoped_refptr<VideoFrame> video_frame, |
| 128 const PipelineStatistics& statistics)); | 128 const PipelineStatistics& statistics)); |
| 129 MOCK_METHOD1(ProduceVideoSample, | 129 MOCK_METHOD1(ProduceVideoSample, |
| 130 void(scoped_refptr<Buffer> buffer)); | 130 void(scoped_refptr<Buffer> buffer)); |
| 131 MOCK_METHOD1(OnInitializeComplete, | 131 MOCK_METHOD1(OnInitializeComplete, |
| 132 void(const VideoCodecInfo& info)); | 132 void(const VideoCodecInfo& info)); |
| 133 MOCK_METHOD0(OnUninitializeComplete, void()); | 133 MOCK_METHOD0(OnUninitializeComplete, void()); |
| 134 MOCK_METHOD0(OnFlushComplete, void()); | 134 MOCK_METHOD0(OnFlushComplete, void()); |
| 135 MOCK_METHOD0(OnSeekComplete, void()); | 135 MOCK_METHOD0(OnSeekComplete, void()); |
| 136 MOCK_METHOD0(OnError, void()); | 136 MOCK_METHOD0(OnError, void()); |
| 137 | 137 |
| 138 // Used by gmock actions. | 138 void CallProduceVideoFrame() { |
| 139 void set_video_frame(scoped_refptr<VideoFrame> video_frame) { | 139 test_engine_->ProduceVideoFrame(VideoFrame::CreateFrame(VideoFrame::YV12, |
| 140 video_frame_ = video_frame; | 140 kWidth, |
| 141 } | 141 kHeight, |
| 142 | 142 kNoTimestamp, |
| 143 void set_video_codec_info(const VideoCodecInfo& info) { | 143 kNoTimestamp)); |
| 144 info_ = info; | |
| 145 } | 144 } |
| 146 | 145 |
| 147 protected: | 146 protected: |
| 148 VideoDecoderConfig config_; | 147 VideoDecoderConfig config_; |
| 149 VideoCodecInfo info_; | |
| 150 scoped_refptr<VideoFrame> video_frame_; | |
| 151 scoped_ptr<FFmpegVideoDecodeEngine> test_engine_; | 148 scoped_ptr<FFmpegVideoDecodeEngine> test_engine_; |
| 152 scoped_array<uint8_t> frame_buffer_; | 149 scoped_array<uint8_t> frame_buffer_; |
| 153 StrictMock<MockFFmpeg> mock_ffmpeg_; | 150 scoped_refptr<Buffer> i_frame_buffer_; |
| 154 | 151 scoped_refptr<DataBuffer> corrupt_i_frame_buffer_; |
| 155 AVFrame yuv_frame_; | 152 scoped_refptr<Buffer> end_of_stream_buffer_; |
| 156 AVCodecContext codec_context_; | |
| 157 AVCodec codec_; | |
| 158 scoped_refptr<DataBuffer> buffer_; | |
| 159 | 153 |
| 160 private: | 154 private: |
| 161 DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecodeEngineTest); | 155 DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecodeEngineTest); |
| 162 }; | 156 }; |
| 163 | 157 |
| 164 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_Normal) { | 158 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_Normal) { |
| 165 Initialize(); | 159 Initialize(); |
| 166 } | 160 } |
| 167 | 161 |
| 168 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) { | 162 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) { |
| 163 VideoDecoderConfig config(kUnknown, kWidth, kHeight, kSurfaceWidth, | |
| 164 kSurfaceHeight, kFrameRate.num, kFrameRate.den, | |
| 165 NULL, 0); | |
| 169 // Test avcodec_find_decoder() returning NULL. | 166 // Test avcodec_find_decoder() returning NULL. |
| 170 EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) | 167 VideoCodecInfo info; |
| 171 .WillOnce(Return(&codec_context_)); | |
| 172 EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) | |
| 173 .WillOnce(ReturnNull()); | |
| 174 EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) | |
| 175 .WillOnce(Return(&yuv_frame_)); | |
| 176 EXPECT_CALL(mock_ffmpeg_, AVCodecClose(&codec_context_)) | |
| 177 .WillOnce(Return(0)); | |
| 178 EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) | |
| 179 .Times(1); | |
| 180 EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) | |
| 181 .Times(1); | |
| 182 | |
| 183 EXPECT_CALL(*this, OnInitializeComplete(_)) | 168 EXPECT_CALL(*this, OnInitializeComplete(_)) |
| 184 .WillOnce(SaveInitializeResult(this)); | 169 .WillOnce(SaveArg<0>(&info)); |
| 185 test_engine_->Initialize(MessageLoop::current(), this, NULL, config_); | 170 test_engine_->Initialize(MessageLoop::current(), this, NULL, config); |
| 186 EXPECT_FALSE(info_.success); | 171 EXPECT_FALSE(info.success); |
| 187 } | 172 } |
| 188 | 173 |
| 189 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) { | 174 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) { |
| 190 // Test avcodec_open() failing. | 175 // Specify Theora w/o extra data so that avcodec_open() fails. |
| 191 EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) | 176 VideoDecoderConfig config(kCodecTheora, kWidth, kHeight, kSurfaceWidth, |
| 192 .WillOnce(Return(&codec_context_)); | 177 kSurfaceHeight, kFrameRate.num, kFrameRate.den, |
| 193 EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) | 178 NULL, 0); |
| 194 .WillOnce(Return(&codec_)); | 179 VideoCodecInfo info; |
| 195 EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) | |
| 196 .WillOnce(Return(&yuv_frame_)); | |
| 197 EXPECT_CALL(mock_ffmpeg_, AVCodecOpen(&codec_context_, &codec_)) | |
| 198 .WillOnce(Return(-1)); | |
| 199 EXPECT_CALL(mock_ffmpeg_, AVCodecClose(&codec_context_)) | |
| 200 .WillOnce(Return(0)); | |
| 201 EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) | |
| 202 .Times(1); | |
| 203 EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) | |
| 204 .Times(1); | |
| 205 | |
| 206 EXPECT_CALL(*this, OnInitializeComplete(_)) | 180 EXPECT_CALL(*this, OnInitializeComplete(_)) |
| 207 .WillOnce(SaveInitializeResult(this)); | 181 .WillOnce(SaveArg<0>(&info)); |
| 208 test_engine_->Initialize(MessageLoop::current(), this, NULL, config_); | 182 test_engine_->Initialize(MessageLoop::current(), this, NULL, config); |
| 209 EXPECT_FALSE(info_.success); | 183 EXPECT_FALSE(info.success); |
| 210 } | 184 } |
| 211 | 185 |
| 212 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) { | 186 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) { |
| 213 Initialize(); | 187 Initialize(); |
| 214 | 188 |
| 215 // We rely on FFmpeg for timestamp and duration reporting. The one tricky | 189 // We rely on FFmpeg for timestamp and duration reporting. |
| 216 // bit is calculating the duration when |repeat_pict| > 0. | 190 const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(0); |
| 217 const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(123); | 191 const base::TimeDelta kDuration = base::TimeDelta::FromMicroseconds(10000); |
| 218 const base::TimeDelta kDuration = base::TimeDelta::FromMicroseconds(15000); | |
| 219 yuv_frame_.repeat_pict = 1; | |
| 220 yuv_frame_.reordered_opaque = kTimestamp.InMicroseconds(); | |
| 221 | 192 |
| 222 // Simulate decoding a single frame. | 193 // Simulate decoding a single frame. |
| 223 Decode(); | 194 scoped_refptr<VideoFrame> video_frame; |
| 195 DecodeASingleFrame(i_frame_buffer_, &video_frame); | |
| 224 | 196 |
| 225 // |video_frame_| timestamp is 0 because we set the timestamp based off | 197 // |video_frame| timestamp is 0 because we set the timestamp based off |
| 226 // the buffer timestamp. | 198 // the buffer timestamp. |
| 227 EXPECT_EQ(0, video_frame_->GetTimestamp().ToInternalValue()); | 199 ASSERT_TRUE(video_frame); |
| 200 EXPECT_EQ(0, video_frame->GetTimestamp().ToInternalValue()); | |
| 228 EXPECT_EQ(kDuration.ToInternalValue(), | 201 EXPECT_EQ(kDuration.ToInternalValue(), |
| 229 video_frame_->GetDuration().ToInternalValue()); | 202 video_frame->GetDuration().ToInternalValue()); |
| 230 } | 203 } |
| 231 | 204 |
| 205 | |
| 206 // Verify current behavior for 0 byte frames. FFmpeg simply ignores | |
| 207 // the 0 byte frames. | |
| 232 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_0ByteFrame) { | 208 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_0ByteFrame) { |
| 233 Initialize(); | 209 Initialize(); |
| 234 | 210 |
| 235 // Expect a bunch of avcodec calls. | 211 scoped_refptr<DataBuffer> zero_byte_buffer = new DataBuffer(1); |
| 236 EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_)) | 212 |
| 237 .Times(2); | 213 scoped_refptr<VideoFrame> video_frame_a; |
| 238 EXPECT_CALL(mock_ffmpeg_, | 214 scoped_refptr<VideoFrame> video_frame_b; |
| 239 AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _)) | 215 scoped_refptr<VideoFrame> video_frame_c; |
| 240 .WillOnce(DoAll(SetArgumentPointee<2>(0), // Simulate 0 byte frame. | |
| 241 Return(0))) | |
| 242 .WillOnce(DoAll(SetArgumentPointee<2>(1), // Simulate 1 byte frame. | |
| 243 Return(0))); | |
| 244 | 216 |
| 245 EXPECT_CALL(*this, ProduceVideoSample(_)) | 217 EXPECT_CALL(*this, ProduceVideoSample(_)) |
| 246 .WillOnce(DemuxComplete(test_engine_.get(), buffer_)) | 218 .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)) |
| 247 .WillOnce(DemuxComplete(test_engine_.get(), buffer_)); | 219 .WillOnce(DemuxComplete(test_engine_.get(), zero_byte_buffer)) |
| 220 .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)) | |
| 221 .WillRepeatedly(DemuxComplete(test_engine_.get(), | |
| 222 end_of_stream_buffer_)); | |
| 223 | |
| 248 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | 224 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) |
| 249 .WillOnce(DecodeComplete(this)); | 225 .WillOnce(SaveArg<0>(&video_frame_a)) |
| 250 test_engine_->ProduceVideoFrame(video_frame_); | 226 .WillOnce(SaveArg<0>(&video_frame_b)) |
| 227 .WillOnce(SaveArg<0>(&video_frame_c)); | |
| 228 CallProduceVideoFrame(); | |
| 229 CallProduceVideoFrame(); | |
| 230 CallProduceVideoFrame(); | |
| 251 | 231 |
| 252 EXPECT_TRUE(video_frame_.get()); | 232 EXPECT_TRUE(video_frame_a); |
| 233 EXPECT_TRUE(video_frame_b); | |
| 234 EXPECT_FALSE(video_frame_c); | |
| 253 } | 235 } |
| 254 | 236 |
| 237 | |
| 255 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) { | 238 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) { |
| 256 Initialize(); | 239 Initialize(); |
| 257 | 240 |
| 258 // Expect a bunch of avcodec calls. | 241 EXPECT_CALL(*this, ProduceVideoSample(_)) |
| 259 EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_)); | 242 .WillOnce(DemuxComplete(test_engine_.get(), corrupt_i_frame_buffer_)) |
| 260 EXPECT_CALL(mock_ffmpeg_, | 243 .WillRepeatedly(DemuxComplete(test_engine_.get(), i_frame_buffer_)); |
| 261 AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _)) | 244 EXPECT_CALL(*this, OnError()); |
| 262 .WillOnce(Return(-1)); | 245 |
| 246 CallProduceVideoFrame(); | |
| 247 } | |
| 248 | |
| 249 // Multi-threaded decoders have different behavior than single-threaded | |
| 250 // decoders at the end of the stream. Multithreaded decoders hide errors | |
| 251 // that happen on the last |codec_context_->thread_count| frames to avoid | |
| 252 // prematurely signalling EOS. This test just exposes that behavior so we can | |
| 253 // detect if it changes. | |
| 254 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeErrorAtEndOfStream) { | |
| 255 Initialize(); | |
| 263 | 256 |
| 264 EXPECT_CALL(*this, ProduceVideoSample(_)) | 257 EXPECT_CALL(*this, ProduceVideoSample(_)) |
| 265 .WillOnce(DemuxComplete(test_engine_.get(), buffer_)); | 258 .WillOnce(DemuxComplete(test_engine_.get(), corrupt_i_frame_buffer_)) |
| 266 EXPECT_CALL(*this, OnError()); | 259 .WillRepeatedly(DemuxComplete(test_engine_.get(), end_of_stream_buffer_)); |
| 267 | 260 |
| 268 test_engine_->ProduceVideoFrame(video_frame_); | 261 scoped_refptr<VideoFrame> video_frame; |
| 262 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | |
| 263 .WillOnce(SaveArg<0>(&video_frame)); | |
| 264 CallProduceVideoFrame(); | |
| 265 | |
| 266 EXPECT_FALSE(video_frame); | |
| 269 } | 267 } |
| 270 | 268 |
| 269 // Decode |i_frame_buffer_| and then a frame with a larger width and verify | |
| 270 // the output size didn't change. | |
| 271 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerWidth) { | 271 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerWidth) { |
| 272 Initialize(); | 272 DecodeIFrameThenTestFile("vp8-I-frame-640x240"); |
| 273 ChangeDimensions(kWidth * 2, kHeight); | |
| 274 Decode(); | |
| 275 } | 273 } |
| 276 | 274 |
| 275 // Decode |i_frame_buffer_| and then a frame with a smaller width and verify | |
| 276 // the output size didn't change. | |
| 277 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerWidth) { | 277 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerWidth) { |
| 278 Initialize(); | 278 DecodeIFrameThenTestFile("vp8-I-frame-160x240"); |
| 279 ChangeDimensions(kWidth / 2, kHeight); | |
| 280 Decode(); | |
| 281 } | 279 } |
| 282 | 280 |
| 281 // Decode |i_frame_buffer_| and then a frame with a larger height and verify | |
| 282 // the output size didn't change. | |
| 283 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerHeight) { | 283 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerHeight) { |
| 284 Initialize(); | 284 DecodeIFrameThenTestFile("vp8-I-frame-320x480"); |
| 285 ChangeDimensions(kWidth, kHeight * 2); | |
| 286 Decode(); | |
| 287 } | 285 } |
| 288 | 286 |
| 287 // Decode |i_frame_buffer_| and then a frame with a smaller height and verify | |
| 288 // the output size didn't change. | |
| 289 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerHeight) { | 289 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerHeight) { |
| 290 Initialize(); | 290 DecodeIFrameThenTestFile("vp8-I-frame-320x120"); |
| 291 ChangeDimensions(kWidth, kHeight / 2); | |
| 292 Decode(); | |
| 293 } | |
| 294 | |
| 295 TEST_F(FFmpegVideoDecodeEngineTest, GetSurfaceFormat) { | |
| 296 Initialize(); | |
| 297 | |
| 298 // YV12 formats. | |
| 299 codec_context_.pix_fmt = PIX_FMT_YUV420P; | |
| 300 EXPECT_EQ(VideoFrame::YV12, test_engine_->GetSurfaceFormat()); | |
| 301 codec_context_.pix_fmt = PIX_FMT_YUVJ420P; | |
| 302 EXPECT_EQ(VideoFrame::YV12, test_engine_->GetSurfaceFormat()); | |
| 303 | |
| 304 // YV16 formats. | |
| 305 codec_context_.pix_fmt = PIX_FMT_YUV422P; | |
| 306 EXPECT_EQ(VideoFrame::YV16, test_engine_->GetSurfaceFormat()); | |
| 307 codec_context_.pix_fmt = PIX_FMT_YUVJ422P; | |
| 308 EXPECT_EQ(VideoFrame::YV16, test_engine_->GetSurfaceFormat()); | |
| 309 | |
| 310 // Invalid value. | |
| 311 codec_context_.pix_fmt = PIX_FMT_NONE; | |
| 312 EXPECT_EQ(VideoFrame::INVALID, test_engine_->GetSurfaceFormat()); | |
| 313 } | 291 } |
| 314 | 292 |
| 315 } // namespace media | 293 } // namespace media |
| OLD | NEW |