package com.foxx.androidcast.media;

/*********************************************************************
 * H264Bitstream.java
 * Created at: Fri 15 May 2026 16:49:02 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * class H264Bitstream
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (3 commits, 66 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	b09701f961146fa6938b2e4b6c55942356bf385127ca222b5473f247e86e462d
**********************************************************************/
import java.io.ByteArrayOutputStream;

/**
 * Converts H.264 / HEVC AVCC (length-prefixed) NAL units to Annex-B start codes for decoders.
 * MediaCodec encoders on Android typically emit AVCC; many decoders expect Annex-B in input buffers.
 */
public final class H264Bitstream {
    private static final byte[] START_CODE = {0, 0, 0, 1};

    private H264Bitstream() {}

    public static byte[] toAnnexB(byte[] data) {
        if (data == null || data.length < 4) {
            return data;
        }
        if (startsWithAnnexB(data)) {
            return data;
        }
        if (!looksLikeAvcc(data)) {
            return data;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream(data.length + 16);
        int offset = 0;
        while (offset + 4 <= data.length) {
            int length = readLength(data, offset);
            if (length <= 0 || offset + 4 + length > data.length) {
                break;
            }
            out.write(START_CODE, 0, START_CODE.length);
            out.write(data, offset + 4, length);
            offset += 4 + length;
        }
        if (out.size() == 0) {
            return data;
        }
        return out.toByteArray();
    }

    private static boolean startsWithAnnexB(byte[] data) {
        return data.length >= 4
                && data[0] == 0
                && data[1] == 0
                && (data[2] == 1 || (data[2] == 0 && data[3] == 1));
    }

    private static boolean looksLikeAvcc(byte[] data) {
        int length = readLength(data, 0);
        return length > 0 && length < data.length;
    }

    private static int readLength(byte[] data, int offset) {
        return ((data[offset] & 0xFF) << 24)
                | ((data[offset + 1] & 0xFF) << 16)
                | ((data[offset + 2] & 0xFF) << 8)
                | (data[offset + 3] & 0xFF);
    }
}
