HLS(HTTP Live Streaming)で分割されたAACファイルはそのままcatしても正しいファイルにはなりません。
ためしにffmpegでつないでみます。
% ls -l
total 368
-rw-r--r-- 1 hiroki wheel 60377 Feb 9 06:42 1120.aac
-rw-r--r-- 1 hiroki wheel 60174 Feb 9 06:43 1121.aac
-rw-r--r-- 1 hiroki wheel 60704 Feb 9 06:43 1122.aac
-rw-r--r-- 1 hiroki wheel 42 Feb 9 06:55 list
% cat list
file 1120.aac
file 1121.aac
file 1122.aac
% ffmpeg -f concat -safe 0 -i list -c copy output.aac
% ls -l output.aac
-rw-r--r-- 1 hiroki wheel 180796 Feb 9 07:11 output.aac
中身はこんな感じです。
% hd 1120.aac | head
00000000 49 44 33 04 00 00 00 00 00 3f 50 52 49 56 00 00 |ID3......?PRIV..|
00000010 00 35 00 00 63 6f 6d 2e 61 70 70 6c 65 2e 73 74 |.5..com.apple.st|
00000020 72 65 61 6d 69 6e 67 2e 74 72 61 6e 73 70 6f 72 |reaming.transpor|
00000030 74 53 74 72 65 61 6d 54 69 6d 65 73 74 61 6d 70 |tStreamTimestamp|
00000040 00 00 00 00 01 bc 2e ba 1f 49 44 33 04 00 00 00 |.........ID3....|
00000050 00 00 46 50 52 49 56 00 00 00 3c 00 00 63 6f 6d |..FPRIV...<..com|
00000060 2e 65 6c 65 6d 65 6e 74 61 6c 74 65 63 68 6e 6f |.elementaltechno|
00000070 6c 6f 67 69 65 73 2e 74 69 6d 65 73 74 61 6d 70 |logies.timestamp|
00000080 2e 75 74 63 00 32 30 32 33 2d 30 32 2d 30 38 54 |.utc.2023-02-08T|
00000090 30 36 3a 34 32 3a 34 37 5a ff f1 58 80 26 a1 c8 |06:42:47Z..X.&..|
% hd output.aac | head
00000000 ff f1 58 80 26 a1 c8 21 1a 94 ed aa 1d 63 a2 58 |..X.&..!.....c.X|
00000010 90 42 44 08 88 00 34 8d ea a0 71 68 a4 01 74 6a |.BD...4...qh..tj|
00000020 aa 2d 38 00 04 7f d2 2b 1e e4 03 2e 47 64 d8 8b |.-8....+....Gd..|
00000030 c6 d2 73 c7 ed aa 01 90 8f 27 84 2c 7f 0c b9 38 |..s......'.,...8|
00000040 cd e4 78 6f 63 6b 3a ae d5 2a 2a 46 61 47 0d 55 |..xock:..**FaG.U|
00000050 d6 05 95 44 92 48 44 95 17 6a 84 9d 36 db 5c cd |...D.HD..j..6.\.|
00000060 96 1f cd 8f 74 8f 65 67 aa bb 91 73 a4 c7 b0 a7 |....t.eg...s....|
00000070 41 38 89 de 84 bc 43 89 a8 4d ef f4 8f 0c b8 f6 |A8....C..M......|
00000080 3c 4f d3 e5 70 5f be ce b3 79 c5 6d 70 73 67 d8 |<O..p_...y.mpsg.|
00000090 e7 40 7a cd 4e 1f 38 d6 7c 6a f2 27 07 c6 f2 b7 |.@z.N.8.|j.'....|
ID3タグx2(0x99バイト)が消されてますね。上記の三つのファイルサイズから0x99バイト引いて足した値が、output.aacのサイズに合致します。output.aacは切れ目なくちゃんと再生できました。
mrubyで処理してみました。
url = "https://radio-stream.nhk.jp/hls/live/2023546/nhkradiruikfm/master48k.m3u8"
h = HttpRequest.new()
index = h.get(url)
ri = url.rindex("/")
base = url[0, ri]
ar = Array.new
index.body.each_line {|line|
if line[0] != "#"
l = line.chomp
if !ar.include?(l)
ar << l
end
end
}
file = File.new('sample.aac', 'w')
ar.each{|var|
a = h.get(base + "/" + var)
t1 = a.body[9,1].ord
t2 = a.body[t1+10+9,1].ord
off = t1 + 10 + t2 + 10
len = a.body.size - off
file.write(a.body[off, len])
}
file.close
256バイト以下のID3タグが2つ入っていることを前提にした手抜きコードです。良い子はまねしないでね。
当初ffmpegのソースを参考にしようかと思ったのですが、この手の調査は実際動かして確認するのが手っ取り早いです。
ライブ配信のm3u8は随時更新されてリストにAACファイルが追加されますが、聞き逃しサービスのm3u8は完全な一覧が返ってくるので、上記のスクリプトで完全なAACファイルを作ることができます。
聞き逃しのm3u8のURLはFirefoxのpluginのVideo DownloadHelperなどで確認できるようです。
FreeBSD/mipsなRT3883で上記のスクリプト実行するとけっこう負荷高いです。はて、
# vmstat -c 100
procs memory page disks faults cpu
r b w avm fre flt re pi po fr sr fl0 md0 in sy cs us sy id
0 0 0 50M 38M 43 0 4 0 45 2 0 0 116 203 322 2 14 85
0 0 0 50M 38M 1 0 0 0 0 3 0 0 40 59 134 0 0 100
0 0 0 50M 38M 0 0 0 0 0 2 0 0 46 59 139 1 0 99
0 0 0 50M 38M 0 0 0 0 0 3 0 0 36 59 113 1 0 99
0 0 0 50M 38M 0 0 0 0 0 3 0 0 40 59 130 0 0 100
0 0 0 50M 38M 0 0 0 0 0 2 0 0 35 59 121 0 0 100
1 0 0 56M 38M 139 0 0 0 18 3 0 0 50 206 135 5 1 94
1 0 0 56M 37M 151 0 0 0 0 3 0 4 299 423 529 68 3 29
1 0 0 57M 37M 124 0 1 0 6 4 1 1 376 321 639 73 9 18
1 0 0 57M 36M 142 0 0 0 0 3 0 1 423 445 846 62 10 27
1 0 0 58M 35M 94 0 0 0 0 3 0 1 346 325 614 74 5 21
0 0 0 50M 38M 111 0 0 0 707 3 0 1 282 288 592 38 7 55
0 0 0 50M 38M 0 0 0 0 0 3 0 0 36 59 116 0 0 100
0 0 0 50M 38M 0 0 0 0 0 2 0 0 38 59 129 0 1 99
0 0 0 50M 38M 0 0 0 0 0 3 0 0 37 59 112 1 0 99A
0 0 0 50M 38M 0 0 0 0 0 3 0 0 35 59 108 0 0 100
聞き逃しのAACファイルはフォーマットが違うみたいです。
aacファイルはADTS形式とM4A形式があるようです。