@@ -154,10 +154,16 @@ public void openCodecContext(AVFormatContext avFormatContext) throws IllegalStat
154154 setCodecName (codec .name ().getString ());
155155 setCodecId (codec .id ());
156156
157- if (isInjectingExtradata && codecId == avcodec . AV_CODEC_ID_H264 ) {
157+ if (isInjectingExtradata ) {
158158 BytePointer extra = params .extradata ();
159159 int extraLen = params .extradata_size ();
160- extraData = getAnnexBExtradata (extra , extraLen );
160+ if (codecId == avcodec .AV_CODEC_ID_H264 ) {
161+ extraData = getAnnexBExtradata (extra , extraLen );
162+ } else if (codecId == avcodec .AV_CODEC_ID_HEVC ) {
163+ extraData = getHvccAnnexBExtradata (extra , extraLen );
164+ } else {
165+ extraData = null ;
166+ }
161167 } else {
162168 extraData = null ;
163169 }
@@ -178,7 +184,9 @@ public void processPacket(AVPacket avPacket) {
178184 byte [] dataBuffer = new byte [avPacket .size ()];
179185 avPacket .data ().get (dataBuffer );
180186
181- // Add extradata if the packet has an h264 keyframe
187+ // Prepend cached parameter sets ahead of every keyframe so late-join
188+ // decoders have the VPS/SPS/PPS they need. Applies to both H.264 (SPS/PPS)
189+ // and HEVC (VPS/SPS/PPS) when extradata injection is enabled.
182190 if (extraData != null && (avPacket .flags () & avcodec .AV_PKT_FLAG_KEY ) != 0 ) {
183191 getDataBufferListener ().onDataBuffer (new DataBufferRecord (avPacket .pts () * getStreamTimeBase (), extraData ));
184192 }
@@ -187,8 +195,9 @@ public void processPacket(AVPacket avPacket) {
187195 }
188196
189197 /**
190- * Converts the given extradata from AVCC format to Annex B format.
191- * If the data is already in Annex B format, it is returned directly.
198+ * Converts the given H.264 extradata from AVCC format (AVCDecoderConfigurationRecord,
199+ * ISO/IEC 14496-15 §5.2.4.1) to Annex B format. If the data is already in Annex B format,
200+ * it is returned directly.
192201 *
193202 * @param extradata A BytePointer containing the codec extradata. Must not be null.
194203 * @param size The size of the extradata in bytes.
@@ -235,4 +244,66 @@ private byte[] getAnnexBExtradata(BytePointer extradata, int size) {
235244 }
236245 return out .toByteArray ();
237246 }
247+
248+ /**
249+ * Converts HEVC extradata from HVCC format (HEVCDecoderConfigurationRecord,
250+ * ISO/IEC 14496-15 §8.3.3.1.2) to Annex B format. Walks the {@code numOfArrays}
251+ * table and emits every contained NAL (typically VPS=32, SPS=33, PPS=34) prefixed
252+ * with the 4-byte 0x00000001 start code so that downstream pipelines can decode
253+ * without out-of-band parameter sets.
254+ * <p>
255+ * If the data is already in Annex B format, it is returned directly.
256+ *
257+ * @param extradata A BytePointer containing the codec extradata. Must not be null.
258+ * @param size The size of the extradata in bytes.
259+ * @return A byte array containing the Annex B formatted VPS/SPS/PPS NALs, or
260+ * {@code null} if the data is invalid, too short, or cannot be parsed.
261+ */
262+ private byte [] getHvccAnnexBExtradata (BytePointer extradata , int size ) {
263+ // HVCC has a 22-byte fixed header followed by numOfArrays (1 byte),
264+ // so the minimum useful size is 23 bytes.
265+ if (extradata == null || size < 23 ) return null ;
266+
267+ byte [] data = new byte [size ];
268+ extradata .get (data );
269+
270+ // If already Annex B, pass straight through.
271+ if (data [0 ] == 0x00 && data [1 ] == 0x00 && (data [2 ] == 0x01 || (data [2 ] == 0x00 && data [3 ] == 0x01 ))) {
272+ return data ;
273+ }
274+
275+ // configurationVersion must be 1 per ISO/IEC 14496-15.
276+ if ((data [0 ] & 0xFF ) != 1 ) {
277+ logger .warn ("Unsupported HVCC configurationVersion: {}" , data [0 ] & 0xFF );
278+ return null ;
279+ }
280+
281+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
282+ try {
283+ int pos = 22 ;
284+ int numOfArrays = data [pos ++] & 0xFF ;
285+
286+ for (int i = 0 ; i < numOfArrays ; i ++) {
287+ if (pos + 3 > data .length ) return null ;
288+
289+ pos ++;
290+ int numNalus = ((data [pos ++] & 0xFF ) << 8 ) | (data [pos ++] & 0xFF );
291+
292+ for (int j = 0 ; j < numNalus ; j ++) {
293+ if (pos + 2 > data .length ) return null ;
294+ int nalLen = ((data [pos ++] & 0xFF ) << 8 ) | (data [pos ++] & 0xFF );
295+ if (nalLen <= 0 || pos + nalLen > data .length ) return null ;
296+ out .write (new byte []{0x00 , 0x00 , 0x00 , 0x01 });
297+ out .write (data , pos , nalLen );
298+ pos += nalLen ;
299+ }
300+ }
301+ } catch (Exception e ) {
302+ logger .error ("Error extracting VPS/SPS/PPS from HVCC extradata" , e );
303+ return null ;
304+ }
305+
306+ byte [] result = out .toByteArray ();
307+ return result .length == 0 ? null : result ;
308+ }
238309}
0 commit comments