use XML::XPath; #sudo apt install libxml-xpath-perl
use XML::XPath::XMLParser;
use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat);
-#use Time::Piece;
-use IPC::Open3 qw(open3);
-use Symbol qw(gensym);
sub dp;
my $config_url = 'https://www.nhk.or.jp/radio/config/config_web.xml';
# オプション解析
-my $is_check_mode = 0;
-my $is_verbose_mode = 0;
-my $retry = 5;
-GetOptions('check|c' => \$is_check_mode,
- 'verbose|v' => \$is_verbose_mode,
- 'retry|r=i' => \$retry,
+my $is_check_mode = 0;
+my $is_verbose_mode = 0;
+my $ffmpeg_loglevel = 'fatal'; #ログレベル fatal: 致命的なもののみ出力
+my $ffmpeg_seg_max_retry = 5; #hls エラー発生時にセグメントを再読み込みする最大回数
+my $retry = 5;
+GetOptions('check|c' => \$is_check_mode,
+ 'verbose|v' => \$is_verbose_mode,
+ 'ffmpeg-loglevel=s' => \$ffmpeg_loglevel,
+ 'ffmpeg-seg_max_retry=i' => \$ffmpeg_seg_max_retry,
+ 'retry|r=i' => \$retry,
);
my($station, $req_duration, $output) = @ARGV;
+$output = Encode::decode('UTF-8', $output);
#HLS URL 取得
$station = 'tokyo-r2' if $station eq 'r2';
dp "try: $try numbered_filename: $numbered_filename this_duration: $this_duration";
- $ret = system('ffmpeg', '-fflags', '+discardcorrupt', #破損したフレームを破棄
- '-loglevel', 'fatal', #ログレベル 致命的なもののみ出力
- '-y', #Overwrite -> Yes
- '-i', $hls_url, '-t', $this_duration, $numbered_filename);
+ my $ffmpeg = 'ffmpeg -fflags +discardcorrupt '. #破損したフレームを破棄
+ "-loglevel $ffmpeg_loglevel ".
+ "-seg_max_retry $ffmpeg_seg_max_retry ". #hls エラー発生時にセグメントを再読み込みする最大回数
+ '-y '. #Overwrite -> Yes
+ "-i $hls_url ".
+ "-t $this_duration ".
+ "'$numbered_filename' ";
+ print Encode::decode('UTF-8', `$ffmpeg 2>&1`);
+ $ret = $? >> 8;
#出力ファイルが存在する?
next unless -f $numbered_filename;
#ffmpeg で録音時間を取得 (秒数で返す)
sub get_rec_duration_by_file {
my($file) = @_;
- my $err = gensym;
- my $pid = open3(undef, undef, $err, "ffmpeg -i $file");
- my $info = do { local $/; <$err> };
- waitpid($pid, 0);
+ my $info = do { local $/; `ffmpeg -i '$file' 2>&1` };
if ($info =~ /Duration: (\d+:\d{2}:\d{2})\./) {
return duration2sec($1);
# オプション解析
-my $is_check_mode = 0;
-my $is_verbose_mode = 0;
-my $offset_time_sec = 35; #開始オフセット(秒)
-my $end_margin_sec = 30; #後方余白(秒)
-GetOptions('check|c' => \$is_check_mode,
- 'verbose' => \$is_verbose_mode,
- 'offset-time=i' => \$offset_time_sec,
- 'end-margin=i' => \$end_margin_sec );
+my $is_check_mode = 0;
+my $is_verbose_mode = 0;
+my $is_logging_mode = 0;
+my $ffmpeg_loglevel = '';
+my $ffmpeg_seg_max_retry = '';
+my $offset_time_sec = 30; #開始オフセット(秒)
+my $end_margin_sec = 40; #後方余白(秒)
+GetOptions('check|c' => \$is_check_mode,
+ 'verbose' => \$is_verbose_mode,
+ 'logging' => \$is_logging_mode,
+ 'ffmpeg-loglevel=s' => \$ffmpeg_loglevel,
+ 'ffmpeg-seg_max_retry=s' => \$ffmpeg_seg_max_retry,
+ 'offset-time=i' => \$offset_time_sec,
+ 'end-margin=i' => \$end_margin_sec );
my $script_name = basename($0, '');
my $usage = << " EOM_USAGE";
USAGE) $script_name [ -c | --check | --verbose ]
+ [--logging]
+ [--ffmpeg-loglevel <ffmpeg ログレベル>]
+ [--ffmpeg-seg_max_retry <ffmpeg hls エラー リトライ回数>]
[--offset-time <開始オフセット(秒)>]
[--end-margin <後方余白(秒)>]
<放送局ID> <録音開始日> <録音開始時刻> [<録音長さ(分)> <タイトル>]
--verbose を指定すると, job発行コマンドと番組表の内容を表示してからjob発行をする,
冗長表示モード になります.
+ <ffmpeg ログレベル> ffmpeg の loglevel を info(ffmpegのデフォルト) や warning, quiet 等から指定します.
+ このコマンドにおけるデフォルトは fatal: 致命的なもののみ出力 です.
+
+ <ffmpeg hls エラー リトライ回数> hls エラー発生時にセグメントを再読み込みする最大回数を指定します.
+ デフォルトは 5回 です.
+
<開始オフセット(秒)> には, 録音開始時刻の00秒から実際に録音を開始するまでの秒数を指定します.
デフォルトは $offset_time_sec 秒です.
my $file_name_base = sprintf('%04d-%02d-%02d_%02d%02d_nhk-%s_%s_%dmin', $y, $m, $d, $time_h, $time_m, $station, $title, $min);
my $mp3 = "${file_name_base}.mp3";
+my $txt = "${file_name_base}.txt";
my $duration = get_duration_string($min, $end_margin_sec);
# コマンド構築
-my $rec_cmd = "cd $ENV{RADIKO_SAVE_TO}; $SLEEP $offset_time_sec; $DL $station $duration $mp3; $CHMOD $FILE_PERMISSION $file_name_base*.mp3; ";
+my $rec_cmd = "cd $ENV{RADIKO_SAVE_TO}; $SLEEP $offset_time_sec; ".
+ "$DL ".
+ (length $ffmpeg_loglevel ? "--ffmpeg-loglevel $ffmpeg_loglevel " : '').
+ (length $ffmpeg_seg_max_retry ? "--ffmpeg-seg_max_retry $ffmpeg_seg_max_retry " : '').
+ "$station $duration $mp3 ".
+ ($is_logging_mode ? ">> $txt; " : '; ').
+ "$CHMOD $FILE_PERMISSION $file_name_base*.mp3; ";
my $at_time = sprintf('%d:%02d %02d.%02d.%02d', $time_h, $time_m, $d, $m, ($y - int($y/100)*100 ) );
my $cmd = qq{$ECHO -e '$BASH << EOC\\n$rec_cmd\\nEOC' | $AT "$at_time"};