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