none
Windows Media Player RRS feed

  • 質問

  • OS: Vista Home VS2008 C#

    1つのFormに4つのWMPがあります。1つのMP4またはAVIファイルを同時に再生させようとしていますが、

    微妙に時間がずれます。同時に再生させる、なにかいい方法はないでしょうか?

    今は、URLにファイルを指定し、以下のように再生させてるだけです。

                       axWindowsMediaPlayer1.Ctlcontrols.play();
                    axWindowsMediaPlayer2.Ctlcontrols.play();
                    axWindowsMediaPlayer3.Ctlcontrols.play();
                    axWindowsMediaPlayer4.Ctlcontrols.play();


     

     

    2010年8月25日 7:31

回答

  • >私の知識でできるかわかりませんが、一度、試してみます。

     

    ちょっと環境が無いので動作するかもわかりませんが、以下のような感じではどうですか。

    ※ちょろっと書いただけなんで動作保証はできません、参考として・・・(汗

    /// <summary>
    /// 再生する
    /// </summary>
    private void button1_Click(object sender, EventArgs e)
    {
    	// 同時実行時間を決める
    	DateTime execTime = DateTime.Now.AddSeconds(1);
    
    	// 別スレッドで同時実行する
    	System.Threading.Thread t1 = new System.Threading.Thread(PlaySound);
    	System.Threading.Thread t2 = new System.Threading.Thread(PlaySound);
    	System.Threading.Thread t3 = new System.Threading.Thread(PlaySound);
    	System.Threading.Thread t4 = new System.Threading.Thread(PlaySound);
    	t1.Start(new ThreadArg(this.axWindowsMediaPlayer1, execTime));
    	t2.Start(new ThreadArg(this.axWindowsMediaPlayer2, execTime));
    	t3.Start(new ThreadArg(this.axWindowsMediaPlayer3, execTime));
    	t4.Start(new ThreadArg(this.axWindowsMediaPlayer4, execTime));
    }
    /// <summary>
    /// スレッド引数クラス
    /// </summary>
    private class ThreadArg
    {
    	public AxWMPLib.AxWindowsMediaPlayer wmp;
    	public DateTime execTime;
    	public ThreadArg(AxWMPLib.AxWindowsMediaPlayer arg1, DateTime arg2)
    	{
    		wmp = arg1;
    		execTime = arg2;
    	}
    }
    /// <summary>デリゲート</summary>
    delegate void PlaySoundDelegate();
    
    /// <summary>再生メソッド</summary>
    private void PlaySound(object arg)
    {
    	ThreadArg threadArg = (ThreadArg)arg;
    	// 実行時刻になったら再生
    	while(true)
    	{
    		if (DateTime.Now >= threadArg.execTime)
    		{
    			Invoke(new PlaySoundDelegate(threadArg.wmp.Ctlcontrols.play));
    			Console.WriteLine(threadArg.wmp.ToString());
    			break;
    		}
    	}
    }
    
    

    • 回答としてマーク co-lab 2010年8月26日 6:17
    2010年8月26日 1:21
  • ここは発想を変えて以下の方法もありだと思います。

    1.WPF と Windows Froms の相互運用で、WPF のコントロールを作り Form に貼りつける。
    2.メディアの再生は WPF の MediaElement を使用する。
    3. MediaElement は一つだけ使い、後は ViaualBrush を使って MediaElement の再生状況を描画する。

    これでほぼ同期が取れます。(正確に言えば見せかけているだけ)
    WPF と WindowsForms の相互運用に関しては以下を参考にしてください。

    http://d.hatena.ne.jp/hilapon/20100603/1275583609

    ViaualBrush の使い方に関しては、エッセンシャル WPF を参考にしています。

    以下 サンプルコード、4つのコントロール(MediaElement ×1 、Rectangle × 3) で一つのメディアを同期再生しています。

    xaml

    <Window x:Class="MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="MainWindow" Height="260" Width="430">
      <Grid>
        <MediaElement Height="100" Width="140" HorizontalAlignment="Left" Name="MediaElement1" 
               VerticalAlignment="Top" LoadedBehavior="Manual" UnloadedBehavior="Manual"
               Source="C:\WINDOWS\Help\Tours\WindowsMediaPlayer\Video\copycd.wmv" />
        <Button Content="Button" Height="30" 
            HorizontalAlignment="Left" Margin="313,0,0,0" 
            Name="Button1" VerticalAlignment="Top" Width="90" />
        <Rectangle Height="100" HorizontalAlignment="Left" Margin="0,114,0,0" 
              Name="Rectangle1" Stroke="Black" VerticalAlignment="Top" Width="140">
          <Rectangle.Fill>
            <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" />
          </Rectangle.Fill>
        </Rectangle>
        <Rectangle Height="100" HorizontalAlignment="Left" Margin="162,0,0,0" 
              Name="Rectangle2" Stroke="Black" VerticalAlignment="Top" Width="140">
          <Rectangle.Fill>
            <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" />
          </Rectangle.Fill>
        </Rectangle>
        <Rectangle Height="100" HorizontalAlignment="Left" Margin="162,114,0,0" 
              Name="Rectangle3" Stroke="Black" VerticalAlignment="Top" Width="140">
          <Rectangle.Fill>
            <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" />
          </Rectangle.Fill>
        </Rectangle>
      </Grid>
    </Window>
    


    vb (C# でなくてすみませんが・・)

    Public Class MainWindow
    
      Private Sub Button1_Click(
        ByVal sender As Object, ByVal e As RoutedEventArgs) Handles Button1.Click
    
        MediaElement1.Play()
    
      End Sub
    
    End Class
    
    

     


    ひらぽん http://d.hatena.ne.jp/hilapon/
    • 回答としてマーク 山本春海 2010年8月27日 5:58
    2010年8月26日 3:07
    モデレータ

すべての返信

  • 再生する為の各MediaPlayerコントロールのPlayメソッドが1,2,3,4と順に

    呼び出されているので微妙にズレるのではないかと思います。

     

    それぞれのPlayメソッドを別スレッドで呼び出すようにし、かつPlayメソッドを

    実行するタイミングを同時刻にすることで(ループで時刻をチェックして)

    ある程度ズレは解消されるかもしれませんが、ちょっと手元に検証できる

    環境がないので(音が出せない・・・)、あくまで提案として検討ください。

    2010年8月25日 8:51
  • 返信、ありがとうございました。

    私の知識でできるかわかりませんが、一度、試してみます。

    2010年8月26日 0:26
  • >私の知識でできるかわかりませんが、一度、試してみます。

     

    ちょっと環境が無いので動作するかもわかりませんが、以下のような感じではどうですか。

    ※ちょろっと書いただけなんで動作保証はできません、参考として・・・(汗

    /// <summary>
    /// 再生する
    /// </summary>
    private void button1_Click(object sender, EventArgs e)
    {
    	// 同時実行時間を決める
    	DateTime execTime = DateTime.Now.AddSeconds(1);
    
    	// 別スレッドで同時実行する
    	System.Threading.Thread t1 = new System.Threading.Thread(PlaySound);
    	System.Threading.Thread t2 = new System.Threading.Thread(PlaySound);
    	System.Threading.Thread t3 = new System.Threading.Thread(PlaySound);
    	System.Threading.Thread t4 = new System.Threading.Thread(PlaySound);
    	t1.Start(new ThreadArg(this.axWindowsMediaPlayer1, execTime));
    	t2.Start(new ThreadArg(this.axWindowsMediaPlayer2, execTime));
    	t3.Start(new ThreadArg(this.axWindowsMediaPlayer3, execTime));
    	t4.Start(new ThreadArg(this.axWindowsMediaPlayer4, execTime));
    }
    /// <summary>
    /// スレッド引数クラス
    /// </summary>
    private class ThreadArg
    {
    	public AxWMPLib.AxWindowsMediaPlayer wmp;
    	public DateTime execTime;
    	public ThreadArg(AxWMPLib.AxWindowsMediaPlayer arg1, DateTime arg2)
    	{
    		wmp = arg1;
    		execTime = arg2;
    	}
    }
    /// <summary>デリゲート</summary>
    delegate void PlaySoundDelegate();
    
    /// <summary>再生メソッド</summary>
    private void PlaySound(object arg)
    {
    	ThreadArg threadArg = (ThreadArg)arg;
    	// 実行時刻になったら再生
    	while(true)
    	{
    		if (DateTime.Now >= threadArg.execTime)
    		{
    			Invoke(new PlaySoundDelegate(threadArg.wmp.Ctlcontrols.play));
    			Console.WriteLine(threadArg.wmp.ToString());
    			break;
    		}
    	}
    }
    
    

    • 回答としてマーク co-lab 2010年8月26日 6:17
    2010年8月26日 1:21
  • そもそも、同期機構が無いコントロールで完全に同期させることはできないと考えるべきです

    その上で可能な限り同期させようとした場合に、
    わたしの記憶では使用されている WMP のライブラリが「AxWMPLib」である場合、
    メディアファイルの読み込みは play メソッドを実行した際に開始されたと思ったので、
    ただ単純に play メソッドを列挙しただけでは、各ファイルの読み込みにかかった時間+αずつズレが生じていきます

    そのため、play メソッドでファイルを読み込み後に一旦再生を停止しておき、
    すべての play メソッドを実行後にスレッドを使うなりして一斉に再生を再開することで、
    再生時間のズレをある程度は解消できるかもしれません

    2010年8月26日 1:21
  • >そもそも、同期機構が無いコントロールで完全に同期させることはできないと考えるべきです

     

    そうですね、今回の場合少しのズレもNGというのであれば、

    C++なりで同時再生する音声を合成して一つの音声にし、

    それを再生デバイスにバッファリングして再生する、という方法が望ましいような気がします。

    ちなみに上記のような方法をとれば、作り方次第でMTRのようなものも可能ですね。

    2010年8月26日 1:44
  • ここは発想を変えて以下の方法もありだと思います。

    1.WPF と Windows Froms の相互運用で、WPF のコントロールを作り Form に貼りつける。
    2.メディアの再生は WPF の MediaElement を使用する。
    3. MediaElement は一つだけ使い、後は ViaualBrush を使って MediaElement の再生状況を描画する。

    これでほぼ同期が取れます。(正確に言えば見せかけているだけ)
    WPF と WindowsForms の相互運用に関しては以下を参考にしてください。

    http://d.hatena.ne.jp/hilapon/20100603/1275583609

    ViaualBrush の使い方に関しては、エッセンシャル WPF を参考にしています。

    以下 サンプルコード、4つのコントロール(MediaElement ×1 、Rectangle × 3) で一つのメディアを同期再生しています。

    xaml

    <Window x:Class="MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="MainWindow" Height="260" Width="430">
      <Grid>
        <MediaElement Height="100" Width="140" HorizontalAlignment="Left" Name="MediaElement1" 
               VerticalAlignment="Top" LoadedBehavior="Manual" UnloadedBehavior="Manual"
               Source="C:\WINDOWS\Help\Tours\WindowsMediaPlayer\Video\copycd.wmv" />
        <Button Content="Button" Height="30" 
            HorizontalAlignment="Left" Margin="313,0,0,0" 
            Name="Button1" VerticalAlignment="Top" Width="90" />
        <Rectangle Height="100" HorizontalAlignment="Left" Margin="0,114,0,0" 
              Name="Rectangle1" Stroke="Black" VerticalAlignment="Top" Width="140">
          <Rectangle.Fill>
            <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" />
          </Rectangle.Fill>
        </Rectangle>
        <Rectangle Height="100" HorizontalAlignment="Left" Margin="162,0,0,0" 
              Name="Rectangle2" Stroke="Black" VerticalAlignment="Top" Width="140">
          <Rectangle.Fill>
            <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" />
          </Rectangle.Fill>
        </Rectangle>
        <Rectangle Height="100" HorizontalAlignment="Left" Margin="162,114,0,0" 
              Name="Rectangle3" Stroke="Black" VerticalAlignment="Top" Width="140">
          <Rectangle.Fill>
            <VisualBrush TileMode="None" Visual="{Binding ElementName=MediaElement1}" />
          </Rectangle.Fill>
        </Rectangle>
      </Grid>
    </Window>
    


    vb (C# でなくてすみませんが・・)

    Public Class MainWindow
    
      Private Sub Button1_Click(
        ByVal sender As Object, ByVal e As RoutedEventArgs) Handles Button1.Click
    
        MediaElement1.Play()
    
      End Sub
    
    End Class
    
    

     


    ひらぽん http://d.hatena.ne.jp/hilapon/
    • 回答としてマーク 山本春海 2010年8月27日 5:58
    2010年8月26日 3:07
    モデレータ
  • ありがとうございました。

    各Threadで再生できるようになりました。

    シングルスレッドのときは たまに「xxx.exeはハンドルされませんでした」とエラーになっていたのですが、

    それが解消されました!勉強にもなりました。

    しかし、ほんの少しずれがあります。難しいですね~。CPUの性能とかもあるんですかね。。。

    いろいろ試してみます。下記ひらぽんさんの方法もかなり難しそうですが、おもしろそうです。

    2010年8月26日 6:16
  • ありがとうございます。

    WPFですか~聞いたことがあるくらいですが。

    調べながら、試せたら試してみます。

     

     

    2010年8月26日 6:20
  • ひらぽんさん

    真似しただけですけどできました。

    同期してます!!!感動です!

    みなさんご教授ありがとうございました。 C# is Good!

     

    MediaElementのソース部分を書き換えたりもできるのでしょうか?

    Source
    =
    "xxx"
    

     

    2010年8月26日 8:18
  • MediaElementのソース部分を書き換えたりもできるのでしょうか?
    Source = "xxx"

    可能です。XAML で指定してもいいですし、コードレベルでも設定可能です。コードだと

    MediaElement1.Source = new System.Uri("hogehoge");

    のような書き方になります。詳しくは MediaElement.Source プロパティのドキュメントを見てください。
    あと、メディアの再生・停止・一時停止をマニュアルで操作したいなら MediaElement.LoadedBehavior プロパティと MediaElement.UnloadedBehavior プロパティのドキュメントも合わせて読んでおくといいかも知れません。


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2010年8月26日 15:15
    モデレータ