| history | TOP |
2004/06/20:作成
| overview | TOP |
普段動画を見る必要がある場合、A Player(Windows95/98/Me/画像&サウンド)を使用しています。いろいろ試したんですが、これが一番使い易いように思います。で、状況に応じて、Windows Media Player 9 シリーズや、Real.com - RealOnePlayer is now RealPlayerを使ったりもしています。もちろん後者の2つは異常に重たいですが。
それはそれとして、DelphiからTWindowsMediaPlayerを使いたくなりました。RealPlayerの方は置いといて、Windows Media Player 9 をDelphiから利用したいと、そういうことです。
基本方針としては、インターネット上のファイルではなく、ローカルに保存されているファイルを再生することだけを考えます。というか、実際に試してみた結果、ネットワーク上のファイルを扱うにはなにやらいろいろ邪魔くさそうだったのと、そんなことは本家に任せとけ、というあたりから、逆算された方針だったりします。
| 参考サイト | TOP |
次のリンクが、参考にしたサイトですが、ほとんどが「Windows Media Player 7 SDK」を元にしています。「9」ではなく「7」です。開発環境は「9」が入っているので取り返しがつかないのですが、「7」を使用している場合でも、そのままサンプルコードが使用できるはずです。「9にしかないメソッドやプロパティやイベントは一切使用していません」から。
| ActiveXの取り込み | TOP |
まず、Windows Media Player をコンポーネントとして使うために、コンポーネントパレットに表示させる必要があります(と思う)。現在形は基本的な手順で、過去形になっているのはひょっとしたら変更すべきかも知れない手順です。
というような感じで、だいたいでインストールします。いらなくなった時の消し方ですが、実はよく知りません。ただ、次のようにやるのが正しい削除のニアミスな方法だと思います。
| 前提条件 | TOP |
以降のサンプルでは、フォームに配置した「TWindowsMediaPlayer」の Name は「player」とします。
次のような関数を使用して、取得した値を表示します。
// ---------------------------------------------------------------------------
// デバッグ用メッセージ表示
procedure TForm1.ShowMsg(const S: string);
begin
if (Assigned(Memo1)) then
begin
if (S <> '') then Memo1.Lines.Add(S);
if (Memo1.Lines.Count > 100) then Memo1.Lines.Delete(0);
end;
end;
次のような配列を使用して、取得した値を表示します。
// ---------------------------------------------------------------------------
// ON/OFF表示用文字配列
const
ON_OFF: array[Boolean] of string = ('OFF', 'ON');
OK_NG: array[Boolean] of string = ('NG', 'OK');
| プレイヤーの設定を調べる | TOP |
// ---------------------------------------------------------------------------
// プレイヤーの設定を調べる
procedure TForm1.Button14Click(Sender: TObject);
var
b: Boolean;
s: string;
i: integer;
d: double;
begin
// 区切り線
ShowMsg(StringOfChar('-', 20));
// バージョン
s := player.versionInfo;
ShowMsg(s);
// 自動再生(変更可能)
b := player.settings.autoStart;
s := Format('自動再生 = %s', [ON_OFF[b]]);
ShowMsg(s);
// バランス(変更可能)
i := player.settings.balance;
s := Format('ステレオバランス(-100..100) = %d', [i]);
ShowMsg(s);
// エラーダイアログ(変更可能)
b := player.settings.enableErrorDialogs;
s := Format('エラーダイアログ表示 = %s', [ON_OFF[b]]);
ShowMsg(s);
// ミュート(変更可能)
b := player.settings.mute;
s := Format('ミュート = %s', [ON_OFF[b]]);
ShowMsg(s);
// 再生回数(変更可能)
i := player.settings.playCount;
s := Format('再生回数 = %d', [i]);
ShowMsg(s);
// 再生速度(変更可能)
d := player.settings.rate;
s := Format('再生速度 = %f', [d]);
ShowMsg(s);
b := player.settings.isAvailable['Rate'];
if (b) then
s := '再生速度変更 = 可能'
else
s := '再生速度変更 = 不可';
ShowMsg(s);
// 音量(変更可能)
i := player.settings.volume;
s := Format('音量 = %d', [i]);
ShowMsg(s);
// ループモード(変更可能)
b := player.settings.getMode('loop');
s := Format('ループ再生モード = %s', [ON_OFF[b]]);
ShowMsg(s);
// シャッフルモード(変更可能)
b := player.settings.getMode('shuffle');
s := Format('ランダム再生モード = %s', [ON_OFF[b]]);
ShowMsg(s);
// 区切り線
ShowMsg(StringOfChar('-', 20));
end;
| プレイリストにメディアを追加する | TOP |
TWindowsMediaPlayerでは、再生対象のファイルはプレイリストで管理されています。プレイリストは、本家のプレイヤーでは、直接ファイルをドロップすると「再生リスト1」みたいな色気のない名前のプレイリストが作成されますが、Delphiで使う時も同様で、player.currentPlaylist で取り出せるプレイリストは表示を見る限り「再生リスト1」にあたるようです。多分保存したりすると話が変わるんでしょうけど。
で、問題は、プレイリストにファイルを追加する方法です。何故問題か、というと、プレイリストに追加するメソッドは、引数を、IWMPMedia という型で受けとるからで、これを単純にファイルパスから作り出す方法がよくわからないんです。
で、いろいろと検索したりSDKを眺めたりしていると、player.mediaCollection.add というメソッドが、IWMPMedia を返すことがわかりました。で、次のコードはこれを利用しているというわけです。
本来の player.mediaCollection は、メディアをぼこぼこと追加していき、著作権者とかジャンルとかで絞り込んだプレイリストを作成するメソッドがあるので、これを利用して大量のメディアから見たいメディアをビシッと取り出して、それだけを再生する、というような目的に使用するもののようです。
また、あとあとのために、プレイリストに何が入っているかを知っておく必要があるので、ListBoxに追加したメディアを表示しています。
// ---------------------------------------------------------------------------
// プレイリストにファイルを追加
procedure TForm1.Button17Click(Sender: TObject);
var
i: integer;
Media: IWMPMedia;
begin
// ファイルを選択させる
if OpenDialog1.Execute then
begin
// もっとも簡単な追加方法(ただし1ファイル限定)
//player.URL := OpenDialog1.FileName;
// 全てのファイルについて
for i := 0 to OpenDialog1.Files.Count - 1 do
begin
try
// プレイリストに追加するためのMediaオブジェクトを作成する
Media := player.mediaCollection.add(OpenDialog1.Files[i]);
// プレイリストに追加する
player.currentPlaylist.appendItem(Media);
// 操作用のリストボックスにも追加する
ListBox1.Items.Add(Media.name + '=' + Media.sourceURL);
except
// 未対応ファイルは無視する
;
end;
end;
end;
end;
| プレイリスト操作 | TOP |
以下の2つの「選択したメディアを...する」という処理で、リストボックスとプレイリストの対応が必要になってきます。というかこのためにリストボックスに追加しています。
プレイリスト中のアクティブなメディアを変更するのも、メディアを削除するのも、相変わらず IWMPMedia で指定する必要があります。この IWMPMedia を探すのにまたしてもファイルパスは使用できないため(まあ普通のような気がしてきましたが)、リストボックスとの対応を取っているんですが、問題はSDKを見ると、「想定外のファイル削除にも対応しましょう」とあるんですね。つまりリストボックスには表示されているのに、実際のファイルがどこかに行方不明になっている状態がありうる、ということですよね。ここでは考えていません。
// ---------------------------------------------------------------------------
// 選択したメディアを再生する
procedure TForm1.Button18Click(Sender: TObject);
begin
if (ListBox1.ItemIndex = -1) then exit;
// 念のため今のメディアを停止しておいて
player.controls.stop;
// 確認のために今のメディアの名称を表示
Label3.Caption := player.currentMedia.name;
// メディアを切り替える
player.controls.currentItem := player.currentPlaylist.Item[ListBox1.ItemIndex];
// 確認のために今のメディアの名称を表示
Label2.Caption := player.currentMedia.name;
// で、再生する
player.controls.play;
end;
// ---------------------------------------------------------------------------
// 選択したメディアを削除する
procedure TForm1.Button15Click(Sender: TObject);
var
Media: IWMPMedia;
begin
if (ListBox1.ItemIndex = -1) then exit;
// 選択したメディアを取り出す
Media := player.currentPlaylist.Item[ListBox1.ItemIndex];
// プレイリストからそのメディアを削除する
player.currentPlaylist.removeItem(Media);
// リストボックスからも削除しておく
ListBox1.DeleteSelected;
end;
以下の1行処理達は、実際のところ、TWindowsMediaPlayerをフォーム上に置いただけで、標準でコンポーネントに表示されているボタンにほぼ対応しています。つまり、本当にこれだけの処理を行うボタンを配置する必要は、コレッポッチもないだろうということです。
// ---------------------------------------------------------------------------
// 再生リスト内の現在の項目の位置を、次の項目に
procedure TForm1.Button6Click(Sender: TObject);
begin
player.controls.next();
end;
// ---------------------------------------------------------------------------
// 再生リスト内の現在の項目の位置を、1 つ前の項目に
procedure TForm1.Button9Click(Sender: TObject);
begin
player.controls.previous();
end;
// ---------------------------------------------------------------------------
// 現在のメディア項目の再生を開始するか、または一時停止の項目の再生を再開
procedure TForm1.Button8Click(Sender: TObject);
begin
player.controls.play();
end;
// ---------------------------------------------------------------------------
// メディア項目の再生を停止
procedure TForm1.Button10Click(Sender: TObject);
begin
player.controls.stop();
end;
// ---------------------------------------------------------------------------
// メディア項目の再生を一時停止
procedure TForm1.Button7Click(Sender: TObject);
begin
player.controls.pause();
end;
// ---------------------------------------------------------------------------
// メディア項目の早送りを開始
procedure TForm1.Button3Click(Sender: TObject);
begin
player.controls.fastForward();
end;
// ---------------------------------------------------------------------------
// メディア項目の高速の巻き戻しを開始
procedure TForm1.Button4Click(Sender: TObject);
begin
player.controls.fastReverse();
end;
| 時間に関する情報 | TOP |
これは、再生中のメディアがないと意味がない情報です。実際、nilチェックを行わないままで、全くファイルを追加せずにこの処理を行うと、例外が発生してしまいます。
// ---------------------------------------------------------------------------
// 再生中のメディアの時間に関する情報
procedure TForm1.Button1Click(Sender: TObject);
var
s, s1, s2: string;
d1, d2: double;
begin
// 区切り線
ShowMsg(StringOfChar('-', 20));
// 再生中のメディアがあるかどうか
if (player.currentMedia = nil) then
begin
s := '再生中のメディアがありません。';
ShowMsg(s);
end
else
begin
// 現在のメディア項目の秒数で示した再生時間(時分秒)
s1 := player.currentMedia.durationString;
// メディア項目内の現在位置取得(時分秒)
s2 := player.controls.currentPositionString;
// 連結
s := Format('現在位置 %s / 再生時間 %s', [s2, s1]);
ShowMsg(s);
// 現在のメディア項目の秒数で示した再生時間
d1 := player.currentMedia.duration;
// メディア項目内の現在位置取得(変更可能)
d2 := player.controls.currentPosition;
// 連結
s := Format('現在位置 %f 秒 / 再生時間 %f 秒', [d2, d1]);
ShowMsg(s);
end;
// 区切り線
ShowMsg(StringOfChar('-', 20));
end;
次のものは、コンポーネントに標準でついているトラックバーで現在位置を変更した時などに呼ばれるイベント(OnPositionChange)です。元の位置と新しい位置が渡されてきます。どのように使用するのかわからないんですが、強制的に元に戻すとか?
// ---------------------------------------------------------------------------
// 現在位置がマウスとかで変更された(イベント)
procedure TForm1.playerPositionChange(Sender: TObject; oldPosition,
newPosition: Double);
var
s: string;
begin
// 区切り線
ShowMsg(StringOfChar('-', 20));
// 表示用に整形
s := Format('%f 秒 -> %f 秒に変更', [oldPosition, newPosition]);
ShowMsg(s);
// 区切り線
ShowMsg(StringOfChar('-', 20));
end;
| to be continued... | TOP |
予想外に長くなってきたので、その2へ続きます。
| EOF | TOP |