blob: 22286d7f8635921243e0d9d512415442f28a421b [file] [log] [blame]
// Copyright 2020 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/gpu/mac/vp9_super_frame_bitstream_filter.h"
#include <CoreMedia/CoreMedia.h>
#include "media/base/media.h"
#include "media/base/test_data_util.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_glue.h"
#include "media/filters/in_memory_url_protocol.h"
#include "media/filters/vp9_parser.h"
#include "media/media_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
#if BUILDFLAG(ENABLE_FFMPEG)
class VP9SuperFrameBitstreamFilterTest : public testing::Test {
public:
VP9SuperFrameBitstreamFilterTest()
: parser_(/*parsing_compressed_header=*/false) {
InitializeMediaLibrary();
}
~VP9SuperFrameBitstreamFilterTest() override = default;
void LoadTestData(const char* file_name) {
buffer_ = ReadTestDataFile(file_name);
ASSERT_TRUE(buffer_);
// Initialize ffmpeg with the file data.
protocol_ = std::make_unique<InMemoryUrlProtocol>(
buffer_->data(), buffer_->data_size(), false);
glue_ = std::make_unique<FFmpegGlue>(protocol_.get());
ASSERT_TRUE(glue_->OpenContext());
}
scoped_refptr<DecoderBuffer> ReadPacket(int stream_index = 0) {
AVPacket packet = {0};
while (av_read_frame(glue_->format_context(), &packet) >= 0) {
if (packet.stream_index == stream_index) {
auto buffer = DecoderBuffer::CopyFrom(packet.data, packet.size);
av_packet_unref(&packet);
return buffer;
}
av_packet_unref(&packet);
}
return nullptr;
}
Vp9Parser::Result ParseNextFrame() {
// Temporaries for the Vp9Parser.
Vp9FrameHeader fhdr;
gfx::Size coded_size;
std::unique_ptr<DecryptConfig> null_config;
return parser_.ParseNextFrame(&fhdr, &coded_size, &null_config);
}
protected:
Vp9Parser parser_;
private:
scoped_refptr<DecoderBuffer> buffer_;
std::unique_ptr<InMemoryUrlProtocol> protocol_;
std::unique_ptr<FFmpegGlue> glue_;
};
TEST_F(VP9SuperFrameBitstreamFilterTest, Passthrough) {
// This test file has no super frames.
ASSERT_NO_FATAL_FAILURE(LoadTestData("bear-vp9.webm"));
// Run through a few packets for good measure.
VP9SuperFrameBitstreamFilter bsf;
for (int i = 0; i < 16; ++i) {
auto buffer = ReadPacket();
EXPECT_TRUE(buffer->HasOneRef());
// Passthrough buffers should be zero-copy, so a ref should be added.
bsf.EnqueueBuffer(buffer);
EXPECT_FALSE(buffer->HasOneRef());
auto cm_block = bsf.take_buffer();
ASSERT_TRUE(cm_block);
ASSERT_EQ(buffer->data_size(), CMBlockBufferGetDataLength(cm_block));
std::unique_ptr<uint8_t> block_data(new uint8_t[buffer->data_size()]);
ASSERT_EQ(noErr, CMBlockBufferCopyDataBytes(
cm_block, 0, buffer->data_size(), block_data.get()));
// Verify that the block is valid.
parser_.SetStream(block_data.get(), buffer->data_size(), nullptr);
EXPECT_EQ(Vp9Parser::kOk, ParseNextFrame());
EXPECT_EQ(Vp9Parser::kEOStream, ParseNextFrame());
// Releasing the block should bring our ref count back down.
cm_block.reset();
ASSERT_TRUE(buffer->HasOneRef());
}
}
TEST_F(VP9SuperFrameBitstreamFilterTest, Superframe) {
ASSERT_NO_FATAL_FAILURE(LoadTestData("buck-1280x720-vp9.webm"));
VP9SuperFrameBitstreamFilter bsf;
// The first packet in this file is not part of a super frame. We still need
// to send it to the VP9 parser so that the superframe can reference it.
auto buffer = ReadPacket();
parser_.SetStream(buffer->data(), buffer->data_size(), nullptr);
EXPECT_EQ(Vp9Parser::kOk, ParseNextFrame());
bsf.EnqueueBuffer(std::move(buffer));
ASSERT_TRUE(bsf.take_buffer());
// The second and third belong to a super frame.
buffer = ReadPacket();
size_t total_size = buffer->data_size();
bsf.EnqueueBuffer(std::move(buffer));
ASSERT_FALSE(bsf.take_buffer());
buffer = ReadPacket();
total_size += buffer->data_size();
bsf.EnqueueBuffer(std::move(buffer));
auto cm_block = bsf.take_buffer();
ASSERT_TRUE(cm_block);
// Two marker bytes and 2x 16-bit sizes.
const size_t kExpectedTotalSize = 1 + 2 + 2 + 1 + total_size;
EXPECT_EQ(kExpectedTotalSize, CMBlockBufferGetDataLength(cm_block));
std::unique_ptr<uint8_t> block_data(new uint8_t[kExpectedTotalSize]);
ASSERT_EQ(noErr, CMBlockBufferCopyDataBytes(cm_block, 0, kExpectedTotalSize,
block_data.get()));
parser_.SetStream(block_data.get(), kExpectedTotalSize, nullptr);
EXPECT_EQ(Vp9Parser::kOk, ParseNextFrame());
EXPECT_EQ(Vp9Parser::kOk, ParseNextFrame());
EXPECT_EQ(Vp9Parser::kEOStream, ParseNextFrame());
}
TEST_F(VP9SuperFrameBitstreamFilterTest, FlushPassthroughFrame) {
ASSERT_NO_FATAL_FAILURE(LoadTestData("buck-1280x720-vp9.webm"));
VP9SuperFrameBitstreamFilter bsf;
// The first packet in this file is not part of a super frame.
bsf.EnqueueBuffer(ReadPacket());
ASSERT_TRUE(bsf.has_buffers_for_testing());
bsf.Flush();
ASSERT_FALSE(bsf.has_buffers_for_testing());
ASSERT_FALSE(bsf.take_buffer());
}
TEST_F(VP9SuperFrameBitstreamFilterTest, FlushPartialSuperFrame) {
ASSERT_NO_FATAL_FAILURE(LoadTestData("buck-1280x720-vp9.webm"));
VP9SuperFrameBitstreamFilter bsf;
// The first packet in this file is not part of a super frame.
bsf.EnqueueBuffer(ReadPacket());
ASSERT_TRUE(bsf.has_buffers_for_testing());
ASSERT_TRUE(bsf.take_buffer());
// The second and third belong to a super frame.
bsf.EnqueueBuffer(ReadPacket());
ASSERT_FALSE(bsf.take_buffer());
ASSERT_TRUE(bsf.has_buffers_for_testing());
bsf.Flush();
ASSERT_FALSE(bsf.has_buffers_for_testing());
ASSERT_FALSE(bsf.take_buffer());
}
#endif // BUILDFLAG(ENABLE_FFMPEG)
} // namespace media