<?php

/*

PRIV frame type

should contain:

   The ID3 PRIV owner identifier MUST be
   "com.apple.streaming.transportStreamTimestamp".  The ID3 payload MUST
   be a 33-bit MPEG-2 Program Elementary Stream timestamp expressed as a
   big-endian eight-octet number, with the upper 31 bits set to zero.
   Clients SHOULD NOT play Packed Audio Segments without this ID3 tag.

https://id3.org/id3v2.4.0-frames
https://id3.org/id3v2.4.0-structure

bit order is MSB first, big-endian

header 10 bytes
extended header (var, optional)
frames (variable)
pading (variable, optional)
footer (10 bytes, optional)


header:
    "ID3"
    version: 16 bits $04 00
    flags: 32 bits
    idv2 size: 32 bits (in chunks of 4 bytes, not counting header or footer)

flags:
    bit 7 - unsyncrhonization (??)
    bit 6 - extended header
    bit 5 - experimental indicator
    bit 4 - footer present

frame:
    id - 32 bits (four chars)
    size - 32 bits (in chunks of 4 bytes, excluding frame header)
    flags - 16 bits
    (frame data)

priv payload:
    owner text string followed by \x00
    (binary data)

The timestamps... I think... have 90 kHz integer resolution
so convert from the decimal seconds in the HLS

*/

function hexdump($str) {
    $len = strlen( $str );
    return unpack("H*", $str)[1];
}

const KHZ_90 = 90000;
const MHZ_27 = 27000000;

function process_mp3( $filename, $pts ) {
    $owner = "com.apple.streaming.transportStreamTimestamp\x00";
    $timestamp = $pts * KHZ_90;
    $timestamp_high = 0;
    $timestamp_low = intval( $timestamp ); // assume they won't get too big for 31 bits
    
    $frame_data = pack(
        'a*NN',
        $owner,
        $timestamp_high,
        $timestamp_low,
    );

    $frame_type = 'PRIV';
    $frame_flags = 0;
    $frame_length = strlen( $frame_data ); // if >127 bytes may need to adjust
    $frame = pack(
        'a4Nna*',
        $frame_type,
        $frame_length,
        $frame_flags,
        $frame_data
    );

    $tag_type = 'ID3';
    $tag_version = 0x0400;
    $tag_flags = 0;
    $tag_length = strlen( $frame ); // if >127 bytes may need to adjust
    $tag = pack(
        'a3nCNa*',
        $tag_type,
        $tag_version,
        $tag_flags,
        $tag_length,
        $frame
    );

    $hex = hexdump($tag);
    print "$filename $pts $hex\n";

    $data = file_get_contents( $filename );
    if ( substr( $data, 0, 3 ) == 'ID3' ) {
        echo "SKIPPING already has ID3\n";
    } else {
        echo "ADDING ID3\n";
        file_put_contents( $filename, "$tag$data" );
    }
}

$playlist = "caminandes-llamigos.webm.audio.mp3.m3u8";
$lines = file( $playlist, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES );

$pts = 0.0;
$duration = 0.0;
foreach ( $lines as $line ) {

    /*
    #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-ALLOW-CACHE:YES
    #EXT-X-TARGETDURATION:11
    #EXTINF:10.005397,
    caminandes-llamigos.webm.audio.mp3.0000.mp3
    #EXTINF:10.004898,
    caminandes-llamigos.webm.audio.mp3.0001.mp3
    ...
    */
    $matches = null;
    if ( preg_match( '/^#EXTINF:\s*(\d+(?:\.\d+)?),/', $line, $matches ) ) {
        $duration = floatval( $matches[1] );
        continue;
    } else if (preg_match( '/^#/', $line ) ) {
        continue;
    }
    $filename = $line;
    process_mp3( $filename, $pts );
    $pts += $duration;
    $duration = 0;
}