プログラムの概要
ffmpegのフロントエンドです。ご存知の通り、ffmpeg(FFmpeg is a trademark of Fabrice Bellard, originator of the FFmpeg project.)は、有名な動画変換プログラムです。添付したffmpeg.exeは、7/2009に、その時点のものを無変更でビルドしたものです。
VolMaxは、ユーザインタフェースのVolMax.exeが、4つのコンソールアプリケーションを使って動作します。ffmpeg.exe以外は、すべてC#で書きました。
処理は次のように進みます。
1.preAudioが、「音量計測」をしながら、「WAVファイル作成」を行います。また、画像のサイズやフレームレートを「動画情報ファイル」に記録します。PCMへのデコードには、DirectShowが使われます。
2.mk_bat1は、「動画情報ファイル」を読み込んで、ffmpegのオプションを決めてバッチファイルを作ります。
3.このバッチファイルを実行して、変換を行います。バッチファイルの内容は、preAudioの出力を、パイプでffmpegの入力にするものです。preAudioは、DirectShowの機能で映像をデコードして、標準出力に出力します。
音声も映像もデコードは、DirectShowを使用しています。デコードしたものを、ffmpegの入力としています。したがって、Windows Media Playerで再生できるものが変換可能です。(ただし、保護されたファイは変換できません。)
Windows Media Playerは、asf、wmv,mpeg1が再生できるようです。他の動画ファイル用には、スプリッタとデコーダを追加する必要があります。
ffmpegは、いろいろな動画ファイルをそのまま入力できるように作られており、デコードとエンコードを同時に行えるようになっています。しかし、実際には上手く行かないことも多々あります。音声と映像の同期や字幕の処理で問題がおきることがありました。
■音声トラックの選択
音声トラックが複数ある場合は、自動的に日本語を選択したいと考えました。preAudioは、複数の音声トラックがある場合は、「Jを含む名前」の付いたトラックを選択します。無い場合は、2番目のトラックを選択します。「2番目のトラック」には、あまり根拠がありません。
preAudio自体は、-at n オプション(nは、トラック番号か先頭文字)で、トラックが選択できるように作られていますが、自動変換が目的なのでVolMaxはトラックを選択できません。
(音声の種類は、Pinの名前、ストリームの名前として取得しますが、命名にルールがあるわけではないようです。名前がない(空)こともありました。)
■その他の変換ルール
実際のmpegファイルへの変換は、バッチファイルで行います。このバッチファイルを作るのが、mk_bat1です。mk_bak1.exeは、名前が、"mk_bak1.exe"であれば、任意のものに置き換えてかまいません。
_tmp.batには、次のような内容を出力してください。
preVideo %1| ffmpeg -y -i "C:¥tmp¥_tmppcm.wav" -acodec mp2 -ar 44100 -ab 192000 -vol 1079 -f rawvideo -s 320x240 -pix_fmt yuyv422 -r 25.000 -i pipe: -vcodec mpeg2video -qscale 1 -r 23.98 -f mpeg "C:¥tmp¥video.flv.mpg"
VolMaxは、「_tmp.bat 動画情報ファイル」と起動します。%1は、preVideoに動画情報ファイルを渡すために指定します。|は、パイプの使用を示します。後は、任意のffmpegのオプションを出力してください。mpeg以外への変換でもかまいません。注意が必要なのは、映像のサイズやフレームレートは、正確に指定する必要があることです。パイプ入力なので、ffmpegは、動画の情報を動画ファイルから取得できません。
変換の条件となる、動画情報ファイル()は、以下の内容になっています。「mk_bat1.exe 動画情報ファイル名 バッチファイル名」として、渡されます。
<?xml version="1.0"?>
<MediaInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<in_file>C:¥tmp¥FLV¥tmp¥tmp¥video25.flv</in_file>
<wav_file>C:¥tmp¥FLV¥VolMax_1_5¥_tmppcm.wav</wav_file>
<videoFormat>MEDIASUBTYPE_YUY2</videoFormat>
<fps>25</fps>
<width>540</width>
<height>406</height>
<startTime>0</startTime>
<samplingRate>22050</samplingRate>
<channels>2</channels>
<peak>17464</peak>
</MediaInfo>
私の作成したルール(mk_bat1.cs)は以下のようなものです。
using System;
using System.Text;
using System.IO;
using System.Diagnostics;
namespace mk_bat1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("mk_bat1 v1.0 !#" + Process.GetCurrentProcess().Id.ToString());
if (args.Length != 2)
{
Console.WriteLine("Usage: mk_bat1 media_info_file bat_file");
return;
}
string media_info_file = args[0]; //メディア情報ファイル名
string bat_file = args[1]; //作成するバッチファイル名
File.Delete(bat_file); //作成するバッチファイルを削除しておく
//メディア情報読み取り---------------------------------------------------------------
preAudio.MediaInfo.SetPath(media_info_file);
preAudio.MediaInfo mi = preAudio.MediaInfo.Load();
//出力ファイル名の作成---------------------------------------------------------------
string output_file_name = mi.in_file + ".mpg";//入力ファイル名に.mpgを付加
if (File.Exists(output_file_name))
{
//単に.mpgを付加した名前は既に使われている。.番号.mpgを付加して一意の名前を作る
for (int i = 1; ; i++)
{
output_file_name = mi.in_file + "." + i.ToString() + ".mpg";
if (!File.Exists(output_file_name)) break;
}
}
//ffmpegのコマドラインを作成----------------------------------------------------------
string cmd = @"preVideo %1| ffmpeg -y";
//Audioのオプションを追加
cmd += " -i \"" + mi.wav_file + "\""; //入力WAVファイルの指定
cmd += " -acodec mp2"; //エンコーダの指定
if (mi.samplingRate == 22050)
cmd += " -ar 44100"; //入力が22050Hzなら44100Hzに
else if (mi.samplingRate == 24000)
cmd += " -ar 48000"; //入力が22050Hzなら44100Hzに
else if (mi.samplingRate < 44100)
cmd += " -ar 44100"; //その他の44100Hz未満は44100Hzに
cmd += " -ab 192000"; //入力より圧縮率が劣ると考え、大きく
cmd += " -vol " + ((int)(32767.0 / (double)mi.peak * 256)).ToString();//音量最大化
//Video入力の設定
cmd += " -f rawvideo -s " + mi.width.ToString() + "x" + mi.height.ToString();//サイズ
cmd += " -pix_fmt ";//フォーマット
if (mi.videoFormat == "MEDIASUBTYPE_YUY2")
cmd += "yuyv422";
else
cmd += "bgr24";
cmd += " -r " + mi.fps.ToString("#0.000");//fps設定
if (mi.startTime >= 5000000)
{
//音がズレる。映像の先頭をカット(ffmpegで-ssはpipe:に無効と出るが、実際には有効)
long ss=(mi.startTime+5000000)/10000000;
cmd += " -ss " + ss.ToString();
}
cmd += " -i pipe:";//パイプ入力指定
//Video出力指定
cmd += " -vcodec mpeg2video -qscale 1";
if (mi.fps < 29.0)
{
cmd += "^ -r 23.98";
}
else if (mi.fps <= 60.0)
{
cmd += " -r 29.97";
}
else
{
//30fps以上 119.98を想定
cmd += " -r " + ((double)(mi.fps / 4.0)).ToString("#0.000");
}
if ((mi.width > 720) || (mi.height > 480))
{
//DVDの規格より大きい
double f = 720.0 / mi.width;
int new_height = (int)(mi.height * f);
new_height >>= 4;
new_height <<= 4;//16の倍数に
cmd += " -s 720x" + new_height.ToString();
}
cmd += " -f mpeg \"" + output_file_name + "\"";
//バッチファイル出力
TextWriter tw = new StreamWriter(bat_file,false,Encoding.GetEncoding("sjis"));
tw.WriteLine(cmd);
tw.Close();
Environment.ExitCode = 0;//終了コード:正常
}
}
}
■私の変換ルールの説明
1.出力ファイル名
入力ファイル名に".mpg"を付加あする。同じファイル名がすでにある場合は、".番号.mpg"を付加する。(必ず、存在しないファイル名にする。ffmpegが上書き確認しないようにすることが必須。)
2.音声のサンプリングレート
mpegでは44100Hz か48000Hzが使われる。22050Hz、24000Hzなら2倍する。その他は、44100Hzにする」。
3.最近は、119.88fpsなどの映像フレームレートのものがある。私のDVDプレイヤー、テレビでは再生できないので、23.98fps、29.87fpsにする。
4.映像のサイズが1280x720だったりする。もちろん私のテレビには映らない。時には、8の倍数以外のサイズもある。これを720x480以下の適切なサイズにする。
5.開始時間がゼロで無い場合、その時間分の映像をカットする。
■気がついていること
1.640MB以上のMPEGファイル
640MB(だと思う)以上のMPEGファイルは、私のDVDプレイヤーで再生できない。再生が止まって、メニューに戻ってしまう。
自動分割できないものか。
2.分割ダウンロードされたasfのタイムスタンプ
50分程度の動画をダウンロードツールでダウンロードすると4つのファイルに分割してダウンロードされました。1.の問題があるので分割自体は歓迎。ただ、タイムスタンプが「正しく」付与されているらしく、2つ目以降のファイルは再生できない。(VLCは再生する。)
今は、windows media encoderに含まれるfile editerでインデクスを書き換えてから変換している。
3.一時的にテレビでみることが目的なので、字幕や複数音声を出力することは考えていない。
|