ffmpeg_video_decoder_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <stdint.h>
  5. #include <list>
  6. #include <string>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "base/callback_helpers.h"
  10. #include "base/memory/ref_counted.h"
  11. #include "base/memory/singleton.h"
  12. #include "base/run_loop.h"
  13. #include "base/strings/string_util.h"
  14. #include "base/test/gmock_callback_support.h"
  15. #include "base/test/task_environment.h"
  16. #include "media/base/decoder_buffer.h"
  17. #include "media/base/limits.h"
  18. #include "media/base/media_log.h"
  19. #include "media/base/media_util.h"
  20. #include "media/base/mock_filters.h"
  21. #include "media/base/mock_media_log.h"
  22. #include "media/base/test_data_util.h"
  23. #include "media/base/test_helpers.h"
  24. #include "media/base/video_decoder.h"
  25. #include "media/base/video_frame.h"
  26. #include "media/base/video_util.h"
  27. #include "media/ffmpeg/ffmpeg_common.h"
  28. #include "media/filters/ffmpeg_video_decoder.h"
  29. #include "testing/gmock/include/gmock/gmock.h"
  30. using ::testing::_;
  31. using ::testing::AtLeast;
  32. using ::testing::AtMost;
  33. using ::testing::InSequence;
  34. using ::testing::IsNull;
  35. using ::testing::Return;
  36. using ::testing::SaveArg;
  37. using ::testing::StrictMock;
  38. namespace media {
  39. static const gfx::Size kCodedSize(320, 240);
  40. static const gfx::Rect kVisibleRect(320, 240);
  41. static const gfx::Size kNaturalSize(320, 240);
  42. ACTION_P(ReturnBuffer, buffer) {
  43. arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
  44. }
  45. MATCHER(ContainsFailedToSendLog, "") {
  46. return CONTAINS_STRING(arg, "Failed to send");
  47. }
  48. class FFmpegVideoDecoderTest : public testing::Test {
  49. public:
  50. FFmpegVideoDecoderTest() : decoder_(new FFmpegVideoDecoder(&media_log_)) {
  51. // Initialize various test buffers.
  52. frame_buffer_.reset(new uint8_t[kCodedSize.GetArea()]);
  53. end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer();
  54. i_frame_buffer_ = ReadTestDataFile("vp8-I-frame-320x240");
  55. corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame");
  56. }
  57. FFmpegVideoDecoderTest(const FFmpegVideoDecoderTest&) = delete;
  58. FFmpegVideoDecoderTest& operator=(const FFmpegVideoDecoderTest&) = delete;
  59. ~FFmpegVideoDecoderTest() override { Destroy(); }
  60. void Initialize() {
  61. InitializeWithConfig(TestVideoConfig::Normal());
  62. }
  63. void InitializeWithConfigWithResult(const VideoDecoderConfig& config,
  64. bool success) {
  65. decoder_->Initialize(
  66. config, false, nullptr,
  67. base::BindOnce(
  68. [](bool success, DecoderStatus status) {
  69. EXPECT_EQ(status.is_ok(), success);
  70. },
  71. success),
  72. base::BindRepeating(&FFmpegVideoDecoderTest::FrameReady,
  73. base::Unretained(this)),
  74. base::NullCallback());
  75. base::RunLoop().RunUntilIdle();
  76. }
  77. void InitializeWithConfig(const VideoDecoderConfig& config) {
  78. InitializeWithConfigWithResult(config, true);
  79. }
  80. void Reinitialize() {
  81. InitializeWithConfig(TestVideoConfig::Large());
  82. }
  83. void Reset() {
  84. decoder_->Reset(NewExpectedClosure());
  85. base::RunLoop().RunUntilIdle();
  86. }
  87. void Destroy() {
  88. decoder_.reset();
  89. base::RunLoop().RunUntilIdle();
  90. }
  91. // Sets up expectations and actions to put FFmpegVideoDecoder in an active
  92. // decoding state.
  93. void EnterDecodingState() {
  94. EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
  95. ASSERT_EQ(1U, output_frames_.size());
  96. }
  97. // Sets up expectations and actions to put FFmpegVideoDecoder in an end
  98. // of stream state.
  99. void EnterEndOfStreamState() {
  100. EXPECT_TRUE(DecodeSingleFrame(end_of_stream_buffer_).is_ok());
  101. ASSERT_FALSE(output_frames_.empty());
  102. }
  103. typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers;
  104. typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames;
  105. // Decodes all buffers in |input_buffers| and push all successfully decoded
  106. // output frames into |output_frames|.
  107. // Returns the last decode status returned by the decoder.
  108. DecoderStatus DecodeMultipleFrames(const InputBuffers& input_buffers) {
  109. for (auto iter = input_buffers.begin(); iter != input_buffers.end();
  110. ++iter) {
  111. DecoderStatus status = Decode(*iter);
  112. switch (status.code()) {
  113. case DecoderStatus::Codes::kOk:
  114. break;
  115. case DecoderStatus::Codes::kAborted:
  116. NOTREACHED();
  117. [[fallthrough]];
  118. default:
  119. DCHECK(output_frames_.empty());
  120. return status;
  121. }
  122. }
  123. return DecoderStatus::Codes::kOk;
  124. }
  125. // Decodes the single compressed frame in |buffer| and writes the
  126. // uncompressed output to |video_frame|. This method works with single
  127. // and multithreaded decoders. End of stream buffers are used to trigger
  128. // the frame to be returned in the multithreaded decoder case.
  129. DecoderStatus DecodeSingleFrame(scoped_refptr<DecoderBuffer> buffer) {
  130. InputBuffers input_buffers;
  131. input_buffers.push_back(buffer);
  132. input_buffers.push_back(end_of_stream_buffer_);
  133. return DecodeMultipleFrames(input_buffers);
  134. }
  135. // Decodes |i_frame_buffer_| and then decodes the data contained in
  136. // the file named |test_file_name|. This function expects both buffers
  137. // to decode to frames that are the same size.
  138. void DecodeIFrameThenTestFile(const std::string& test_file_name,
  139. int expected_width,
  140. int expected_height) {
  141. Initialize();
  142. scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
  143. InputBuffers input_buffers;
  144. input_buffers.push_back(i_frame_buffer_);
  145. input_buffers.push_back(buffer);
  146. input_buffers.push_back(end_of_stream_buffer_);
  147. DecoderStatus status = DecodeMultipleFrames(input_buffers);
  148. EXPECT_TRUE(status.is_ok());
  149. ASSERT_EQ(2U, output_frames_.size());
  150. gfx::Size original_size = kVisibleRect.size();
  151. EXPECT_EQ(original_size.width(),
  152. output_frames_[0]->visible_rect().size().width());
  153. EXPECT_EQ(original_size.height(),
  154. output_frames_[0]->visible_rect().size().height());
  155. EXPECT_EQ(expected_width,
  156. output_frames_[1]->visible_rect().size().width());
  157. EXPECT_EQ(expected_height,
  158. output_frames_[1]->visible_rect().size().height());
  159. }
  160. DecoderStatus Decode(scoped_refptr<DecoderBuffer> buffer) {
  161. DecoderStatus status;
  162. EXPECT_CALL(*this, DecodeDone(_)).WillOnce(SaveArg<0>(&status));
  163. decoder_->Decode(buffer, base::BindOnce(&FFmpegVideoDecoderTest::DecodeDone,
  164. base::Unretained(this)));
  165. base::RunLoop().RunUntilIdle();
  166. return status;
  167. }
  168. void FrameReady(scoped_refptr<VideoFrame> frame) {
  169. DCHECK(!frame->metadata().end_of_stream);
  170. output_frames_.push_back(std::move(frame));
  171. }
  172. MOCK_METHOD1(DecodeDone, void(DecoderStatus));
  173. StrictMock<MockMediaLog> media_log_;
  174. base::test::SingleThreadTaskEnvironment task_environment_;
  175. std::unique_ptr<FFmpegVideoDecoder> decoder_;
  176. // Various buffers for testing.
  177. std::unique_ptr<uint8_t[]> frame_buffer_;
  178. scoped_refptr<DecoderBuffer> end_of_stream_buffer_;
  179. scoped_refptr<DecoderBuffer> i_frame_buffer_;
  180. scoped_refptr<DecoderBuffer> corrupt_i_frame_buffer_;
  181. OutputFrames output_frames_;
  182. };
  183. TEST_F(FFmpegVideoDecoderTest, Initialize_Normal) {
  184. Initialize();
  185. }
  186. TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
  187. // Specify Theora w/o extra data so that avcodec_open2() fails.
  188. VideoDecoderConfig config(VideoCodec::kTheora, VIDEO_CODEC_PROFILE_UNKNOWN,
  189. VideoDecoderConfig::AlphaMode::kIsOpaque,
  190. VideoColorSpace(), kNoTransformation, kCodedSize,
  191. kVisibleRect, kNaturalSize, EmptyExtraData(),
  192. EncryptionScheme::kUnencrypted);
  193. InitializeWithConfigWithResult(config, false);
  194. }
  195. TEST_F(FFmpegVideoDecoderTest, Reinitialize_Normal) {
  196. Initialize();
  197. Reinitialize();
  198. }
  199. TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterDecodeFrame) {
  200. Initialize();
  201. EnterDecodingState();
  202. Reinitialize();
  203. }
  204. TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterReset) {
  205. Initialize();
  206. EnterDecodingState();
  207. Reset();
  208. Reinitialize();
  209. }
  210. TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) {
  211. Initialize();
  212. // Simulate decoding a single frame.
  213. EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
  214. ASSERT_EQ(1U, output_frames_.size());
  215. }
  216. TEST_F(FFmpegVideoDecoderTest, DecodeFrame_OOM) {
  217. Initialize();
  218. decoder_->force_allocation_error_for_testing();
  219. EXPECT_MEDIA_LOG(_);
  220. EXPECT_FALSE(DecodeSingleFrame(i_frame_buffer_).is_ok());
  221. EXPECT_TRUE(output_frames_.empty());
  222. }
  223. TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
  224. Initialize();
  225. EXPECT_MEDIA_LOG(ContainsFailedToSendLog());
  226. // The error is only raised on the second decode attempt, so we expect at
  227. // least one successful decode but we don't expect valid frame to be decoded.
  228. // During the second decode attempt an error is raised.
  229. EXPECT_TRUE(Decode(corrupt_i_frame_buffer_).is_ok());
  230. EXPECT_TRUE(output_frames_.empty());
  231. EXPECT_THAT(Decode(i_frame_buffer_), IsDecodeErrorStatus());
  232. EXPECT_TRUE(output_frames_.empty());
  233. // After a decode error occurred, all following decodes will return
  234. // DecoderStatus::Codes::kFailed.
  235. EXPECT_THAT(Decode(i_frame_buffer_), IsDecodeErrorStatus());
  236. EXPECT_TRUE(output_frames_.empty());
  237. }
  238. // A corrupt frame followed by an EOS buffer should raise a decode error.
  239. TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) {
  240. Initialize();
  241. EXPECT_MEDIA_LOG(ContainsFailedToSendLog());
  242. EXPECT_THAT(DecodeSingleFrame(corrupt_i_frame_buffer_),
  243. IsDecodeErrorStatus());
  244. }
  245. // Decode |i_frame_buffer_| and then a frame with a larger width and verify
  246. // the output size was adjusted.
  247. TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerWidth) {
  248. DecodeIFrameThenTestFile("vp8-I-frame-640x240", 640, 240);
  249. }
  250. // Decode |i_frame_buffer_| and then a frame with a smaller width and verify
  251. // the output size was adjusted.
  252. TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerWidth) {
  253. DecodeIFrameThenTestFile("vp8-I-frame-160x240", 160, 240);
  254. }
  255. // Decode |i_frame_buffer_| and then a frame with a larger height and verify
  256. // the output size was adjusted.
  257. TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerHeight) {
  258. DecodeIFrameThenTestFile("vp8-I-frame-320x480", 320, 480);
  259. }
  260. // Decode |i_frame_buffer_| and then a frame with a smaller height and verify
  261. // the output size was adjusted.
  262. TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerHeight) {
  263. DecodeIFrameThenTestFile("vp8-I-frame-320x120", 320, 120);
  264. }
  265. // Test resetting when decoder has initialized but not decoded.
  266. TEST_F(FFmpegVideoDecoderTest, Reset_Initialized) {
  267. Initialize();
  268. Reset();
  269. }
  270. // Test resetting when decoder has decoded single frame.
  271. TEST_F(FFmpegVideoDecoderTest, Reset_Decoding) {
  272. Initialize();
  273. EnterDecodingState();
  274. Reset();
  275. }
  276. // Test resetting when decoder has hit end of stream.
  277. TEST_F(FFmpegVideoDecoderTest, Reset_EndOfStream) {
  278. Initialize();
  279. EnterDecodingState();
  280. EnterEndOfStreamState();
  281. Reset();
  282. }
  283. // Test destruction when decoder has initialized but not decoded.
  284. TEST_F(FFmpegVideoDecoderTest, Destroy_Initialized) {
  285. Initialize();
  286. Destroy();
  287. }
  288. // Test destruction when decoder has decoded single frame.
  289. TEST_F(FFmpegVideoDecoderTest, Destroy_Decoding) {
  290. Initialize();
  291. EnterDecodingState();
  292. Destroy();
  293. }
  294. // Test destruction when decoder has hit end of stream.
  295. TEST_F(FFmpegVideoDecoderTest, Destroy_EndOfStream) {
  296. Initialize();
  297. EnterDecodingState();
  298. EnterEndOfStreamState();
  299. Destroy();
  300. }
  301. } // namespace media