1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.media.samplevideoencoder;
18 
19 import android.util.Log;
20 
21 import java.nio.ByteBuffer;
22 
23 import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_B;
24 import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_I;
25 import static com.android.media.samplevideoencoder.MainActivity.FRAME_TYPE_P;
26 
27 public class NalUnitUtil {
28     private static final String TAG = MediaCodecSurfaceEncoder.class.getSimpleName();
29     private static final boolean DEBUG = false;
30 
findNalUnit(byte[] dataArray, int pos, int limit)31     public static int findNalUnit(byte[] dataArray, int pos, int limit) {
32         int startOffset = 0;
33         if (limit - pos < 4) {
34             return startOffset;
35         }
36         if (dataArray[pos] == 0 && dataArray[pos + 1] == 0 && dataArray[pos + 2] == 1) {
37             startOffset = 3;
38         } else {
39             if (dataArray[pos] == 0 && dataArray[pos + 1] == 0 && dataArray[pos + 2] == 0 &&
40                     dataArray[pos + 3] == 1) {
41                 startOffset = 4;
42             }
43         }
44         return startOffset;
45     }
46 
getAVCNalUnitType(byte[] dataArray, int nalUnitOffset)47     private static int getAVCNalUnitType(byte[] dataArray, int nalUnitOffset) {
48         return dataArray[nalUnitOffset] & 0x1F;
49     }
50 
parseAVCNALUnitData(byte[] dataArray, int offset, int limit)51     private static int parseAVCNALUnitData(byte[] dataArray, int offset, int limit) {
52         ParsableBitArray bitArray = new ParsableBitArray(dataArray);
53         bitArray.reset(dataArray, offset, limit);
54 
55         bitArray.skipBit(); // forbidden_zero_bit
56         bitArray.readBits(2); // nal_ref_idc
57         bitArray.skipBits(5); // nal_unit_type
58 
59         bitArray.readUEV(); // first_mb_in_slice
60         if (!bitArray.canReadUEV()) {
61             return -1;
62         }
63         int sliceType = bitArray.readUEV();
64         if (DEBUG) Log.d(TAG, "slice_type = " + sliceType);
65         if (sliceType == 0) {
66             return FRAME_TYPE_P;
67         } else if (sliceType == 1) {
68             return FRAME_TYPE_B;
69         } else if (sliceType == 2) {
70             return FRAME_TYPE_I;
71         } else {
72             return -1;
73         }
74     }
75 
getHEVCNalUnitType(byte[] dataArray, int nalUnitOffset)76     private static int getHEVCNalUnitType(byte[] dataArray, int nalUnitOffset) {
77         return (dataArray[nalUnitOffset] & 0x7E) >> 1;
78     }
79 
parseHEVCNALUnitData(byte[] dataArray, int offset, int limit, int nalUnitType)80     private static int parseHEVCNALUnitData(byte[] dataArray, int offset, int limit,
81                                             int nalUnitType) {
82         // nal_unit_type values from H.265/HEVC Table 7-1.
83         final int BLA_W_LP = 16;
84         final int RSV_IRAP_VCL23 = 23;
85 
86         ParsableBitArray bitArray = new ParsableBitArray(dataArray);
87         bitArray.reset(dataArray, offset, limit);
88 
89         bitArray.skipBit(); // forbidden zero bit
90         bitArray.readBits(6); // nal_unit_header
91         bitArray.readBits(6); // nuh_layer_id
92         bitArray.readBits(3); // nuh_temporal_id_plus1
93 
94         // Parsing slice_segment_header values from H.265/HEVC Table 7.3.6.1
95         boolean first_slice_segment = bitArray.readBit(); // first_slice_segment_in_pic_flag
96         if (!first_slice_segment) return -1;
97         if (nalUnitType >= BLA_W_LP && nalUnitType <= RSV_IRAP_VCL23) {
98             bitArray.readBit();  // no_output_of_prior_pics_flag
99         }
100         bitArray.readUEV(); // slice_pic_parameter_set_id
101         // Assume num_extra_slice_header_bits element of PPS data to be 0
102         int sliceType = bitArray.readUEV();
103         if (DEBUG) Log.d(TAG, "slice_type = " + sliceType);
104         if (sliceType == 0) {
105             return FRAME_TYPE_B;
106         } else if (sliceType == 1) {
107             return FRAME_TYPE_P;
108         } else if (sliceType == 2) {
109             return FRAME_TYPE_I;
110         } else {
111             return -1;
112         }
113     }
114 
getStandardizedFrameTypesFromAVC(ByteBuffer buf)115     public static int getStandardizedFrameTypesFromAVC(ByteBuffer buf) {
116         int limit = buf.limit();
117         byte[] dataArray = new byte[buf.remaining()];
118         buf.get(dataArray);
119         int frameType = -1;
120         for (int pos = 0; pos + 3 < limit; ) {
121             int startOffset = NalUnitUtil.findNalUnit(dataArray, pos, limit);
122             if (startOffset != 0) {
123                 int nalUnitType = getAVCNalUnitType(dataArray, (pos + startOffset));
124                 if (DEBUG) {
125                     Log.d(TAG, "NalUnitOffset = " + (pos + startOffset));
126                     Log.d(TAG, "NalUnitType = " + nalUnitType);
127                 }
128                 // SLICE_NAL = 1; IDR_SLICE_NAL = 5
129                 if (nalUnitType == 1 || nalUnitType == 5) {
130                     frameType = parseAVCNALUnitData(dataArray, (pos + startOffset),
131                             (limit - pos - startOffset));
132                     break;
133                 }
134                 pos += 3;
135             } else {
136                 pos++;
137             }
138         }
139         return frameType;
140     }
141 
getStandardizedFrameTypesFromHEVC(ByteBuffer buf)142     public static int getStandardizedFrameTypesFromHEVC(ByteBuffer buf) {
143         int limit = buf.limit();
144         byte[] dataArray = new byte[buf.remaining()];
145         buf.get(dataArray);
146         int frameType = -1;
147         for (int pos = 0; pos + 3 < limit; ) {
148             int startOffset = NalUnitUtil.findNalUnit(dataArray, pos, limit);
149             if (startOffset != 0) {
150                 int nalUnitType = NalUnitUtil.getHEVCNalUnitType(dataArray, (pos + startOffset));
151                 if (DEBUG) {
152                     Log.d(TAG, "NalUnitOffset = " + (pos + startOffset));
153                     Log.d(TAG, "NalUnitType = " + nalUnitType);
154                 }
155                 // Parse NALUnits containing slice_headers which lies in the range of 0 to 21
156                 if (nalUnitType >= 0 && nalUnitType <= 21) {
157                     frameType = parseHEVCNALUnitData(dataArray, (pos + startOffset),
158                             (limit - pos - startOffset), nalUnitType);
159                     break;
160                 }
161                 pos += 3;
162             } else {
163                 pos++;
164             }
165         }
166         return frameType;
167     }
168 }
169