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