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 ); return "$tag$data"; } $playlist = "caminandes-llamigos.webm.audio.mp3.m3u8"; $playlist_out = "caminandes-llamigos.webm.audio.mp3.combined.m3u8"; $outfile = "caminandes-llamigos.webm.audio.mp3"; $lines = file( $playlist, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES ); $pts = 0.0; $duration = 0.0; $lines_out = []; $chunks = []; $offset = 0; foreach ( $lines as $line ) { // todo: create a single-file version // and rewrite the manifest /* #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 ... for output: #EXT-X-BYTERANGE:132872@730 */ $matches = null; if ( preg_match( '/^#EXTINF:\s*(\d+(?:\.\d+)?),/', $line, $matches ) ) { $duration = floatval( $matches[1] ); } if ( preg_match( '/^#EXT-X-VERSION:(.*)/', $line, $matches ) ) { if ( intval( $matches[1] ) < 4 ) { $line = "#EXT-X-VERSION:7"; } } if (preg_match( '/^#/', $line ) ) { $lines_out[] = $line; continue; } $filename = $line; $chunk = process_mp3( $filename, $pts ); $len = strlen( $chunk ); $lines_out[] = "#EXT-X-BYTERANGE:$len@$offset"; $lines_out[] = "$outfile"; $chunks[] = $chunk; $offset += $len; $pts += $duration; $duration = 0; } file_put_contents( $outfile, implode( '', $chunks ) ); file_put_contents( $playlist_out, implode( "\n", $lines_out ) );