ffmpeg_glue_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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 "media/filters/ffmpeg_glue.h"
  5. #include <stdint.h>
  6. #include <memory>
  7. #include "base/check.h"
  8. #include "base/test/metrics/histogram_tester.h"
  9. #include "build/chromeos_buildflags.h"
  10. #include "media/base/container_names.h"
  11. #include "media/base/mock_filters.h"
  12. #include "media/base/test_data_util.h"
  13. #include "media/ffmpeg/ffmpeg_common.h"
  14. #include "media/ffmpeg/ffmpeg_deleters.h"
  15. #include "media/filters/in_memory_url_protocol.h"
  16. #include "testing/gtest/include/gtest/gtest.h"
  17. using ::testing::_;
  18. using ::testing::DoAll;
  19. using ::testing::InSequence;
  20. using ::testing::Return;
  21. using ::testing::SetArgPointee;
  22. using ::testing::StrictMock;
  23. namespace media {
  24. class MockProtocol : public FFmpegURLProtocol {
  25. public:
  26. MockProtocol() = default;
  27. MockProtocol(const MockProtocol&) = delete;
  28. MockProtocol& operator=(const MockProtocol&) = delete;
  29. virtual ~MockProtocol() = default;
  30. MOCK_METHOD2(Read, int(int size, uint8_t* data));
  31. MOCK_METHOD1(GetPosition, bool(int64_t* position_out));
  32. MOCK_METHOD1(SetPosition, bool(int64_t position));
  33. MOCK_METHOD1(GetSize, bool(int64_t* size_out));
  34. MOCK_METHOD0(IsStreaming, bool());
  35. };
  36. class FFmpegGlueTest : public ::testing::Test {
  37. public:
  38. FFmpegGlueTest()
  39. : protocol_(new StrictMock<MockProtocol>()) {
  40. // IsStreaming() is called when opening.
  41. EXPECT_CALL(*protocol_.get(), IsStreaming()).WillOnce(Return(true));
  42. glue_ = std::make_unique<FFmpegGlue>(protocol_.get());
  43. CHECK(glue_->format_context());
  44. CHECK(glue_->format_context()->pb);
  45. }
  46. FFmpegGlueTest(const FFmpegGlueTest&) = delete;
  47. FFmpegGlueTest& operator=(const FFmpegGlueTest&) = delete;
  48. ~FFmpegGlueTest() override {
  49. // Ensure |glue_| and |protocol_| are still alive.
  50. CHECK(glue_.get());
  51. CHECK(protocol_.get());
  52. // |protocol_| should outlive |glue_|, so ensure it's destructed first.
  53. glue_.reset();
  54. }
  55. int ReadPacket(int size, uint8_t* data) {
  56. return glue_->format_context()->pb->read_packet(protocol_.get(), data,
  57. size);
  58. }
  59. int64_t Seek(int64_t offset, int whence) {
  60. return glue_->format_context()->pb->seek(protocol_.get(), offset, whence);
  61. }
  62. protected:
  63. std::unique_ptr<FFmpegGlue> glue_;
  64. std::unique_ptr<StrictMock<MockProtocol>> protocol_;
  65. };
  66. class FFmpegGlueDestructionTest : public ::testing::Test {
  67. public:
  68. FFmpegGlueDestructionTest() = default;
  69. void Initialize(const char* filename) {
  70. data_ = ReadTestDataFile(filename);
  71. protocol_ = std::make_unique<InMemoryUrlProtocol>(
  72. data_->data(), data_->data_size(), false);
  73. glue_ = std::make_unique<FFmpegGlue>(protocol_.get());
  74. CHECK(glue_->format_context());
  75. CHECK(glue_->format_context()->pb);
  76. }
  77. FFmpegGlueDestructionTest(const FFmpegGlueDestructionTest&) = delete;
  78. FFmpegGlueDestructionTest& operator=(const FFmpegGlueDestructionTest&) =
  79. delete;
  80. ~FFmpegGlueDestructionTest() override {
  81. // Ensure Initialize() was called.
  82. CHECK(glue_.get());
  83. CHECK(protocol_.get());
  84. // |glue_| should be destroyed before |protocol_|.
  85. glue_.reset();
  86. // |protocol_| should be destroyed before |data_|.
  87. protocol_.reset();
  88. data_.reset();
  89. }
  90. protected:
  91. std::unique_ptr<FFmpegGlue> glue_;
  92. private:
  93. std::unique_ptr<InMemoryUrlProtocol> protocol_;
  94. scoped_refptr<DecoderBuffer> data_;
  95. };
  96. // Tests that ensure we are using the correct AVInputFormat name given by ffmpeg
  97. // for supported containers.
  98. class FFmpegGlueContainerTest : public FFmpegGlueDestructionTest {
  99. public:
  100. FFmpegGlueContainerTest() = default;
  101. FFmpegGlueContainerTest(const FFmpegGlueContainerTest&) = delete;
  102. FFmpegGlueContainerTest& operator=(const FFmpegGlueContainerTest&) = delete;
  103. ~FFmpegGlueContainerTest() override = default;
  104. protected:
  105. void InitializeAndOpen(const char* filename) {
  106. Initialize(filename);
  107. ASSERT_TRUE(glue_->OpenContext());
  108. }
  109. void ExpectContainer(container_names::MediaContainerName container) {
  110. histogram_tester_.ExpectUniqueSample("Media.DetectedContainer", container,
  111. 1);
  112. }
  113. private:
  114. base::HistogramTester histogram_tester_;
  115. };
  116. // Ensure writing has been disabled.
  117. TEST_F(FFmpegGlueTest, Write) {
  118. ASSERT_FALSE(glue_->format_context()->pb->write_packet);
  119. ASSERT_FALSE(glue_->format_context()->pb->write_flag);
  120. }
  121. // Test both successful and unsuccessful reads pass through correctly.
  122. TEST_F(FFmpegGlueTest, Read) {
  123. const int kBufferSize = 16;
  124. uint8_t buffer[kBufferSize];
  125. // Reads are for the most part straight-through calls to Read().
  126. InSequence s;
  127. EXPECT_CALL(*protocol_, Read(0, buffer))
  128. .WillOnce(Return(0));
  129. EXPECT_CALL(*protocol_, Read(kBufferSize, buffer))
  130. .WillOnce(Return(kBufferSize));
  131. EXPECT_CALL(*protocol_, Read(kBufferSize, buffer))
  132. .WillOnce(Return(AVERROR(EIO)));
  133. EXPECT_EQ(0, ReadPacket(0, buffer));
  134. EXPECT_EQ(kBufferSize, ReadPacket(kBufferSize, buffer));
  135. EXPECT_EQ(AVERROR(EIO), ReadPacket(kBufferSize, buffer));
  136. }
  137. // Test a variety of seek operations.
  138. TEST_F(FFmpegGlueTest, Seek) {
  139. // SEEK_SET should be a straight-through call to SetPosition(), which when
  140. // successful will return the result from GetPosition().
  141. InSequence s;
  142. EXPECT_CALL(*protocol_, SetPosition(-16))
  143. .WillOnce(Return(false));
  144. EXPECT_CALL(*protocol_, SetPosition(16))
  145. .WillOnce(Return(true));
  146. EXPECT_CALL(*protocol_, GetPosition(_))
  147. .WillOnce(DoAll(SetArgPointee<0>(8), Return(true)));
  148. EXPECT_EQ(AVERROR(EIO), Seek(-16, SEEK_SET));
  149. EXPECT_EQ(8, Seek(16, SEEK_SET));
  150. // SEEK_CUR should call GetPosition() first, and if it succeeds add the offset
  151. // to the result then call SetPosition()+GetPosition().
  152. EXPECT_CALL(*protocol_, GetPosition(_))
  153. .WillOnce(Return(false));
  154. EXPECT_CALL(*protocol_, GetPosition(_))
  155. .WillOnce(DoAll(SetArgPointee<0>(8), Return(true)));
  156. EXPECT_CALL(*protocol_, SetPosition(16))
  157. .WillOnce(Return(false));
  158. EXPECT_CALL(*protocol_, GetPosition(_))
  159. .WillOnce(DoAll(SetArgPointee<0>(8), Return(true)));
  160. EXPECT_CALL(*protocol_, SetPosition(16))
  161. .WillOnce(Return(true));
  162. EXPECT_CALL(*protocol_, GetPosition(_))
  163. .WillOnce(DoAll(SetArgPointee<0>(16), Return(true)));
  164. EXPECT_EQ(AVERROR(EIO), Seek(8, SEEK_CUR));
  165. EXPECT_EQ(AVERROR(EIO), Seek(8, SEEK_CUR));
  166. EXPECT_EQ(16, Seek(8, SEEK_CUR));
  167. // SEEK_END should call GetSize() first, and if it succeeds add the offset
  168. // to the result then call SetPosition()+GetPosition().
  169. EXPECT_CALL(*protocol_, GetSize(_))
  170. .WillOnce(Return(false));
  171. EXPECT_CALL(*protocol_, GetSize(_))
  172. .WillOnce(DoAll(SetArgPointee<0>(16), Return(true)));
  173. EXPECT_CALL(*protocol_, SetPosition(8))
  174. .WillOnce(Return(false));
  175. EXPECT_CALL(*protocol_, GetSize(_))
  176. .WillOnce(DoAll(SetArgPointee<0>(16), Return(true)));
  177. EXPECT_CALL(*protocol_, SetPosition(8))
  178. .WillOnce(Return(true));
  179. EXPECT_CALL(*protocol_, GetPosition(_))
  180. .WillOnce(DoAll(SetArgPointee<0>(8), Return(true)));
  181. EXPECT_EQ(AVERROR(EIO), Seek(-8, SEEK_END));
  182. EXPECT_EQ(AVERROR(EIO), Seek(-8, SEEK_END));
  183. EXPECT_EQ(8, Seek(-8, SEEK_END));
  184. // AVSEEK_SIZE should be a straight-through call to GetSize().
  185. EXPECT_CALL(*protocol_, GetSize(_))
  186. .WillOnce(Return(false));
  187. EXPECT_CALL(*protocol_, GetSize(_))
  188. .WillOnce(DoAll(SetArgPointee<0>(16), Return(true)));
  189. EXPECT_EQ(AVERROR(EIO), Seek(0, AVSEEK_SIZE));
  190. EXPECT_EQ(16, Seek(0, AVSEEK_SIZE));
  191. }
  192. // Ensure destruction release the appropriate resources when OpenContext() is
  193. // never called.
  194. TEST_F(FFmpegGlueDestructionTest, WithoutOpen) {
  195. Initialize("ten_byte_file");
  196. }
  197. // Ensure destruction releases the appropriate resources when
  198. // avformat_open_input() fails.
  199. TEST_F(FFmpegGlueDestructionTest, WithOpenFailure) {
  200. Initialize("ten_byte_file");
  201. ASSERT_FALSE(glue_->OpenContext());
  202. }
  203. // Ensure destruction release the appropriate resources when OpenContext() is
  204. // called, but no streams have been opened.
  205. TEST_F(FFmpegGlueDestructionTest, WithOpenNoStreams) {
  206. Initialize("no_streams.webm");
  207. ASSERT_TRUE(glue_->OpenContext());
  208. }
  209. // Ensure destruction release the appropriate resources when OpenContext() is
  210. // called and streams exist.
  211. TEST_F(FFmpegGlueDestructionTest, WithOpenWithStreams) {
  212. Initialize("bear-320x240.webm");
  213. ASSERT_TRUE(glue_->OpenContext());
  214. }
  215. // Ensure destruction release the appropriate resources when OpenContext() is
  216. // called and streams have been opened. This now requires user of FFmpegGlue to
  217. // ensure any allocated AVCodecContext is closed prior to ~FFmpegGlue().
  218. TEST_F(FFmpegGlueDestructionTest, WithOpenWithOpenStreams) {
  219. Initialize("bear-320x240.webm");
  220. ASSERT_TRUE(glue_->OpenContext());
  221. ASSERT_GT(glue_->format_context()->nb_streams, 0u);
  222. // Use ScopedPtrAVFreeContext to ensure |context| is closed, and use scoping
  223. // and ordering to ensure |context| is destructed before |glue_|.
  224. // Pick the audio stream (1) so this works when the ffmpeg video decoders are
  225. // disabled.
  226. std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> context(
  227. AVStreamToAVCodecContext(glue_->format_context()->streams[1]));
  228. ASSERT_NE(nullptr, context.get());
  229. ASSERT_EQ(0, avcodec_open2(context.get(),
  230. avcodec_find_decoder(context->codec_id), nullptr));
  231. }
  232. TEST_F(FFmpegGlueContainerTest, OGG) {
  233. InitializeAndOpen("sfx.ogg");
  234. ExpectContainer(container_names::CONTAINER_OGG);
  235. }
  236. TEST_F(FFmpegGlueContainerTest, WEBM) {
  237. InitializeAndOpen("sfx-opus-441.webm");
  238. ExpectContainer(container_names::CONTAINER_WEBM);
  239. }
  240. TEST_F(FFmpegGlueContainerTest, FLAC) {
  241. InitializeAndOpen("sfx.flac");
  242. ExpectContainer(container_names::CONTAINER_FLAC);
  243. }
  244. TEST_F(FFmpegGlueContainerTest, WAV) {
  245. InitializeAndOpen("sfx_s16le.wav");
  246. ExpectContainer(container_names::CONTAINER_WAV);
  247. }
  248. TEST_F(FFmpegGlueContainerTest, MP3) {
  249. InitializeAndOpen("sfx.mp3");
  250. ExpectContainer(container_names::CONTAINER_MP3);
  251. }
  252. #if BUILDFLAG(USE_PROPRIETARY_CODECS)
  253. TEST_F(FFmpegGlueContainerTest, MOV) {
  254. InitializeAndOpen("sfx.m4a");
  255. ExpectContainer(container_names::CONTAINER_MOV);
  256. }
  257. TEST_F(FFmpegGlueContainerTest, AAC) {
  258. InitializeAndOpen("sfx.adts");
  259. ExpectContainer(container_names::CONTAINER_AAC);
  260. }
  261. #if BUILDFLAG(IS_CHROMEOS_ASH)
  262. TEST_F(FFmpegGlueContainerTest, AVI) {
  263. InitializeAndOpen("bear.avi");
  264. ExpectContainer(container_names::CONTAINER_AVI);
  265. }
  266. TEST_F(FFmpegGlueContainerTest, AMR) {
  267. InitializeAndOpen("bear.amr");
  268. ExpectContainer(container_names::CONTAINER_AMR);
  269. }
  270. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  271. #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
  272. // Probe something unsupported to ensure we fall back to the our internal guess.
  273. TEST_F(FFmpegGlueContainerTest, FLV) {
  274. Initialize("bear.flv");
  275. ASSERT_FALSE(glue_->OpenContext());
  276. ExpectContainer(container_names::CONTAINER_FLV);
  277. }
  278. } // namespace media