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.
5 #include "base/logging.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "media/base/mock_filters.h"
8 #include "media/base/test_data_util.h"
9 #include "media/ffmpeg/ffmpeg_common.h"
10 #include "media/filters/ffmpeg_glue.h"
11 #include "media/filters/in_memory_url_protocol.h"
12 #include "testing/gtest/include/gtest/gtest.h"
15 using ::testing::DoAll
;
16 using ::testing::InSequence
;
17 using ::testing::Return
;
18 using ::testing::SetArgumentPointee
;
19 using ::testing::StrictMock
;
23 class MockProtocol
: public FFmpegURLProtocol
{
27 MOCK_METHOD2(Read
, int(int size
, uint8
* data
));
28 MOCK_METHOD1(GetPosition
, bool(int64
* position_out
));
29 MOCK_METHOD1(SetPosition
, bool(int64 position
));
30 MOCK_METHOD1(GetSize
, bool(int64
* size_out
));
31 MOCK_METHOD0(IsStreaming
, bool());
34 DISALLOW_COPY_AND_ASSIGN(MockProtocol
);
37 class FFmpegGlueTest
: public ::testing::Test
{
40 : protocol_(new StrictMock
<MockProtocol
>()) {
41 // IsStreaming() is called when opening.
42 EXPECT_CALL(*protocol_
.get(), IsStreaming()).WillOnce(Return(true));
43 glue_
.reset(new FFmpegGlue(protocol_
.get()));
44 CHECK(glue_
->format_context());
45 CHECK(glue_
->format_context()->pb
);
48 ~FFmpegGlueTest() override
{
49 // Ensure |glue_| and |protocol_| are still alive.
51 CHECK(protocol_
.get());
53 // |protocol_| should outlive |glue_|, so ensure it's destructed first.
57 int ReadPacket(int size
, uint8
* data
) {
58 return glue_
->format_context()->pb
->read_packet(
59 protocol_
.get(), data
, size
);
62 int64
Seek(int64 offset
, int whence
) {
63 return glue_
->format_context()->pb
->seek(protocol_
.get(), offset
, whence
);
67 scoped_ptr
<FFmpegGlue
> glue_
;
68 scoped_ptr
< StrictMock
<MockProtocol
> > protocol_
;
71 DISALLOW_COPY_AND_ASSIGN(FFmpegGlueTest
);
74 class FFmpegGlueDestructionTest
: public ::testing::Test
{
76 FFmpegGlueDestructionTest() {}
78 void Initialize(const char* filename
) {
79 data_
= ReadTestDataFile(filename
);
80 protocol_
.reset(new InMemoryUrlProtocol(
81 data_
->data(), data_
->data_size(), false));
82 glue_
.reset(new FFmpegGlue(protocol_
.get()));
83 CHECK(glue_
->format_context());
84 CHECK(glue_
->format_context()->pb
);
87 ~FFmpegGlueDestructionTest() override
{
88 // Ensure Initialize() was called.
90 CHECK(protocol_
.get());
92 // |glue_| should be destroyed before |protocol_|.
95 // |protocol_| should be destroyed before |data_|.
101 scoped_ptr
<FFmpegGlue
> glue_
;
104 scoped_ptr
<InMemoryUrlProtocol
> protocol_
;
105 scoped_refptr
<DecoderBuffer
> data_
;
107 DISALLOW_COPY_AND_ASSIGN(FFmpegGlueDestructionTest
);
110 // Ensure writing has been disabled.
111 TEST_F(FFmpegGlueTest
, Write
) {
112 ASSERT_FALSE(glue_
->format_context()->pb
->write_packet
);
113 ASSERT_FALSE(glue_
->format_context()->pb
->write_flag
);
116 // Test both successful and unsuccessful reads pass through correctly.
117 TEST_F(FFmpegGlueTest
, Read
) {
118 const int kBufferSize
= 16;
119 uint8 buffer
[kBufferSize
];
121 // Reads are for the most part straight-through calls to Read().
123 EXPECT_CALL(*protocol_
, Read(0, buffer
))
124 .WillOnce(Return(0));
125 EXPECT_CALL(*protocol_
, Read(kBufferSize
, buffer
))
126 .WillOnce(Return(kBufferSize
));
127 EXPECT_CALL(*protocol_
, Read(kBufferSize
, buffer
))
128 .WillOnce(Return(DataSource::kReadError
));
130 EXPECT_EQ(0, ReadPacket(0, buffer
));
131 EXPECT_EQ(kBufferSize
, ReadPacket(kBufferSize
, buffer
));
132 EXPECT_EQ(AVERROR(EIO
), ReadPacket(kBufferSize
, buffer
));
135 // Test a variety of seek operations.
136 TEST_F(FFmpegGlueTest
, Seek
) {
137 // SEEK_SET should be a straight-through call to SetPosition(), which when
138 // successful will return the result from GetPosition().
140 EXPECT_CALL(*protocol_
, SetPosition(-16))
141 .WillOnce(Return(false));
143 EXPECT_CALL(*protocol_
, SetPosition(16))
144 .WillOnce(Return(true));
145 EXPECT_CALL(*protocol_
, GetPosition(_
))
146 .WillOnce(DoAll(SetArgumentPointee
<0>(8), Return(true)));
148 EXPECT_EQ(AVERROR(EIO
), Seek(-16, SEEK_SET
));
149 EXPECT_EQ(8, Seek(16, SEEK_SET
));
151 // SEEK_CUR should call GetPosition() first, and if it succeeds add the offset
152 // to the result then call SetPosition()+GetPosition().
153 EXPECT_CALL(*protocol_
, GetPosition(_
))
154 .WillOnce(Return(false));
156 EXPECT_CALL(*protocol_
, GetPosition(_
))
157 .WillOnce(DoAll(SetArgumentPointee
<0>(8), Return(true)));
158 EXPECT_CALL(*protocol_
, SetPosition(16))
159 .WillOnce(Return(false));
161 EXPECT_CALL(*protocol_
, GetPosition(_
))
162 .WillOnce(DoAll(SetArgumentPointee
<0>(8), Return(true)));
163 EXPECT_CALL(*protocol_
, SetPosition(16))
164 .WillOnce(Return(true));
165 EXPECT_CALL(*protocol_
, GetPosition(_
))
166 .WillOnce(DoAll(SetArgumentPointee
<0>(16), Return(true)));
168 EXPECT_EQ(AVERROR(EIO
), Seek(8, SEEK_CUR
));
169 EXPECT_EQ(AVERROR(EIO
), Seek(8, SEEK_CUR
));
170 EXPECT_EQ(16, Seek(8, SEEK_CUR
));
172 // SEEK_END should call GetSize() first, and if it succeeds add the offset
173 // to the result then call SetPosition()+GetPosition().
174 EXPECT_CALL(*protocol_
, GetSize(_
))
175 .WillOnce(Return(false));
177 EXPECT_CALL(*protocol_
, GetSize(_
))
178 .WillOnce(DoAll(SetArgumentPointee
<0>(16), Return(true)));
179 EXPECT_CALL(*protocol_
, SetPosition(8))
180 .WillOnce(Return(false));
182 EXPECT_CALL(*protocol_
, GetSize(_
))
183 .WillOnce(DoAll(SetArgumentPointee
<0>(16), Return(true)));
184 EXPECT_CALL(*protocol_
, SetPosition(8))
185 .WillOnce(Return(true));
186 EXPECT_CALL(*protocol_
, GetPosition(_
))
187 .WillOnce(DoAll(SetArgumentPointee
<0>(8), Return(true)));
189 EXPECT_EQ(AVERROR(EIO
), Seek(-8, SEEK_END
));
190 EXPECT_EQ(AVERROR(EIO
), Seek(-8, SEEK_END
));
191 EXPECT_EQ(8, Seek(-8, SEEK_END
));
193 // AVSEEK_SIZE should be a straight-through call to GetSize().
194 EXPECT_CALL(*protocol_
, GetSize(_
))
195 .WillOnce(Return(false));
197 EXPECT_CALL(*protocol_
, GetSize(_
))
198 .WillOnce(DoAll(SetArgumentPointee
<0>(16), Return(true)));
200 EXPECT_EQ(AVERROR(EIO
), Seek(0, AVSEEK_SIZE
));
201 EXPECT_EQ(16, Seek(0, AVSEEK_SIZE
));
204 // Ensure destruction release the appropriate resources when OpenContext() is
206 TEST_F(FFmpegGlueDestructionTest
, WithoutOpen
) {
207 Initialize("ten_byte_file");
210 // Ensure destruction releases the appropriate resources when
211 // avformat_open_input() fails.
212 TEST_F(FFmpegGlueDestructionTest
, WithOpenFailure
) {
213 Initialize("ten_byte_file");
214 ASSERT_FALSE(glue_
->OpenContext());
217 // Ensure destruction release the appropriate resources when OpenContext() is
218 // called, but no streams have been opened.
219 TEST_F(FFmpegGlueDestructionTest
, WithOpenNoStreams
) {
220 Initialize("no_streams.webm");
221 ASSERT_TRUE(glue_
->OpenContext());
224 // Ensure destruction release the appropriate resources when OpenContext() is
225 // called and streams exist.
226 TEST_F(FFmpegGlueDestructionTest
, WithOpenWithStreams
) {
227 Initialize("bear-320x240.webm");
228 ASSERT_TRUE(glue_
->OpenContext());
231 // Ensure destruction release the appropriate resources when OpenContext() is
232 // called and streams have been opened.
233 TEST_F(FFmpegGlueDestructionTest
, WithOpenWithOpenStreams
) {
234 Initialize("bear-320x240.webm");
235 ASSERT_TRUE(glue_
->OpenContext());
236 ASSERT_GT(glue_
->format_context()->nb_streams
, 0u);
238 // Pick the audio stream (1) so this works when the ffmpeg video decoders are
240 AVCodecContext
* context
= glue_
->format_context()->streams
[1]->codec
;
241 ASSERT_EQ(0, avcodec_open2(
242 context
, avcodec_find_decoder(context
->codec_id
), NULL
));