/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mangui.hls.demux { import flash.utils.ByteArray; CONFIG::LOGGING { import org.mangui.hls.HLSSettings; import org.mangui.hls.utils.Log; } /** Constants and utilities for the H264 video format. **/ public class Nalu { private static var _audNalu : ByteArray; // static initializer { _audNalu = new ByteArray(); _audNalu.length = 2; _audNalu.writeByte(0x09); _audNalu.writeByte(0xF0); }; /** Return an array with NAL delimiter indexes. **/ public static function getNALU(nalu : ByteArray, position : uint) : Vector. { var len : uint = nalu.length,i : uint = position; var unitHeader : int,lastUnitHeader : int = 0; var unitStart : int,lastUnitStart : int = 0; var unitType : int,lastUnitType : int = 0; var audFound : Boolean = false; var value : uint,state : uint = 0; var units : Vector. = new Vector.(); // Loop through data to find NAL startcodes. while (i < len) { // finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01) value = nalu[i++]; switch(state) { case 0: if(!value) { state = 1; // unitHeader is NAL header offset unitHeader=i-1; } break; case 1: if(value) { state = 0; } else { state = 2; } break; case 2: case 3: if(value) { if(value === 1 && i < len) { unitType = nalu[i] & 0x1f; if(unitType == 9) { audFound = true; } if(lastUnitStart) { // use Math.min(4,...) as max header size is 4. // in case there are any leading zeros // such as 00 00 00 00 00 00 01 // ^^ // we need to ignore them as they are part of previous NAL unit units.push(new VideoFrame(Math.min(4,lastUnitStart-lastUnitHeader), i-state-1-lastUnitStart, lastUnitStart, lastUnitType)); } lastUnitStart = i; lastUnitType = unitType; lastUnitHeader = unitHeader; if(audFound == true && (unitType === 1 || unitType === 5)) { // OPTI !!! if AUD unit already parsed and if IDR/NDR unit, consider it is last NALu i = len; } } state = 0; } else { state = 3; } break; default: break; } } //push last unit if(lastUnitStart) { units.push(new VideoFrame(Math.min(4,lastUnitStart-lastUnitHeader), len-lastUnitStart, lastUnitStart, lastUnitType)); } // Reset position and return results. CONFIG::LOGGING { if (HLSSettings.logDebug2) { /** H264 NAL unit names. **/ const NAMES : Array = ['Unspecified',// 0 'NDR', // 1 'Partition A', // 2 'Partition B', // 3 'Partition C', // 4 'IDR', // 5 'SEI', // 6 'SPS', // 7 'PPS', // 8 'AUD', // 9 'End of Sequence', // 10 'End of Stream', // 11 'Filler Data'// 12 ]; if (units.length) { var txt : String = "AVC: "; for (i = 0; i < units.length; i++) { txt += NAMES[units[i].type] + ","; //+ ":" + units[i].length } Log.debug2(txt.substr(0,txt.length-1) + " slices"); } else { Log.debug2('AVC: no NALU slices found'); } } } nalu.position = position; return units; }; public static function get AUD():ByteArray { return _audNalu; } /** * remove Emulation Prevention bytes from a RBSP */ public static function unescapeStream(data : ByteArray):ByteArray { var length : uint = data.length; var EPBPositions : Vector. = new Vector.(); var i : uint = 1; var newLength : uint; var newData : ByteArray; // Find all `Emulation Prevention Bytes` while (i < length - 2) { if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) { EPBPositions.push(i + 2); i += 2; } else { i++; } } // If no Emulation Prevention Bytes were found just return the original // array if (EPBPositions.length === 0) { return data; } // Create a new array to hold the NAL unit data newLength = length - EPBPositions.length; newData = new ByteArray(); newData.length = newLength; var sourceIndex : uint = 0; for (i = 0; i < newLength; sourceIndex++, i++) { if (EPBPositions.length && sourceIndex === EPBPositions[0]) { // Skip this byte sourceIndex++; // Remove this position index EPBPositions.shift(); } newData[i] = data[sourceIndex]; } return newData; } } }