1 // Copyright (c) 2016 The WebM project authors. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS.  All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 #include "m2ts/webm2pes.h"
9 
10 #include <cstddef>
11 #include <cstdint>
12 #include <cstdio>
13 #include <cstring>
14 #include <limits>
15 #include <string>
16 #include <vector>
17 
18 #include "gtest/gtest.h"
19 
20 #include "common/file_util.h"
21 #include "common/libwebm_util.h"
22 #include "m2ts/vpxpes_parser.h"
23 #include "testing/test_util.h"
24 
25 namespace {
26 
27 class Webm2PesTests : public ::testing::Test {
28  public:
29   // Constants for validating known values from input data.
30   const std::uint8_t kMinVideoStreamId = 0xE0;
31   const std::uint8_t kMaxVideoStreamId = 0xEF;
32   const int kPesHeaderSize = 6;
33   const int kPesOptionalHeaderStartOffset = kPesHeaderSize;
34   const int kPesOptionalHeaderSize = 9;
35   const int kPesOptionalHeaderMarkerValue = 0x2;
36   const int kWebm2PesOptHeaderRemainingSize = 6;
37   const int kBcmvHeaderSize = 10;
38 
39   Webm2PesTests() = default;
40   ~Webm2PesTests() = default;
41 
CreateAndLoadTestInput()42   void CreateAndLoadTestInput() {
43     libwebm::Webm2Pes converter(input_file_name_, temp_file_name_.name());
44     ASSERT_TRUE(converter.ConvertToFile());
45     ASSERT_TRUE(parser_.Open(pes_file_name()));
46   }
47 
VerifyPacketStartCode(const libwebm::VpxPesParser::PesHeader & header)48   bool VerifyPacketStartCode(const libwebm::VpxPesParser::PesHeader& header) {
49     // PES packets all start with the byte sequence 0x0 0x0 0x1.
50     if (header.start_code[0] != 0 || header.start_code[1] != 0 ||
51         header.start_code[2] != 1) {
52       return false;
53     }
54     return true;
55   }
56 
pes_file_name() const57   const std::string& pes_file_name() const { return temp_file_name_.name(); }
parser()58   libwebm::VpxPesParser* parser() { return &parser_; }
59 
60  private:
61   const libwebm::TempFileDeleter temp_file_name_;
62   const std::string input_file_name_ =
63       test::GetTestFilePath("bbb_480p_vp9_opus_1second.webm");
64   libwebm::VpxPesParser parser_;
65 };
66 
TEST_F(Webm2PesTests,CreatePesFile)67 TEST_F(Webm2PesTests, CreatePesFile) { CreateAndLoadTestInput(); }
68 
TEST_F(Webm2PesTests,CanParseFirstPacket)69 TEST_F(Webm2PesTests, CanParseFirstPacket) {
70   CreateAndLoadTestInput();
71   libwebm::VpxPesParser::PesHeader header;
72   libwebm::VideoFrame frame;
73   ASSERT_TRUE(parser()->ParseNextPacket(&header, &frame));
74   EXPECT_TRUE(VerifyPacketStartCode(header));
75 
76   //   9 bytes: PES optional header
77   //  10 bytes: BCMV Header
78   //  83 bytes: frame
79   // 102 bytes total in packet length field:
80   const std::size_t kPesPayloadLength = 102;
81   EXPECT_EQ(kPesPayloadLength, header.packet_length);
82 
83   EXPECT_GE(header.stream_id, kMinVideoStreamId);
84   EXPECT_LE(header.stream_id, kMaxVideoStreamId);
85 
86   // Test PesOptionalHeader values.
87   EXPECT_EQ(kPesOptionalHeaderMarkerValue, header.opt_header.marker);
88   EXPECT_EQ(kWebm2PesOptHeaderRemainingSize, header.opt_header.remaining_size);
89   EXPECT_EQ(0, header.opt_header.scrambling);
90   EXPECT_EQ(0, header.opt_header.priority);
91   EXPECT_EQ(0, header.opt_header.data_alignment);
92   EXPECT_EQ(0, header.opt_header.copyright);
93   EXPECT_EQ(0, header.opt_header.original);
94   EXPECT_EQ(1, header.opt_header.has_pts);
95   EXPECT_EQ(0, header.opt_header.has_dts);
96   EXPECT_EQ(0, header.opt_header.unused_fields);
97 
98   // Test the BCMV header.
99   // Note: The length field of the BCMV header includes its own length.
100   const std::size_t kBcmvBaseLength = 10;
101   const std::size_t kFirstFrameLength = 83;
102   const libwebm::VpxPesParser::BcmvHeader kFirstBcmvHeader(kFirstFrameLength +
103                                                            kBcmvBaseLength);
104   EXPECT_TRUE(header.bcmv_header.Valid());
105   EXPECT_EQ(kFirstBcmvHeader, header.bcmv_header);
106 
107   // Parse the next packet to confirm correct parse and consumption of payload.
108   EXPECT_TRUE(parser()->ParseNextPacket(&header, &frame));
109 }
110 
TEST_F(Webm2PesTests,CanMuxLargeBuffers)111 TEST_F(Webm2PesTests, CanMuxLargeBuffers) {
112   const std::size_t kBufferSize = 100 * 1024;
113   const std::int64_t kFakeTimestamp = libwebm::kNanosecondsPerSecond;
114   libwebm::VideoFrame fake_frame(kFakeTimestamp, libwebm::VideoFrame::kVP9);
115   ASSERT_TRUE(fake_frame.Init(kBufferSize));
116   std::memset(fake_frame.buffer().data.get(), 0x80, kBufferSize);
117   ASSERT_TRUE(fake_frame.SetBufferLength(kBufferSize));
118   libwebm::PacketDataBuffer pes_packet_buffer;
119   ASSERT_TRUE(
120       libwebm::Webm2Pes::WritePesPacket(fake_frame, &pes_packet_buffer));
121 
122   // TODO(tomfinegan): Change VpxPesParser so it can read from a buffer, and get
123   // rid of this extra step.
124   libwebm::FilePtr pes_file(std::fopen(pes_file_name().c_str(), "wb"),
125                             libwebm::FILEDeleter());
126   ASSERT_EQ(pes_packet_buffer.size(),
127             fwrite(&pes_packet_buffer[0], 1, pes_packet_buffer.size(),
128                    pes_file.get()));
129   fclose(pes_file.get());
130   pes_file.release();
131 
132   libwebm::VpxPesParser parser;
133   ASSERT_TRUE(parser.Open(pes_file_name()));
134   libwebm::VpxPesParser::PesHeader header;
135   libwebm::VideoFrame parsed_frame;
136   ASSERT_TRUE(parser.ParseNextPacket(&header, &parsed_frame));
137   EXPECT_EQ(fake_frame.nanosecond_pts(), parsed_frame.nanosecond_pts());
138   EXPECT_EQ(fake_frame.buffer().length, parsed_frame.buffer().length);
139   EXPECT_EQ(0, std::memcmp(fake_frame.buffer().data.get(),
140                            parsed_frame.buffer().data.get(), kBufferSize));
141 }
142 
TEST_F(Webm2PesTests,ParserConsumesAllInput)143 TEST_F(Webm2PesTests, ParserConsumesAllInput) {
144   CreateAndLoadTestInput();
145   libwebm::VpxPesParser::PesHeader header;
146   libwebm::VideoFrame frame;
147   while (parser()->ParseNextPacket(&header, &frame) == true) {
148     EXPECT_TRUE(VerifyPacketStartCode(header));
149   }
150   EXPECT_EQ(0, parser()->BytesAvailable());
151 }
152 
153 }  // namespace
154 
main(int argc,char * argv[])155 int main(int argc, char* argv[]) {
156   ::testing::InitGoogleTest(&argc, argv);
157   return RUN_ALL_TESTS();
158 }
159