• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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