1 // Copyright 2020 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 "media/gpu/mac/vp9_super_frame_bitstream_filter.h"
6 
7 #include <CoreMedia/CoreMedia.h>
8 
9 #include "media/base/media.h"
10 #include "media/base/test_data_util.h"
11 #include "media/ffmpeg/ffmpeg_common.h"
12 #include "media/filters/ffmpeg_glue.h"
13 #include "media/filters/in_memory_url_protocol.h"
14 #include "media/filters/vp9_parser.h"
15 #include "media/media_buildflags.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace media {
19 
20 #if BUILDFLAG(ENABLE_FFMPEG)
21 
22 class VP9SuperFrameBitstreamFilterTest : public testing::Test {
23  public:
VP9SuperFrameBitstreamFilterTest()24   VP9SuperFrameBitstreamFilterTest()
25       : parser_(/*parsing_compressed_header=*/false) {
26     InitializeMediaLibrary();
27   }
28 
29   ~VP9SuperFrameBitstreamFilterTest() override = default;
30 
LoadTestData(const char * file_name)31   void LoadTestData(const char* file_name) {
32     buffer_ = ReadTestDataFile(file_name);
33     ASSERT_TRUE(buffer_);
34 
35     // Initialize ffmpeg with the file data.
36     protocol_ = std::make_unique<InMemoryUrlProtocol>(
37         buffer_->data(), buffer_->data_size(), false);
38     glue_ = std::make_unique<FFmpegGlue>(protocol_.get());
39     ASSERT_TRUE(glue_->OpenContext());
40   }
41 
ReadPacket(int stream_index=0)42   scoped_refptr<DecoderBuffer> ReadPacket(int stream_index = 0) {
43     AVPacket packet = {0};
44     while (av_read_frame(glue_->format_context(), &packet) >= 0) {
45       if (packet.stream_index == stream_index) {
46         auto buffer = DecoderBuffer::CopyFrom(packet.data, packet.size);
47         av_packet_unref(&packet);
48         return buffer;
49       }
50       av_packet_unref(&packet);
51     }
52     return nullptr;
53   }
54 
ParseNextFrame()55   Vp9Parser::Result ParseNextFrame() {
56     // Temporaries for the Vp9Parser.
57     Vp9FrameHeader fhdr;
58     gfx::Size coded_size;
59     std::unique_ptr<DecryptConfig> null_config;
60     return parser_.ParseNextFrame(&fhdr, &coded_size, &null_config);
61   }
62 
63  protected:
64   Vp9Parser parser_;
65 
66  private:
67   scoped_refptr<DecoderBuffer> buffer_;
68   std::unique_ptr<InMemoryUrlProtocol> protocol_;
69   std::unique_ptr<FFmpegGlue> glue_;
70 };
71 
TEST_F(VP9SuperFrameBitstreamFilterTest,Passthrough)72 TEST_F(VP9SuperFrameBitstreamFilterTest, Passthrough) {
73   // This test file has no super frames.
74   ASSERT_NO_FATAL_FAILURE(LoadTestData("bear-vp9.webm"));
75 
76   // Run through a few packets for good measure.
77   VP9SuperFrameBitstreamFilter bsf;
78   for (int i = 0; i < 16; ++i) {
79     auto buffer = ReadPacket();
80     EXPECT_TRUE(buffer->HasOneRef());
81 
82     // Passthrough buffers should be zero-copy, so a ref should be added.
83     bsf.EnqueueBuffer(buffer);
84     EXPECT_FALSE(buffer->HasOneRef());
85 
86     auto cm_block = bsf.take_buffer();
87     ASSERT_TRUE(cm_block);
88 
89     ASSERT_EQ(buffer->data_size(), CMBlockBufferGetDataLength(cm_block));
90 
91     std::unique_ptr<uint8_t> block_data(new uint8_t[buffer->data_size()]);
92     ASSERT_EQ(noErr, CMBlockBufferCopyDataBytes(
93                          cm_block, 0, buffer->data_size(), block_data.get()));
94 
95     // Verify that the block is valid.
96     parser_.SetStream(block_data.get(), buffer->data_size(), nullptr);
97     EXPECT_EQ(Vp9Parser::kOk, ParseNextFrame());
98     EXPECT_EQ(Vp9Parser::kEOStream, ParseNextFrame());
99 
100     // Releasing the block should bring our ref count back down.
101     cm_block.reset();
102     ASSERT_TRUE(buffer->HasOneRef());
103   }
104 }
105 
TEST_F(VP9SuperFrameBitstreamFilterTest,Superframe)106 TEST_F(VP9SuperFrameBitstreamFilterTest, Superframe) {
107   ASSERT_NO_FATAL_FAILURE(LoadTestData("buck-1280x720-vp9.webm"));
108 
109   VP9SuperFrameBitstreamFilter bsf;
110 
111   // The first packet in this file is not part of a super frame. We still need
112   // to send it to the VP9 parser so that the superframe can reference it.
113   auto buffer = ReadPacket();
114   parser_.SetStream(buffer->data(), buffer->data_size(), nullptr);
115   EXPECT_EQ(Vp9Parser::kOk, ParseNextFrame());
116   bsf.EnqueueBuffer(std::move(buffer));
117   ASSERT_TRUE(bsf.take_buffer());
118 
119   // The second and third belong to a super frame.
120   buffer = ReadPacket();
121   size_t total_size = buffer->data_size();
122   bsf.EnqueueBuffer(std::move(buffer));
123   ASSERT_FALSE(bsf.take_buffer());
124   buffer = ReadPacket();
125   total_size += buffer->data_size();
126   bsf.EnqueueBuffer(std::move(buffer));
127 
128   auto cm_block = bsf.take_buffer();
129   ASSERT_TRUE(cm_block);
130 
131   // Two marker bytes and 2x 16-bit sizes.
132   const size_t kExpectedTotalSize = 1 + 2 + 2 + 1 + total_size;
133   EXPECT_EQ(kExpectedTotalSize, CMBlockBufferGetDataLength(cm_block));
134 
135   std::unique_ptr<uint8_t> block_data(new uint8_t[kExpectedTotalSize]);
136   ASSERT_EQ(noErr, CMBlockBufferCopyDataBytes(cm_block, 0, kExpectedTotalSize,
137                                               block_data.get()));
138 
139   parser_.SetStream(block_data.get(), kExpectedTotalSize, nullptr);
140   EXPECT_EQ(Vp9Parser::kOk, ParseNextFrame());
141   EXPECT_EQ(Vp9Parser::kOk, ParseNextFrame());
142   EXPECT_EQ(Vp9Parser::kEOStream, ParseNextFrame());
143 }
144 
TEST_F(VP9SuperFrameBitstreamFilterTest,FlushPassthroughFrame)145 TEST_F(VP9SuperFrameBitstreamFilterTest, FlushPassthroughFrame) {
146   ASSERT_NO_FATAL_FAILURE(LoadTestData("buck-1280x720-vp9.webm"));
147 
148   VP9SuperFrameBitstreamFilter bsf;
149 
150   // The first packet in this file is not part of a super frame.
151   bsf.EnqueueBuffer(ReadPacket());
152   ASSERT_TRUE(bsf.has_buffers_for_testing());
153   bsf.Flush();
154   ASSERT_FALSE(bsf.has_buffers_for_testing());
155   ASSERT_FALSE(bsf.take_buffer());
156 }
157 
TEST_F(VP9SuperFrameBitstreamFilterTest,FlushPartialSuperFrame)158 TEST_F(VP9SuperFrameBitstreamFilterTest, FlushPartialSuperFrame) {
159   ASSERT_NO_FATAL_FAILURE(LoadTestData("buck-1280x720-vp9.webm"));
160 
161   VP9SuperFrameBitstreamFilter bsf;
162 
163   // The first packet in this file is not part of a super frame.
164   bsf.EnqueueBuffer(ReadPacket());
165   ASSERT_TRUE(bsf.has_buffers_for_testing());
166   ASSERT_TRUE(bsf.take_buffer());
167 
168   // The second and third belong to a super frame.
169   bsf.EnqueueBuffer(ReadPacket());
170   ASSERT_FALSE(bsf.take_buffer());
171   ASSERT_TRUE(bsf.has_buffers_for_testing());
172 
173   bsf.Flush();
174   ASSERT_FALSE(bsf.has_buffers_for_testing());
175   ASSERT_FALSE(bsf.take_buffer());
176 }
177 
178 #endif  // BUILDFLAG(ENABLE_FFMPEG)
179 
180 }  // namespace media
181