123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "media/filters/ffmpeg_glue.h"
- #include <stdint.h>
- #include <memory>
- #include "base/check.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "build/chromeos_buildflags.h"
- #include "media/base/container_names.h"
- #include "media/base/mock_filters.h"
- #include "media/base/test_data_util.h"
- #include "media/ffmpeg/ffmpeg_common.h"
- #include "media/ffmpeg/ffmpeg_deleters.h"
- #include "media/filters/in_memory_url_protocol.h"
- #include "testing/gtest/include/gtest/gtest.h"
- using ::testing::_;
- using ::testing::DoAll;
- using ::testing::InSequence;
- using ::testing::Return;
- using ::testing::SetArgPointee;
- using ::testing::StrictMock;
- namespace media {
- class MockProtocol : public FFmpegURLProtocol {
- public:
- MockProtocol() = default;
- MockProtocol(const MockProtocol&) = delete;
- MockProtocol& operator=(const MockProtocol&) = delete;
- virtual ~MockProtocol() = default;
- MOCK_METHOD2(Read, int(int size, uint8_t* data));
- MOCK_METHOD1(GetPosition, bool(int64_t* position_out));
- MOCK_METHOD1(SetPosition, bool(int64_t position));
- MOCK_METHOD1(GetSize, bool(int64_t* size_out));
- MOCK_METHOD0(IsStreaming, bool());
- };
- class FFmpegGlueTest : public ::testing::Test {
- public:
- FFmpegGlueTest()
- : protocol_(new StrictMock<MockProtocol>()) {
- // IsStreaming() is called when opening.
- EXPECT_CALL(*protocol_.get(), IsStreaming()).WillOnce(Return(true));
- glue_ = std::make_unique<FFmpegGlue>(protocol_.get());
- CHECK(glue_->format_context());
- CHECK(glue_->format_context()->pb);
- }
- FFmpegGlueTest(const FFmpegGlueTest&) = delete;
- FFmpegGlueTest& operator=(const FFmpegGlueTest&) = delete;
- ~FFmpegGlueTest() override {
- // Ensure |glue_| and |protocol_| are still alive.
- CHECK(glue_.get());
- CHECK(protocol_.get());
- // |protocol_| should outlive |glue_|, so ensure it's destructed first.
- glue_.reset();
- }
- int ReadPacket(int size, uint8_t* data) {
- return glue_->format_context()->pb->read_packet(protocol_.get(), data,
- size);
- }
- int64_t Seek(int64_t offset, int whence) {
- return glue_->format_context()->pb->seek(protocol_.get(), offset, whence);
- }
- protected:
- std::unique_ptr<FFmpegGlue> glue_;
- std::unique_ptr<StrictMock<MockProtocol>> protocol_;
- };
- class FFmpegGlueDestructionTest : public ::testing::Test {
- public:
- FFmpegGlueDestructionTest() = default;
- void Initialize(const char* filename) {
- data_ = ReadTestDataFile(filename);
- protocol_ = std::make_unique<InMemoryUrlProtocol>(
- data_->data(), data_->data_size(), false);
- glue_ = std::make_unique<FFmpegGlue>(protocol_.get());
- CHECK(glue_->format_context());
- CHECK(glue_->format_context()->pb);
- }
- FFmpegGlueDestructionTest(const FFmpegGlueDestructionTest&) = delete;
- FFmpegGlueDestructionTest& operator=(const FFmpegGlueDestructionTest&) =
- delete;
- ~FFmpegGlueDestructionTest() override {
- // Ensure Initialize() was called.
- CHECK(glue_.get());
- CHECK(protocol_.get());
- // |glue_| should be destroyed before |protocol_|.
- glue_.reset();
- // |protocol_| should be destroyed before |data_|.
- protocol_.reset();
- data_.reset();
- }
- protected:
- std::unique_ptr<FFmpegGlue> glue_;
- private:
- std::unique_ptr<InMemoryUrlProtocol> protocol_;
- scoped_refptr<DecoderBuffer> data_;
- };
- // Tests that ensure we are using the correct AVInputFormat name given by ffmpeg
- // for supported containers.
- class FFmpegGlueContainerTest : public FFmpegGlueDestructionTest {
- public:
- FFmpegGlueContainerTest() = default;
- FFmpegGlueContainerTest(const FFmpegGlueContainerTest&) = delete;
- FFmpegGlueContainerTest& operator=(const FFmpegGlueContainerTest&) = delete;
- ~FFmpegGlueContainerTest() override = default;
- protected:
- void InitializeAndOpen(const char* filename) {
- Initialize(filename);
- ASSERT_TRUE(glue_->OpenContext());
- }
- void ExpectContainer(container_names::MediaContainerName container) {
- histogram_tester_.ExpectUniqueSample("Media.DetectedContainer", container,
- 1);
- }
- private:
- base::HistogramTester histogram_tester_;
- };
- // Ensure writing has been disabled.
- TEST_F(FFmpegGlueTest, Write) {
- ASSERT_FALSE(glue_->format_context()->pb->write_packet);
- ASSERT_FALSE(glue_->format_context()->pb->write_flag);
- }
- // Test both successful and unsuccessful reads pass through correctly.
- TEST_F(FFmpegGlueTest, Read) {
- const int kBufferSize = 16;
- uint8_t buffer[kBufferSize];
- // Reads are for the most part straight-through calls to Read().
- InSequence s;
- EXPECT_CALL(*protocol_, Read(0, buffer))
- .WillOnce(Return(0));
- EXPECT_CALL(*protocol_, Read(kBufferSize, buffer))
- .WillOnce(Return(kBufferSize));
- EXPECT_CALL(*protocol_, Read(kBufferSize, buffer))
- .WillOnce(Return(AVERROR(EIO)));
- EXPECT_EQ(0, ReadPacket(0, buffer));
- EXPECT_EQ(kBufferSize, ReadPacket(kBufferSize, buffer));
- EXPECT_EQ(AVERROR(EIO), ReadPacket(kBufferSize, buffer));
- }
- // Test a variety of seek operations.
- TEST_F(FFmpegGlueTest, Seek) {
- // SEEK_SET should be a straight-through call to SetPosition(), which when
- // successful will return the result from GetPosition().
- InSequence s;
- EXPECT_CALL(*protocol_, SetPosition(-16))
- .WillOnce(Return(false));
- EXPECT_CALL(*protocol_, SetPosition(16))
- .WillOnce(Return(true));
- EXPECT_CALL(*protocol_, GetPosition(_))
- .WillOnce(DoAll(SetArgPointee<0>(8), Return(true)));
- EXPECT_EQ(AVERROR(EIO), Seek(-16, SEEK_SET));
- EXPECT_EQ(8, Seek(16, SEEK_SET));
- // SEEK_CUR should call GetPosition() first, and if it succeeds add the offset
- // to the result then call SetPosition()+GetPosition().
- EXPECT_CALL(*protocol_, GetPosition(_))
- .WillOnce(Return(false));
- EXPECT_CALL(*protocol_, GetPosition(_))
- .WillOnce(DoAll(SetArgPointee<0>(8), Return(true)));
- EXPECT_CALL(*protocol_, SetPosition(16))
- .WillOnce(Return(false));
- EXPECT_CALL(*protocol_, GetPosition(_))
- .WillOnce(DoAll(SetArgPointee<0>(8), Return(true)));
- EXPECT_CALL(*protocol_, SetPosition(16))
- .WillOnce(Return(true));
- EXPECT_CALL(*protocol_, GetPosition(_))
- .WillOnce(DoAll(SetArgPointee<0>(16), Return(true)));
- EXPECT_EQ(AVERROR(EIO), Seek(8, SEEK_CUR));
- EXPECT_EQ(AVERROR(EIO), Seek(8, SEEK_CUR));
- EXPECT_EQ(16, Seek(8, SEEK_CUR));
- // SEEK_END should call GetSize() first, and if it succeeds add the offset
- // to the result then call SetPosition()+GetPosition().
- EXPECT_CALL(*protocol_, GetSize(_))
- .WillOnce(Return(false));
- EXPECT_CALL(*protocol_, GetSize(_))
- .WillOnce(DoAll(SetArgPointee<0>(16), Return(true)));
- EXPECT_CALL(*protocol_, SetPosition(8))
- .WillOnce(Return(false));
- EXPECT_CALL(*protocol_, GetSize(_))
- .WillOnce(DoAll(SetArgPointee<0>(16), Return(true)));
- EXPECT_CALL(*protocol_, SetPosition(8))
- .WillOnce(Return(true));
- EXPECT_CALL(*protocol_, GetPosition(_))
- .WillOnce(DoAll(SetArgPointee<0>(8), Return(true)));
- EXPECT_EQ(AVERROR(EIO), Seek(-8, SEEK_END));
- EXPECT_EQ(AVERROR(EIO), Seek(-8, SEEK_END));
- EXPECT_EQ(8, Seek(-8, SEEK_END));
- // AVSEEK_SIZE should be a straight-through call to GetSize().
- EXPECT_CALL(*protocol_, GetSize(_))
- .WillOnce(Return(false));
- EXPECT_CALL(*protocol_, GetSize(_))
- .WillOnce(DoAll(SetArgPointee<0>(16), Return(true)));
- EXPECT_EQ(AVERROR(EIO), Seek(0, AVSEEK_SIZE));
- EXPECT_EQ(16, Seek(0, AVSEEK_SIZE));
- }
- // Ensure destruction release the appropriate resources when OpenContext() is
- // never called.
- TEST_F(FFmpegGlueDestructionTest, WithoutOpen) {
- Initialize("ten_byte_file");
- }
- // Ensure destruction releases the appropriate resources when
- // avformat_open_input() fails.
- TEST_F(FFmpegGlueDestructionTest, WithOpenFailure) {
- Initialize("ten_byte_file");
- ASSERT_FALSE(glue_->OpenContext());
- }
- // Ensure destruction release the appropriate resources when OpenContext() is
- // called, but no streams have been opened.
- TEST_F(FFmpegGlueDestructionTest, WithOpenNoStreams) {
- Initialize("no_streams.webm");
- ASSERT_TRUE(glue_->OpenContext());
- }
- // Ensure destruction release the appropriate resources when OpenContext() is
- // called and streams exist.
- TEST_F(FFmpegGlueDestructionTest, WithOpenWithStreams) {
- Initialize("bear-320x240.webm");
- ASSERT_TRUE(glue_->OpenContext());
- }
- // Ensure destruction release the appropriate resources when OpenContext() is
- // called and streams have been opened. This now requires user of FFmpegGlue to
- // ensure any allocated AVCodecContext is closed prior to ~FFmpegGlue().
- TEST_F(FFmpegGlueDestructionTest, WithOpenWithOpenStreams) {
- Initialize("bear-320x240.webm");
- ASSERT_TRUE(glue_->OpenContext());
- ASSERT_GT(glue_->format_context()->nb_streams, 0u);
- // Use ScopedPtrAVFreeContext to ensure |context| is closed, and use scoping
- // and ordering to ensure |context| is destructed before |glue_|.
- // Pick the audio stream (1) so this works when the ffmpeg video decoders are
- // disabled.
- std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> context(
- AVStreamToAVCodecContext(glue_->format_context()->streams[1]));
- ASSERT_NE(nullptr, context.get());
- ASSERT_EQ(0, avcodec_open2(context.get(),
- avcodec_find_decoder(context->codec_id), nullptr));
- }
- TEST_F(FFmpegGlueContainerTest, OGG) {
- InitializeAndOpen("sfx.ogg");
- ExpectContainer(container_names::CONTAINER_OGG);
- }
- TEST_F(FFmpegGlueContainerTest, WEBM) {
- InitializeAndOpen("sfx-opus-441.webm");
- ExpectContainer(container_names::CONTAINER_WEBM);
- }
- TEST_F(FFmpegGlueContainerTest, FLAC) {
- InitializeAndOpen("sfx.flac");
- ExpectContainer(container_names::CONTAINER_FLAC);
- }
- TEST_F(FFmpegGlueContainerTest, WAV) {
- InitializeAndOpen("sfx_s16le.wav");
- ExpectContainer(container_names::CONTAINER_WAV);
- }
- TEST_F(FFmpegGlueContainerTest, MP3) {
- InitializeAndOpen("sfx.mp3");
- ExpectContainer(container_names::CONTAINER_MP3);
- }
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- TEST_F(FFmpegGlueContainerTest, MOV) {
- InitializeAndOpen("sfx.m4a");
- ExpectContainer(container_names::CONTAINER_MOV);
- }
- TEST_F(FFmpegGlueContainerTest, AAC) {
- InitializeAndOpen("sfx.adts");
- ExpectContainer(container_names::CONTAINER_AAC);
- }
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- TEST_F(FFmpegGlueContainerTest, AVI) {
- InitializeAndOpen("bear.avi");
- ExpectContainer(container_names::CONTAINER_AVI);
- }
- TEST_F(FFmpegGlueContainerTest, AMR) {
- InitializeAndOpen("bear.amr");
- ExpectContainer(container_names::CONTAINER_AMR);
- }
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- // Probe something unsupported to ensure we fall back to the our internal guess.
- TEST_F(FFmpegGlueContainerTest, FLV) {
- Initialize("bear.flv");
- ASSERT_FALSE(glue_->OpenContext());
- ExpectContainer(container_names::CONTAINER_FLV);
- }
- } // namespace media
|