使ってみよう! Live Framework

第9回 Mesh-Enabled Web アプリケーション ―― 共有メディアプレイヤー

この記事を読むのに必要な時間:およそ 7.5 分

プレイヤー操作の同期

作成したメディアプレイヤーは,アプリケーションを共有しているユーザーが同時に使用していたとしても独立して動くだけです。アプリケーションを共有している場合,その操作が同期するように改良してみましょう。

操作の同期には,アプリケーションのデータ保存領域として利用できるData FeedとData Entryリソースを使用します。以下のクラスを用意し,メディアプレイヤーに対する操作をひとつのオブジェクトとして表します。

Public Class PlayerCommand
    Private _command As String = 
    Public Property Command() As String
        Get
            Return _command
        End Get
        Set(ByVal value As String)
            _command = value
        End Set
    End Property

    Private _argument As String = 
    Public Property Argument() As String
        Get
            Return _argument
        End Get
        Set(ByVal value As String)
            _argument = value
        End Set
    End Property
End Class

Commandプロパティに「play」などの操作を表す文字列,ArgumentプロパティにはメディアファイルのURLなどの値を設定して使います。本記事では再生を表す「play」コマンドしか使用しませんが,拡張した場合にも利用できるようにとこのようにしています。このクラスのひとつのオブジェクトを,ひとつのData Entryの値としてMesh上に保存します。

Data EntryはData Feed内に格納する必要があります。アプリケーション自身のData FeedコレクションにひとつのData Feedを用意し,ここにData Entryを追加します。

メディアファイルの再生などの処理は,Data Feed内のData Entryに対する変更通知を受けたっときに行います。Data Feed内には複数のData Entryが存在する可能性があるため,最新のData Entryを参照するものとします。Data FeedやData Entryなどの変更通知の受信については第5回でも紹介しています。

ここまでのメディアプレイヤー動作イメージを図5に示します。

図5 操作の同期イメージ

図5 操作の同期イメージ

複数のユーザーが,アプリケーションを共有し,アプリケーションのインスタンスを実行している場合……

  1. ユーザーの操作によりアプリケーションがData Entryを追加します。
  2. Data Entryの追加はすべてのアプリケーションに通知されます。
  3. 通知を受信したアプリケーションは最新のData Entryを参照します。
  4. 最後にプレイヤーに対する操作を実行します。

それではコードを書いていきましょう。まず,Data Feedの追加です。アプリケーション ロード時にData Feedがひとつもない場合は新しく作成し追加します。meshAppLoadedメソッド内を次のように変更します。

Private Sub meshAppLoaded(ByVal o As Object, ByVal e As EventArgs)
    'Mesh application service object is now loaded and usable.

    If meshApp.DataFeeds.Entries.Count = 0 Then
        ' Data Feed がない場合

        ' Data Feed 追加完了時のイベント関連付け
        AddHandler meshApp.DataFeeds.AddCompleted, AddressOf DataFeed_AddCompleted

        ' Data Feed の追加
        Dim feed = New DataFeed("commands")
        meshApp.DataFeeds.AddAsync(feed, Nothing)
    Else
        ' その他イベント関連付け
        AddHandlers()
    End If

    ' ファイル一覧表示
    ShowMediaList()
End Sub

追加処理は非同期のため追加完了時のイベントの関連付けを行っています。また,Data Feedの追加完了後,または既にData Feedがあった場合はData Feedがあることを確認した後に,イベントの関連付けの処理を行います。それぞれ必要なメソッドの内容は次の通りです。

' Data Feed 追加完了
Private Sub DataFeed_AddCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs)
    AddHandlers()
    RemoveHandler meshApp.DataFeeds.AddCompleted, AddressOf DataFeed_AddCompleted
End Sub

' イベント関連付け
Private Sub AddHandlers()
    ' Data Feed 内の Data Entry の変更通知受信
    AddHandler meshApp.DataFeeds.Entries.First.DataEntries.ChangeNotificationReceived, _
        AddressOf DataEntries_ChangeNotificationReceived

    ' Data Feed を最新に更新完了
    AddHandler meshApp.DataFeeds.Entries.First.UpdateCompleted, _
       AddressOf DataFeed_UpdateCompleted

    ' Data Feed 内にData Entry を追加完了 
    AddHandler meshApp.DataFeeds.Entries.First.DataEntries.AddCompleted, _
        AddressOf DataEntry_AddCompleted
End Sub

イベント関連付けは以下の3種類を処理しています。

  • Data Feed内のData Entryの変更通知を受信したときのイベント
  • Data Feedを最新に更新したときのイベント
  • Data Feed内にData Entryを追加完了したときのイベント

Data Entry変更通知受信イベントは,メディアプレイヤーの操作を行うタイミングでしたね。最新のData Entryを取得するためData Feedの更新を行い,その更新完了を知るためにData Feed更新完了イベントも処理しています。

Data Entryの追加処理は非同期で行われますが,現在のライブラリではData Entryに対する追加・削除等の処理完了までその他のData Entryも操作ができないという嫌らしい仕様があります。Data Entryの追加が同時実行されないようData Entry追加完了のイベントも処理しています。

イベント処理の前にボタンクリック時の処理を変更します。クリック時に直接メディアファイルを再生していましたが,ここではPlayerCommandオブジェクトを作成しData Entryの値として設定します。そしてData Feedへ追加のみ行うよう変更します。

Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
    Dim button = DirectCast(sender, Button)
    Dim resource = DirectCast(button.DataContext, DataEntryResource)

    'Player.Source = resource.EditMediaLink
    'Player.Play()

    ' Data Entry 作成
    Dim entry = New DataEntry
    entry.Resource.SetUserData(Of PlayerCommand)( _
        New PlayerCommand With { _
            .Command = "play", _
            .Argument = resource.EditMediaLink.ToString})

    Try
        Monitor.Enter(SyncObject)
        ' Data Entry 追加
        meshApp.DataFeeds.Entries.First.DataEntries.AddAsync(entry, Nothing)
    Catch ex As Exception
        Monitor.Exit(SyncObject)
    End Try

End Sub

Data Entry追加時,追加処理が同時実行されないようにしています。追加完了時のイベント処理は次のようになります。

' Data Entry 追加完了
Private Sub DataEntry_AddCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs)
    Monitor.Exit(SyncObject)
End Sub

Data Entryが変更通知を受けとったとき,Data Feedの更新を行い,Data Feed更新完了後にData Entryを参照し,メディアプレイヤーの操作を処理します。

' Data Entry 変更通知受信
Private Sub DataEntries_ChangeNotificationReceived(ByVal sender As Object, ByVal e As EventArgs)
    ' Data Feed の更新
    meshApp.DataFeeds.Entries.First.UpdateAsync(Nothing)
End Sub

' Data Feed 更新完了
Private Sub DataFeed_UpdateCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs)
    ' 最新の Data Entry の取得
    Dim entries = From entry In meshApp.DataFeeds.Entries.First.DataEntries.Entries _
          Order By entry.Resource.PublishDate Descending
    If entries.Count = 0 Then
        Exit Sub
    End If

    ' PlayerCommand オブジェクト取得
    Dim command = entries.First.Resource.GetUserData(Of PlayerCommand)()

    ' メディアプレイヤーの操作実行
    Select Case command.Command
        Case "play"
            Player.Source = New Uri(command.Argument)
            Player.Play()
        Case Else
            ' Do nothing
    End Select
End Sub

コーディングは以上です。ここまでを実行してみましょう。ボタンをクリックするとメディアファイルが再生されたでしょうか? 見た目からわかる動作は変化ありませんが,内部ではData Entryの追加とその変更通知を経てメディアファイルが再生されています。メディアプレイヤーを別のWebブラウザ等で開くと操作が同期されていることもわかります。

サンプルでは,Data Entryを追加するばかりで削除はしていません。実際にアプリケーションとして洗練させるには,より多くのData FeedやData Entryの管理が必要になります。

著者プロフィール

松江祐輔(まつえゆうすけ)

日本システムウエア株式会社 勤務。現在,ハードウェア設計・検証業務を担当。大学生・大学院生時代はベンチャー企業 有限会社ミレニアムシステムズにプログラマーとして従事。趣味はプログラミング。好きな言語はVisual Basic。Microsoft MVP for Windows Live Platform(Jul 2010 - Jun 2011),Windows Live(Jul 2011 - Jun 2013)。

URL:http://katamari.jp