1 // Copyright 2019 The Chromium OS 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 // #define LOG_NDEBUG 0
6 #define LOG_TAG "EncodedDataHelper"
7
8 #include "encoded_data_helper.h"
9
10 #include <assert.h>
11 #include <string.h>
12
13 #include <utility>
14
15 #include <log/log.h>
16
17 namespace android {
18
19 namespace {
20
IsAnnexb3ByteStartCode(const std::string & data,size_t pos)21 bool IsAnnexb3ByteStartCode(const std::string& data, size_t pos) {
22 // The Annex-B 3-byte start code "\0\0\1" will be prefixed by NALUs per AU
23 // except for the first one.
24 return data[pos] == 0 && data[pos + 1] == 0 && data[pos + 2] == 1;
25 }
26
IsAnnexb4ByteStartCode(const std::string & data,size_t pos)27 bool IsAnnexb4ByteStartCode(const std::string& data, size_t pos) {
28 // The Annex-B 4-byte start code "\0\0\0\1" will be prefixed by first NALU per
29 // AU.
30 return data[pos] == 0 && data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 1;
31 }
32
33 // Get the next position of NALU header byte in |data| from |next_header_pos|,
34 // and update to |next_header_pos|. Return true if there is one; false
35 // otherwise.
36 // Note: this function should be used within an AU.
GetPosForNextNALUHeader(const std::string & data,size_t * next_header_pos)37 bool GetPosForNextNALUHeader(const std::string& data, size_t* next_header_pos) {
38 size_t pos = *next_header_pos;
39
40 // Annex-B 4-byte could be also found by IsAnnexb3ByteStartCode().
41 while (pos + 3 <= data.size() && !IsAnnexb3ByteStartCode(data, pos)) {
42 ++pos;
43 }
44 if (pos + 3 >= data.size()) return false; // No more NALUs
45
46 // NALU header is the first byte after Annex-B start code.
47 *next_header_pos = pos + 3;
48 return true;
49 }
50
51 // For H264, return data bytes of next AU fragment in |data| from |next_pos|,
52 // and update the position to |next_pos|.
GetBytesForNextAU(const std::string & data,size_t * next_pos)53 std::string GetBytesForNextAU(const std::string& data, size_t* next_pos) {
54 // Helpful description:
55 // https://en.wikipedia.org/wiki/Network_Abstraction_Layer
56 size_t start_pos = *next_pos;
57 size_t pos = start_pos;
58 if (pos + 4 > data.size()) {
59 ALOGE("Invalid AU: Start code is less than 4 bytes.\n");
60 *next_pos = data.size();
61 return std::string();
62 }
63
64 assert(IsAnnexb4ByteStartCode(data, pos));
65
66 pos += 4;
67 // The first 4 bytes must be Annex-B 4-byte start code for an AU.
68 while (pos + 4 <= data.size() && !IsAnnexb4ByteStartCode(data, pos)) {
69 ++pos;
70 }
71 if (pos + 3 >= data.size()) pos = data.size();
72
73 // Update next_pos.
74 *next_pos = pos;
75 return data.substr(start_pos, pos - start_pos);
76 }
77
78 // For VP8/9, return data bytes of next frame in |data| from |next_pos|, and
79 // update the position to |next_pos|.
GetBytesForNextFrame(const std::string & data,size_t * next_pos)80 std::string GetBytesForNextFrame(const std::string& data, size_t* next_pos) {
81 // Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
82 size_t pos = *next_pos;
83 std::string bytes;
84 if (pos == 0) pos = 32; // Skip IVF header.
85
86 const uint32_t frame_size = *reinterpret_cast<const uint32_t*>(&data[pos]);
87 pos += 12; // Skip frame header.
88
89 // Update next_pos.
90 *next_pos = pos + frame_size;
91 return data.substr(pos, frame_size);
92 }
93
94 } // namespace
95
EncodedDataHelper(const std::string & file_path,VideoCodecType type)96 EncodedDataHelper::EncodedDataHelper(const std::string& file_path, VideoCodecType type)
97 : type_(type) {
98 CachedInputFileStream input(file_path);
99 if (!input.IsValid()) {
100 ALOGE("Failed to open file: %s", file_path.c_str());
101 return;
102 }
103
104 int file_size = input.GetLength();
105 if (file_size <= 0) {
106 ALOGE("Stream byte size (=%d) is invalid", file_size);
107 return;
108 }
109 input.Rewind();
110
111 char* read_bytes = new char[file_size];
112 if (input.Read(read_bytes, file_size) != file_size) {
113 ALOGE("Failed to read input stream from file to buffer.");
114 return;
115 }
116
117 // Note: must assign |file_size| here otherwise the constructor will terminate
118 // copting at the first '\0' in |read_bytes|.
119 std::string data(read_bytes, file_size);
120 delete[] read_bytes;
121
122 SliceToFragments(data);
123 }
124
~EncodedDataHelper()125 EncodedDataHelper::~EncodedDataHelper() {}
126
GetNextFragment()127 const EncodedDataHelper::Fragment* const EncodedDataHelper::GetNextFragment() {
128 if (ReachEndOfStream()) return nullptr;
129 return next_fragment_iter_++->get();
130 }
131
AtHeadOfStream() const132 bool EncodedDataHelper::AtHeadOfStream() const {
133 return next_fragment_iter_ == fragments_.begin();
134 }
135
ReachEndOfStream() const136 bool EncodedDataHelper::ReachEndOfStream() const {
137 return next_fragment_iter_ == fragments_.end();
138 }
139
SliceToFragments(const std::string & data)140 void EncodedDataHelper::SliceToFragments(const std::string& data) {
141 size_t next_pos = 0;
142 bool seen_csd = false;
143 while (next_pos < data.size()) {
144 std::unique_ptr<Fragment> fragment(new Fragment());
145 switch (type_) {
146 case VideoCodecType::H264:
147 fragment->data = GetBytesForNextAU(data, &next_pos);
148 if (!ParseAUFragmentType(fragment.get())) continue;
149 if (!seen_csd && !fragment->csd_flag)
150 // Skip all AUs beforehand until we get SPS NALU.
151 continue;
152 seen_csd = true;
153 break;
154 case VideoCodecType::VP8:
155 case VideoCodecType::VP9:
156 fragment->data = GetBytesForNextFrame(data, &next_pos);
157 break;
158 default:
159 ALOGE("Unknown video codec type.");
160 return;
161 }
162 fragments_.push_back(std::move(fragment));
163 }
164
165 ALOGD("Total %zu fragments in interest from input stream.", NumValidFragments());
166 next_fragment_iter_ = fragments_.begin();
167 }
168
ParseAUFragmentType(Fragment * fragment)169 bool EncodedDataHelper::ParseAUFragmentType(Fragment* fragment) {
170 size_t next_header_pos = 0;
171 while (GetPosForNextNALUHeader(fragment->data, &next_header_pos)) {
172 // Read the NALU header (first byte) which contains unit type.
173 uint8_t nalu_header = static_cast<uint8_t>(fragment->data[next_header_pos]);
174
175 // Check forbidden_zero_bit (MSB of NALU header) is 0;
176 if (nalu_header & 0x80) {
177 ALOGE("NALU header forbidden_zero_bit is 1.");
178 return false;
179 }
180
181 // Check NALU type ([3:7], 5-bit).
182 uint8_t nalu_type = nalu_header & 0x1f;
183 switch (nalu_type) {
184 case NON_IDR_SLICE:
185 case IDR_SLICE:
186 // If AU contains both CSD and VCL NALUs (e.g. PPS + IDR_SLICE), don't
187 // raise csd_flag, treat this fragment as VCL one.
188 fragment->csd_flag = false;
189 return true; // fragment in interest as VCL.
190 case SPS:
191 case PPS:
192 fragment->csd_flag = true;
193 // Continue on finding the subsequent NALUs, it may have VCL data.
194 break;
195 default:
196 // Skip uninterested NALU type.
197 break;
198 }
199 }
200 return fragment->csd_flag; // fragment in interest as CSD.
201 }
202
203 } // namespace android
204