Home | History | Annotate | Download | only in filters
      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 
      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"
     13 
     14 using ::testing::_;
     15 using ::testing::DoAll;
     16 using ::testing::InSequence;
     17 using ::testing::Return;
     18 using ::testing::SetArgumentPointee;
     19 using ::testing::StrictMock;
     20 
     21 namespace media {
     22 
     23 class MockProtocol : public FFmpegURLProtocol {
     24  public:
     25   MockProtocol() {}
     26 
     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());
     32 
     33  private:
     34   DISALLOW_COPY_AND_ASSIGN(MockProtocol);
     35 };
     36 
     37 class FFmpegGlueTest : public ::testing::Test {
     38  public:
     39   FFmpegGlueTest()
     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);
     46   }
     47 
     48   virtual ~FFmpegGlueTest() {
     49     // Ensure |glue_| and |protocol_| are still alive.
     50     CHECK(glue_.get());
     51     CHECK(protocol_.get());
     52 
     53     // |protocol_| should outlive |glue_|, so ensure it's destructed first.
     54     glue_.reset();
     55   }
     56 
     57   int ReadPacket(int size, uint8* data) {
     58     return glue_->format_context()->pb->read_packet(
     59         protocol_.get(), data, size);
     60   }
     61 
     62   int64 Seek(int64 offset, int whence) {
     63     return glue_->format_context()->pb->seek(protocol_.get(), offset, whence);
     64   }
     65 
     66  protected:
     67   scoped_ptr<FFmpegGlue> glue_;
     68   scoped_ptr< StrictMock<MockProtocol> > protocol_;
     69 
     70  private:
     71   DISALLOW_COPY_AND_ASSIGN(FFmpegGlueTest);
     72 };
     73 
     74 class FFmpegGlueDestructionTest : public ::testing::Test {
     75  public:
     76   FFmpegGlueDestructionTest() {}
     77 
     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);
     85   }
     86 
     87   virtual ~FFmpegGlueDestructionTest() {
     88     // Ensure Initialize() was called.
     89     CHECK(glue_.get());
     90     CHECK(protocol_.get());
     91 
     92     // |glue_| should be destroyed before |protocol_|.
     93     glue_.reset();
     94 
     95     // |protocol_| should be destroyed before |data_|.
     96     protocol_.reset();
     97     data_ = NULL;
     98   }
     99 
    100  protected:
    101   scoped_ptr<FFmpegGlue> glue_;
    102 
    103  private:
    104   scoped_ptr<InMemoryUrlProtocol> protocol_;
    105   scoped_refptr<DecoderBuffer> data_;
    106 
    107   DISALLOW_COPY_AND_ASSIGN(FFmpegGlueDestructionTest);
    108 };
    109 
    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);
    114 }
    115 
    116 // Test both successful and unsuccessful reads pass through correctly.
    117 TEST_F(FFmpegGlueTest, Read) {
    118   const int kBufferSize = 16;
    119   uint8 buffer[kBufferSize];
    120 
    121   // Reads are for the most part straight-through calls to Read().
    122   InSequence s;
    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));
    129 
    130   EXPECT_EQ(0, ReadPacket(0, buffer));
    131   EXPECT_EQ(kBufferSize, ReadPacket(kBufferSize, buffer));
    132   EXPECT_EQ(AVERROR(EIO), ReadPacket(kBufferSize, buffer));
    133 }
    134 
    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().
    139   InSequence s;
    140   EXPECT_CALL(*protocol_, SetPosition(-16))
    141       .WillOnce(Return(false));
    142 
    143   EXPECT_CALL(*protocol_, SetPosition(16))
    144       .WillOnce(Return(true));
    145   EXPECT_CALL(*protocol_, GetPosition(_))
    146       .WillOnce(DoAll(SetArgumentPointee<0>(8), Return(true)));
    147 
    148   EXPECT_EQ(AVERROR(EIO), Seek(-16, SEEK_SET));
    149   EXPECT_EQ(8, Seek(16, SEEK_SET));
    150 
    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));
    155 
    156   EXPECT_CALL(*protocol_, GetPosition(_))
    157       .WillOnce(DoAll(SetArgumentPointee<0>(8), Return(true)));
    158   EXPECT_CALL(*protocol_, SetPosition(16))
    159       .WillOnce(Return(false));
    160 
    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)));
    167 
    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));
    171 
    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));
    176 
    177   EXPECT_CALL(*protocol_, GetSize(_))
    178       .WillOnce(DoAll(SetArgumentPointee<0>(16), Return(true)));
    179   EXPECT_CALL(*protocol_, SetPosition(8))
    180       .WillOnce(Return(false));
    181 
    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)));
    188 
    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));
    192 
    193   // AVSEEK_SIZE should be a straight-through call to GetSize().
    194   EXPECT_CALL(*protocol_, GetSize(_))
    195       .WillOnce(Return(false));
    196 
    197   EXPECT_CALL(*protocol_, GetSize(_))
    198       .WillOnce(DoAll(SetArgumentPointee<0>(16), Return(true)));
    199 
    200   EXPECT_EQ(AVERROR(EIO), Seek(0, AVSEEK_SIZE));
    201   EXPECT_EQ(16, Seek(0, AVSEEK_SIZE));
    202 }
    203 
    204 // Ensure destruction release the appropriate resources when OpenContext() is
    205 // never called.
    206 TEST_F(FFmpegGlueDestructionTest, WithoutOpen) {
    207   Initialize("ten_byte_file");
    208 }
    209 
    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());
    215 }
    216 
    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());
    222 }
    223 
    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());
    229 }
    230 
    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);
    237 
    238   AVCodecContext* context = glue_->format_context()->streams[0]->codec;
    239   ASSERT_EQ(avcodec_open2(
    240       context, avcodec_find_decoder(context->codec_id), NULL), 0);
    241 }
    242 
    243 }  // namespace media
    244