mp4からaacを抜き出して編集する方法

ラジコの録音に失敗するとなんとか保管したくなります。
そうするとラジオ音声がMP4でしか手に入らないです。
そのままMP4で聞いてもいいのですが、映像分が無駄なファイルなので、音声だけ抜き出したくなります。
ところが今のMP4に含まれる音声は高圧縮なAACだったりして古のMP3を抜き出すツールはうまく動作しません。
まったくできないこともないのですが、音質の低いMP3になってしまい、かつ、ファイルサイズがもとより大きくなったりします。
というわけで、私がうまく行った方法を書いておこうと思います。単なるメモです。

まずは聞いてみる。そしてカット

手に入れたMP4はお尻や先頭に無駄な部分(CM)がそのままになっている場合があります。
なので、せめてその部分を除去します。
今は無料動画編集ソフトもなかなかいいのがないのでここで困ってしまうのですが、少し古い簡単なツールが使えました。

UniteMovie

これはもともとUnite(結合)するためのツールなのですが、おまけでカットするツールが付属しています。cutmovie.exeというのがそうです。

一度ムービープレイヤーで再生して切り取りたい時間をメモしておかないといけません。

例えば元が2時間10分ではじめの5分、最後の5分をカットしたい場合は

0時間5分 ? 2時間5分

みたいに設定します。
そうすると「切り取り」で2時間きっかりのMP4が出力されます。
ただし何故かエクスプローラ上での動画時間が変わらないことがあります。でも再生するとわかるのですが、ちゃんとカットされていました。もしかしたら駄目なケースもあるかも・・・?

 

音声だけ抜き出す(FFMPEG)

音声だけのファイルにするためにFFMPEGを使います。コマンドラインのツールです。

そのまま抜き出すだけで良ければこのようにします。

ffmpeg.exe -i test.mp4 -acodec copy test.aac

しかしやってみるとわかるのですが、いつもラジコで録音しているのに比べてファイルサイズが大きくなってしまいます。

そこで私は48kでエンコードをしています。このくらいなら音質もそれほど落ちないと思います。

ffmpeg.exe -i test.mp4 -acodec aac -ab 48k test_48k.aac

 

こうしてaacとして抜き出すと、今度はちゃんとエクスプローラーで時間が正確に認識されたともおいます。
どうしてもおかしい場合には一度Wavかmp3に変換しないと駄目かもしれません・・・。

ただ、これをやってしまうと、どうしてもファイルサイズが小さくならなかったです。
聞き苦しくならないようにファイルサイズを節約するというのも結構難しいものですね。

 

ラジコの録音、東京の放送を取得する一案(9 WEBアプリ

WEBアプリはPerlで簡単に組みました。
単に、mp3フォルダの中身をテーブル組にするだけのものです。
やってみるとわかりますが、ラジオって毎日だったりするのでどんどん溜まっていきます 汗
その為、デリートボタンを各録音ファイルの横に表示するようにしています。
クリックするとunlink(削除)します。削除をCGIから実行するのは結構危険なので、dustとか言うフォルダを作っておいて単に移動させるようにしても良いかもしれません。
実際の削除は時々ログインして自分で消すようにするのです。

#!/usr/bin/perl
use strict;
use CGI;
use Storable qw(nstore retrieve);

my $debug;

my $q=new CGI;
my @param=$q->param;
my %HASH;
for (@param){
$HASH{$_}=$q->param($_);
    $debug.=qq($_ , $HASH{$_} | );
}

my $mes;
if ( -f "./mp3/" . $HASH{'del'} ){
    unlink "./mp3/$HASH{'del'}";
    $mes=qq(./mp3/$HASH{'del'} を削除しました。);
}elsif( $HASH{'del'} ne "" ){
    $mes=qq($HASH{'del'} がありません。);
}

opendir DIR, "mp3";
my @files =
reverse( grep ( /mp3|aac/, readdir DIR ));
close DIR;
#フィルタ
if ( $HASH{'a'} ne "" ){
    @files = grep /$HASH{'a'}/i , @files;
}
my $src;
for my $var (@files){
    $src.=qq(<tr><td><a href="./mp3/$var">$var</a></td><td><button onclick='test("$var")' style="WIDTH:50px; HEIGHT: 20px">del</button></td></tr>\n);
}

if ( -d "debug.flg" ){
    $debug=$debug . "\n<hr>\n";
}else{
    $debug="";
}
print "Content-type: text/html\n\n";
print <<HTML;
<html lang="ja">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <title></title>
<script type="text/javascript">
<!--
function test(msg){
  var element = document.getElementById("1");
  element.value = msg;
}
function ReloadAddr(){
  window.location.reload(); //ページをリロード
}
-->
</script>
</head>
<body>$debug
$mes
<form action="rj.cgi" method="post">
  <input type="text" name="del" size="30" id="1"><input type="submit" value="削除">
  <button onclick='ReloadAddr' style="WIDTH:50px; HEIGHT: 20px">reload</button>
  <a href="#endjump">一番下へ</a>
</form>
<table border="1" cellpadding="0">
$src
</table>
<form action="rj.cgi" method="get">
  <input type="text" name="a" size="30" value=$HASH{'a'}>
  <input type="submit" value="フィルタ"><a name="endjump"><a href="#top">TOP.</a></a>
  <input type="reset" value="riset">
</form>
</body>
</html>
HTML

ラジコの録音、東京の放送を取得する一案(8 録音スクリプト

サーバー接続してトークンを取得して録音するスクリプトを作ります。

大部分はネットから拾ってきたコードです。色々手を入れています。
とくに「get torken from server」の部分です。
ここで、クライアントを使って、サーバーにトークンを取りに行っています。

クライアントはこちら↓
サーバークライアントを試す

あと一部「mp3」と書いてありますが、aacのママとしています。
この方が音質もよくファイルサイズも小さいからです。これは知った時驚きました。
ファイルサイズは約3分の1で、しかもエンコードする時間も不要です。

また出力先を別で指定しているのは、
webアプリで簡単に視聴できるようにするためです。

#!/bin/sh

# TBS:TBSラジオ
# QRR:文化放送
# LFR:ニッポン放送
# NSB:ラジオNIKKEI
# INT:INTER FM
# FMT:TOKYO FM
# FMJ:J-WAVE
# JORF:ラジオ日本
# BAYFM78:bayfm78
# NACK5:NACK5
# YFM:FMヨコハマ

if [ $# -eq 3 ]; then
  OUTFILEPREFIX=$1
  RECTIMEMIN=$2
  CHANNEL=$3
else
  echo "usage : $0 OUTFILEPREFIX RECTIMEMIN CHANNEL"
  exit 1
fi

OUTFILEPREFIX=$1
RECTIMEMIN=$2
CHANNEL=$3

RTMPDUMP=/usr/local/bin/rtmpdump
FFMPEG=/usr/bin/ffmpeg
MP3PATH=[mp3を吐き出したいディレクトリ]

OUTFILEBASEPATH=[処理を実行するディレクトリ]
OUTFILENAME=${OUTFILEBASEPATH}/${OUTFILEPREFIX}_`date +%Y-%m-%d`
FLVFILEEXT=".flv"
AACFILEEXT=".aac"
MP3FILEEXT=".mp3"

MARGINTIMEMIN=1
RECTIME=`expr ${RECTIMEMIN} \* 60 + ${MARGINTIMEMIN} \* 2 \* 30`

# 30sec wait
echo "===== `date` Wait 30sec. ====="
for f1 in 2 1 0
do
  for f2 in 9 8 7 6 5 4 3 2 1 0
  do
    echo -n "$f1$f2\r"
    sleep 1
  done
done
echo `date` Start.

#開始時刻
START=`date +%s` 

cd ${OUTFILEBASEPATH}

playerurl=http://radiko.jp/player/swf/player_3.0.0.01.swf
playerfile=./player.swf
keyfile=./authkey.png

#
# get player
#
if [ ! -f $playerfile ]; then
  wget -q -O $playerfile $playerurl

  if [ $? -ne 0 ]; then
    echo "failed get player"
    exit 1
  fi
fi

#
# get keydata (need swftools)
#
if [ ! -f $keyfile ]; then
  swfextract -b 14 $playerfile -o $keyfile

  if [ ! -f $keyfile ]; then
    echo "failed get keydata"
    exit 1
  fi
fi

#
# get torken from server
#
CRIENT=`./client.pl $CHANNEL`
echo $CRIENT
authtoken=`echo $CRIENT| cut -d ' ' -f2`

#
# rtmpdump
#
RETRYCOUNT=0
while :
do
  ${RTMPDUMP} -v \
              -r "rtmpe://f-radiko.smartstream.ne.jp" \
              --playpath "simul-stream.stream" \
              --app "${CHANNEL}/_definst_" \
              -W $playerurl \
              -C S:"" -C S:"" -C S:"" -C S:$authtoken \
              --live \
              --flv ${OUTFILENAME}${FLVFILEEXT} \
              --stop ${RECTIME}
  RET=$?
  #録音終了
  END=`date +%s`
  SS=`expr ${END} - ${START}`
  if [ $SS -gt `expr $RECTIME - 30` ] ; then
      echo "OK... " $SS " > " $RECTIME "(-30)"
      
  else
      echo "NG... " $SS " <= " $RECTIME "(-30)" NOKORI=`expr $RECTIME - $SS` echo ./simple_rec_radiko.sh $OUTFILEPREFIX $NOKORI $CHANNEL # ------- echo *------------------- RECOVERY START ------------------------* echo *------------------- RECOVERY START -----`date`-------------* >> err.log 2>&1
      ./mail_err.sh
      ./simple_rec_radiko.sh $OUTFILEPREFIX $NOKORI $CHANNEL >> err.log 2>&1 &
      echo *------------------- RECOVERY E N D -----`date`-------------*
  fi

  if [ $RET -ne 1 -o `wc -c ${OUTFILENAME}${FLVFILEEXT} | awk '{print $1}'` -ge 10240 ]; then
    echo RETURN ... $RET 
    echo `wc -c ${OUTFILENAME}${FLVFILEEXT} | awk '{print $1}'`
    break
  elif [ ${RETRYCOUNT} -ge 5 ]; then
    echo "failed rtmpdump"
    exit 1
  else
    RETRYCOUNT=`expr ${RETRYCOUNT} + 1`
  fi
done

echo `date` MP3 Encode Start.

${FFMPEG} -y -i "${OUTFILENAME}${FLVFILEEXT}" -vn -acodec copy "${OUTFILENAME}${AACFILEEXT}"
# ${FFMPEG} -y -i "${OUTFILENAME}${FLVFILEEXT}" -acodec libmp3lame -ab 128k "${OUTFILENAME}${MP3FILEEXT}"

echo `date` MP3 Encode E n d.

# mv ${OUTFILENAME}${MP3FILEEXT}   ${MP3PATH}
mv ${OUTFILENAME}${AACFILEEXT}   ${MP3PATH}

rm -f ${OUTFILENAME}${FLVFILEEXT}

echo "===== `date` E n d. ====="

ラジコの録音、東京の放送を取得する一案(7 サーバーを常時起動させるには

これで万事解決と思いたいですが、
サーバーが起動していないと、このスクリプトは動作しないです。
通常はバックグラウンドで起動させればずっと動いているはずです。
が、さくらインターネットのベーシックプランでは、あまり長い間プロセスが動いているとkillされてしまいます。
?一度暴走させて、「すべてのスクリプトを止めた」というメールが来てひやりとしました^^;?

また、長時間起動しっぱなしの時、プロセスは残っているのに返答がない、という状況が起きました。
それほどのリソースを食うスクリプトではないですが、何か対策は必要です。

起動スクリプト

こんな感じで作ってみました。

#!/bin/sh
export PATH=/home/xxxx/perl5/bin:/home/xxxx/local/bin:$PATH

# server running check.
ST=`ps ax|grep server|grep -v grep`
# running...0 stop...1
RUN=$?

#PID
PID=`echo $ST| cut -d ' ' -f1`

Msg(){
    echo `date '+%Y/%m/%d %H:%M:%S'`	$1
}

if [ $# -eq 0 ];then
    # Status
    if [ $RUN -eq 0 ] ;then
        Msg "running.($PID)"
    else
        Msg "stop." 
    fi
    #
else
    if [ $1 = "start" ] ;then
        Msg "to start." 
        if [ $RUN -eq 0 ] ;then
            Msg "already running.($PID)"
        else
            ./server_token.pl &
        fi
        
    elif [ $1 = "stop" ] ;then
        Msg "to stop." 
        if [ $RUN -eq 0 ] ;then
            kill $PID
        else
            Msg "already stop.($PID)"
        fi
    elif [ $1 = "restart" ] ;then
        Msg "restart." 
        if [ $RUN -eq 0 ] ;then
            kill $PID
            Msg "stop."
            sleep 1
            ./server_token.pl &
        else
            Msg "already stop.($PID)"
            ./server_token.pl &
        fi
    else
        echo "usage : $0 [start|stop|restart]"
    fi
fi

まずexportコマンドでユーザーディレクトリにモジュールをインストールしてあるパスを設定します。

サーバーが起動しているかどうかは、psコマンドでserverという文字列があるかどうかで確認しています。
もちろんserverという名前を含む別プロセスがあると誤作動します。ここは注意が必要です。

引数が0の場合にはstatusとなります。PIDか停止しているかを表示して終わります。

あとはstart、stop、resutartに応じて動作します。
起動するときには
./server_token.pl &
のようにしてバックグラウンド起動しています。

restartは録音を失敗したくない番組の5分前に実行しています。

startは落ちている場合に起動する、という動作になっているので、5分おきに起動しています。

どちらもcrontabにて設定をしてあります。

 

ラジコの録音、東京の放送を取得する一案(6 サーバークライアントを試す

感覚的にはWEBサーバーを介したほうが簡単そうです。
けど、今回はソケットを使います。勉強のためです。

とはいえ、あまり時間を書けて1からやっても仕方ないので、
文明の利器、検索を使います 笑

ものすごく有用なページがヒット。

ソケットによる通信を行う

このサンプルをつかわせてもらいましょ。

動作検証

こういうときは、とりあえず、コードを借りてきて自分の環境で動作させてみます。

サーバー起動

クライアント起動

”Hello”とサーバーに送信

”Hello”をサーバーで受取、”echo:Hello”と返す

サーバーからの応答”echo:Hello”を表示

という流れです。
クライアントから何か文字列を送り、サーバーはそれに対して加工してクライアントに返しています。
これを改造すれば、

クライアントから「局ID」を送信し、トークンをサーバーから受信する

ということが簡単にできそうですね。

サーバーはこのようにしました。

#!/usr/bin/perl

use strict;
use warnings;
use Socket;
use Fcntl qw(:flock);

# 2. 受付用ソケット情報の作成
my $local_port = 9000;

#ロック
open  LOCK , "+>" , "./serverlock.${local_port}";
if ( flock LOCK ,LOCK_EX|LOCK_NB ){
    print "OK LOCK!\n";
}else{
    print "NG LOCK!$!\n";
    exit ;
}

# サーバ
# 1. 受付用ソケットの作成
my $sock_receive;
socket($sock_receive, PF_INET, SOCK_STREAM, getprotobyname( 'tcp' ))
  or die "Cannot create socket: $!";

my $pack_addr = sockaddr_in($local_port, INADDR_ANY);

# 3. 受付用ソケットと受付用ソケット情報を結びつける
bind($sock_receive, $pack_addr)
  or die "Cannot bind: $!";

# 4. 接続を受け付ける準備をする。
listen($sock_receive, SOMAXCONN)
  or die "Cannot listen: $!";

# 5. 接続を受け付けて応答する。
my $sock_client; # クライアントとの通信用のソケット

while (accept( $sock_client, $sock_receive )) {
  my $content;
  
  # クライアントからのデータの読み込み
  while (my $line = <$sock_client>) {
    $content .= $line;
  }

  my $ret=`./token_radiko.sh $content`;
  
  # クライアントへのデータの書き込み
  print $sock_client "$ret";
  close $sock_client;
}

一応ロック機構を付けました。なくても良いかもしれません。
そして、返却文字列を前述したトークン取得のシェルスクリプトにしました。

トークンだけ取得

 

クライアントはこんな感じ

#!/usr/bin/perl

use strict;
use Socket;

my $CH="";
if (@ARGV == 1){
    $CH=$ARGV[0];
    }else{
        print "usage : ./$0 [CHANNEL]\n";
        exit 1;
}

# クライアント
# 1. ソケットの作成
my $sock;
socket($sock, PF_INET, SOCK_STREAM, getprotobyname('tcp' ))
or die "Cannot create socket: $!";

# 2. ソケット情報の作成
# 接続先のホスト名
my $remote_host = 'domein.com';
my $packed_remote_host = inet_aton($remote_host)
or die "Cannot pack $remote_host: $!";

# 接続先のポート番号
my $remote_port = 9000;

# ホスト名とポート番号をパック
my $sock_addr = sockaddr_in($remote_port, $packed_remote_host)
or die "Cannot pack $remote_host:$remote_port: $!";

# 3. ソケットを使って接続
connect($sock, $sock_addr)
or die "Cannot connect $remote_host:$remote_port: $!";

# 4. データの書き込み
# 書き込みバッファリングをしない。
my $old_handle = select $sock;
$| = 1;
select $old_handle;

print $sock $CH;

# 書き込みを終了する
shutdown $sock, 1;

# 5. データの読み込み
my $cnt=0;
while (my $line = <$sock>) {
    print "$cnt $line";
    $cnt++;
}

# 6. ソケットを閉じる
close $sock;

チャンネルIDを引数にして実行するだけです。
これでクライアントにいながら、さくらインターネットで取得したトークンだけを得られます。

ラジコの録音、東京の放送を取得する一案(5 さくらインターネットでトークンを得る

トークンをさくらインターネットで取得すれば東京の放送を録音できそうです。
しかしいくつかモジュールが足りません。
ですが、ホームディレクトリにインストールすることで解決できます。

swftoolsのインストール

http://www.swftools.org/download.htmlから最新版をダウンロードします。

最新版は 2013-04-09の swftools-2013-04-09-1007.tar.gzのようでしたが、
私は一つ前のにしました。
最新版も結構前ですから、2013年版でも問題ないと思います。

$ wget http://www.swftools.org/swftools-0.9.2.tar.gz

コンパイルします。

$ ./configure --prefix=$home/local
$ gmake
$ gmake install

 

makeだと通らなかったので、gmakeを使いました。

base64のインストール

最新版はエラーに成ってしまいました。
よって古いバージョンにしました。
最新版でも成功するなら良いと思います。

ダウンロードします。

$ wget http://ftp.gnu.org/gnu/coreutils/coreutils-8.10.tar.gz

解凍してコンパイル。

$ tar zxfv ~/download/coreutils-8.10.tar.gz
$ cd coreutils-8.10/
$ ./configure --prefix=$home/local
$ make
$ make install

 

これでOKです。

多分設定してあると思いますが$homeを確認してから実行するようにしてください。

echo $home

 

ラジコの録音、東京の放送を取得する一案(4 ラジコのチャンネルIDの調べ方

そもそも自分のネットワークがラジコ的にどこの地域だと認識されているのかを知る方法はあるのでしょうか。
一番簡単なのはラジコに接続して見るのが手っ取り早いです。
ところが、はっきりと「○○県」と出るわけではないので、人によっては???となりそうです。
というわけでその辺りの基本的(だけどあまりオープンじゃない)情報の知り方をレビューします。

自分のネットワークがどこになっているか

地域判定について

ここにアクセスします。
現在の地域判定結果、となっているのがラジコで認識している地域です。
ちゃんと自分の地域になっているでしょうか?

スマホやタブレットであれば正常に認識されている可能性は高いのですが、
PCだとおかしい場合が多いです。
現に私は愛知県になっています。
この下の修正後の地域判定結果に正しい地域を選んで送信すると
2?3日で修正してくれます。ところが、、、しばらくするとまた戻ってしまうのでいたちごっこです。

ま、とりあえず、どこと認識されているかはこれでわかるのです。

チャンネルIDはどうやって調べるのか?

エリアIDから調べます。
エリアIDは先程の地域判定のページのソースからわかります。

このエリアIDを使って以下のURLにアクセスします。
東京の場合です。
http://radiko.jp/v2/station/list/JP13.xml
このJP13の部分を適宜変更します。
そうすると、XMLが表示されます。<ID>の部分がチャンネルIDとなり、録音に使用します。

JP1  北海道
JP2  青森県
JP3  岩手県
JP4  宮城県
JP5  秋田県
JP6  山形県
JP7  福島県
JP8  茨城県
JP9  栃木県
JP10 群馬県
JP11 埼玉県
JP12 千葉県
JP13 東京都
JP14 神奈川県
JP15 新潟県
JP16 富山県
JP17 石川県
JP18 福井県
JP19 山梨県
JP20 長野県
JP21 岐阜県
JP22 静岡県
JP23 愛知県
JP24 三重県
JP25 滋賀県
JP26 京都府
JP27 大阪府
JP28 兵庫県
JP29 奈良県
JP30 和歌山県
JP31 鳥取県
JP32 島根県
JP33 岡山県
JP34 広島県
JP35 山口県
JP36 徳島県
JP37 香川県
JP38 愛媛県
JP39 高知県
JP40 福岡県
JP41 佐賀県
JP42 長崎県
JP43 熊本県
JP44 大分県
JP45 宮崎県
JP46 鹿児島県
JP47 沖縄県

ラジコの録音、東京の放送を取得する一案(3 トークンだけ取得

トークンを取得するだけのスクリプトを作ります。

最終的にちゃんと取得できているかを確認するためのrtmpdumpコマンドをエコー(表示)しています。

#!/bin/sh

# TBS:TBSラジオ
# QRR:文化放送
# LFR:ニッポン放送
# NSB:ラジオNIKKEI
# INT:INTER FM
# FMT:TOKYO FM
# FMJ:J-WAVE
# JORF:ラジオ日本
# BAYFM78:bayfm78
# NACK5:NACK5
# YFM:FMヨコハマ

if [ $# -eq 1 ]; then
  CHANNEL=$1
else
  echo "usage : $0 CHANNEL"
  exit 1
fi

CHANNEL=$1

RTMPDUMP=/usr/local/bin/rtmpdump
FFMPEG=/usr/bin/ffmpeg
MP3PATH=/home/mlin/html/mp3/

OUTFILEBASEPATH=./
OUTFILENAME=${OUTFILEBASEPATH}/`date +%Y-%m-%d`
FLVFILEEXT=".flv"
AACFILEEXT=".aac"
MP3FILEEXT=".mp3"

MARGINTIMEMIN=1
# RECTIME=`expr ${RECTIMEMIN} \* 60 + ${MARGINTIMEMIN} \* 2 \* 30`

# 30sec wait
# echo "===== `date` Wait 30sec. ====="
# for f1 in 0
# do
  # for f2 in 9 8 7 6 5 4 3 2 1 0
  # do
    # echo -n "$f1$f2\r"
    # sleep 1
  # done
# done

#開始時刻
START=`date +%s` 

cd ${OUTFILEBASEPATH}

playerurl=http://radiko.jp/player/swf/player_3.0.0.01.swf
playerfile=./player.swf
keyfile=./authkey.png

#
# get player
#
if [ ! -f $playerfile ]; then
  wget -q -O $playerfile $playerurl

  if [ $? -ne 0 ]; then
    echo "failed get player"
    exit 1
  fi
fi

#
# get keydata (need swftools)
#
if [ ! -f $keyfile ]; then
  swfextract -b 14 $playerfile -o $keyfile

  if [ ! -f $keyfile ]; then
    echo "failed get keydata"
    exit 1
  fi
fi

if [ -f auth1_fms_${CHANNEL} ]; then
  rm -f auth1_fms_${CHANNEL}
fi

#
# access auth1_fms
#
for var in 3 5 7 9 10 0
do
  wget -q \
       --header="pragma: no-cache" \
       --header="X-Radiko-App: pc_1" \
       --header="X-Radiko-App-Version: 2.0.1" \
       --header="X-Radiko-User: test-stream" \
       --header="X-Radiko-Device: pc" \
       --post-data='\r\n' \
       --no-check-certificate \
       --save-headers \
       --tries=5 \
       --timeout=5 \
       -O auth1_fms_${CHANNEL} \
       https://radiko.jp/v2/api/auth1_fms

  if [ $? -ne 0 ]; then
    if [ $var -ne 0 ]; then
      # echo "failed auth1 process [$var sec sleep...retry]"
      sleep $var
    else
      # echo "[[[[ Failed auth1 process --- `date` --- ]]]]"
      exit;
    fi
  else
    # echo "sucsess auth1 process"
    break;
  fi
done
#
# get partial key
#
authtoken=`cat auth1_fms_${CHANNEL} | perl -ne 'print $1 if(/x-radiko-authtoken: ([\w-]+)/i)'`
offset=`cat auth1_fms_${CHANNEL} | perl -ne 'print $1 if(/x-radiko-keyoffset: (\d+)/i)'`
length=`cat auth1_fms_${CHANNEL} | perl -ne 'print $1 if(/x-radiko-keylength: (\d+)/i)'`

partialkey=`dd if=$keyfile bs=1 skip=${offset} count=${length} 2> /dev/null | base64`

# echo "authtoken: ${authtoken} \noffset: ${offset} length: ${length} \npartialkey: $partialkey"
echo ${authtoken}

rm -f auth1_fms_${CHANNEL}

if [ -f auth2_fms_${CHANNEL} ]; then
  rm -f auth2_fms_${CHANNEL}
fi

#
# access auth2_fms
#
for var in 3 5 7 9 10 0
do
  wget -q \
       --header="pragma: no-cache" \
       --header="X-Radiko-App: pc_1" \
       --header="X-Radiko-App-Version: 2.0.1" \
       --header="X-Radiko-User: test-stream" \
       --header="X-Radiko-Device: pc" \
       --header="X-Radiko-Authtoken: ${authtoken}" \
       --header="X-Radiko-Partialkey: ${partialkey}" \
       --post-data='\r\n' \
       --no-check-certificate \
       --tries=5 \
       --timeout=5 \
       -O auth2_fms_${CHANNEL} \
       https://radiko.jp/v2/api/auth2_fms

  if [ $? -ne 0 -o ! -f auth2_fms_${CHANNEL} ]; then
    if [ $var -ne 0 ]; then
      echo "failed auth2 process [$var sec sleep...retry]"
      sleep $var
    else
      echo "[[[[ Failed auth2 process --- `date` --- ]]]]"
      exit;
    fi
  else
    echo "sucsess auth2 process"
    break;
  fi
done

echo "authentication success"

areaid=`cat auth2_fms_${CHANNEL} | perl -ne 'print $1 if(/^([^,]+),/i)'`
echo "areaid: $areaid"

rm -f auth2_fms_${CHANNEL}

echo rtmpdump -v \
              -r "rtmpe://f-radiko.smartstream.ne.jp" \
              --playpath "simul-stream.stream" \
              --app "${CHANNEL}/_definst_" \
              -W $playerurl \
              -C S:"" -C S:"" -C S:"" -C S:$authtoken \
              --live \
              --flv ./test2.flv \
              --stop 30
exit

少し工夫した部分は
  for var in 3 5 7 9 10 0
の部分ですね。
トークンを得る際にエラーが出る場合を考慮して5回やり直します。
3.5.7と数値が書いてあるのは、その秒数スリープさせるためです、
連続でエラーになった場合には徐々に待ち時間を長くしています。
最後の0で諦める、と言った形です。

ラジコの録音、東京の放送を取得する一案(2 サーバークライアントの基本

ソケット通信を試します。
結構難しいのですが、以下のサンプルを使ってなんとかします。

要は、クライアントからチャンネル情報を送って、
サーバーからトークンを返すだけ、なのでこれで良いのです。

サーバー

さくらインターネットの方でサーバーを起動させます。

 

#!/usr/bin/perl

use strict;
use warnings;
use Socket;
use Fcntl qw(:flock);

# 2. 受付用ソケット情報の作成
my $local_port = 9000;

#ロック
open  LOCK , "+>" , "./serverlock.${local_port}";
if ( flock LOCK ,LOCK_EX|LOCK_NB ){
    print "OK LOCK!\n";
}else{
    print "NG LOCK!$!\n";
    exit ;
}

# サーバ

# 1. 受付用ソケットの作成

my $sock_receive;
socket($sock_receive, PF_INET, SOCK_STREAM, getprotobyname( 'tcp' ))
  or die "Cannot create socket: $!";


my $pack_addr = sockaddr_in($local_port, INADDR_ANY);

# 3. 受付用ソケットと受付用ソケット情報を結びつける
bind($sock_receive, $pack_addr)
  or die "Cannot bind: $!";

# 4. 接続を受け付ける準備をする。
listen($sock_receive, SOMAXCONN)
  or die "Cannot listen: $!";

# 5. 接続を受け付けて応答する。
my $sock_client; # クライアントとの通信用のソケット

while (accept( $sock_client, $sock_receive )) {
  my $content;
  
  # クライアントからのデータの読み込み
  while (my $line = <$sock_client>) {
    $content .= $line;
  }

  my $ret="Server."
  
  # クライアントへのデータの書き込み
  print $sock_client "$ret";
  close $sock_client;
}

$contentという変数にクライアントからのデータを取得できます。
クライアントにデータを戻すには、
print $sock_client “$ret”;
で行っています。この例は単に「Server.」と返しています。

クライアント

ラズパイからクライアントを起動します。
サーバーに繋いでサーバーからデータを受け取ります。

#!/usr/bin/perl

use strict;
use Socket;

my $CH="";
if (@ARGV == 1){
    $CH=$ARGV[0];
}else{
    print "usage : ./$0 [CHANNEL]\n";
    exit 1;
}

# クライアント
# 1. ソケットの作成
my $sock;
socket($sock, PF_INET, SOCK_STREAM, getprotobyname('tcp' ))
  or die "Cannot create socket: $!";

# 2. ソケット情報の作成

# 接続先のホスト名
my $remote_host = 'サーバーのドメイン';
my $packed_remote_host = inet_aton($remote_host)
  or die "Cannot pack $remote_host: $!";

# 接続先のポート番号
my $remote_port = 9000;

# ホスト名とポート番号をパック
my $sock_addr = sockaddr_in($remote_port, $packed_remote_host)
  or die "Cannot pack $remote_host:$remote_port: $!";

# 3. ソケットを使って接続
connect($sock, $sock_addr)
  or die "Cannot connect $remote_host:$remote_port: $!";

# 4. データの書き込み
# 書き込みバッファリングをしない。
my $old_handle = select $sock;
$| = 1; 
select $old_handle;

print $sock "Hello";

# 書き込みを終了する
shutdown $sock, 1;

# 5. データの読み込み
my $cnt=0;
while (my $line = <$sock>) {
  print "$cnt $line";
  $cnt++;
}

# 6. ソケットを閉じる
close $sock;

サーバーにデータを送るには
print $sock “Hello”;
でやっています。
そのあとの
print “$cnt $line”;
でサーバーからの返答を受け取るというわけです。

ラジコの録音、東京の放送を取得する一案(1

そもそも、radikoの録音はどうやっているのでしょうか。

まずはトークンを取得します。

TBSラジオの例です。

wget -q '--header=pragma: no-cache' '--header=X-Radiko-App: pc_1' \
'--header=X-Radiko-App-Version: 2.0.1' '--header=X-Radiko-User: test-stream' \
'--header=X-Radiko-Device: pc' '--post-data=\r\n' \
--no-check-certificate --save-headers --tries=5 --timeout=5 \
-O auth1_fms https://radiko.jp/v2/api/auth1_fms

これでトークンがとれます。

トークン

cat auth1_fms | perl -ne 'print $1 if(/x-radiko-authtoken: ([\w-]+)/i)'

パーティカルキー

# offset
cat auth1_fms_${OUTFILEPREFIX}_${CHANNEL} | perl -ne 'print $1 if(/x-radiko-keyoffset: (\d+)
/i)'
# length
cat auth1_fms_${OUTFILEPREFIX}_${CHANNEL} | perl -ne 'print $1 if(/x-radiko-keylength: (\d+)
/i)'

取得したoffsetとlengthを使って取得します。

dd if=$keyfile bs=1 skip=[offset] count=[length] 2> /dev/null | base64

例)トークン S:wdaDhu6evQheSnXIM8vbGA
パーティカルキー cHHB6YxRZrVhZMRASY1J4w==

ところが、このトークンはまだ使えません。

更に認証を通します。

取得したトークンとパーティカルキーを使います。

wget -q '--header=pragma: no-cache' '--header=X-Radiko-App: pc_1' \
'--header=X-Radiko-App-Version: 2.0.1' '--header=X-Radiko-User: test-stream' \
'--header=X-Radiko-Device: pc' \
'--header=X-Radiko-Authtoken: wdaDhu6evQheSnXIM8vbGA' \
'--header=X-Radiko-Partialkey: cHHB6YxRZrVhZMRASY1J4w==' \
'--post-data=\r\n' --no-check-certificate --tries=5 \
--timeout=5 -O auth2_fms https://radiko.jp/v2/api/auth2_fms

こうして取得したトークンを使えばよいです。

rtmpdump -v -r rtmpe://f-radiko.smartstream.ne.jp --playpath simul-stream.stream \
--app TBS/_definst_ -W http://radiko.jp/player/swf/player_3.0.0.01.swf \
-C S: -C S: -C S: -C S:wdaDhu6evQheSnXIM8vbGA --live \
--flv ./test.flv --stop 30

最後を少し変えれば、そのまま再生もできます。

rtmpdump -v -r rtmpe://f-radiko.smartstream.ne.jp --playpath simul-stream.stream \
--app TBS/_definst_ -W http://radiko.jp/player/swf/player_3.0.0.01.swf \
-C S: -C S: -C S: -C S:wdaDhu6evQheSnXIM8vbGA --live \
--flv - | mplayer -cache 500 -quiet -

mplayerが必要です。