撮影した動画を専用サーバーに保存し、再生する

2012年1月20日(金)
PROJECT KySS

次に、IchiranPage.xamlを展開して表示される、IchiranPage.xaml.vbをダブルクリックしてリスト4のコードを記述します。

ロジックコードを記述する

リスト4 (IchiranPage.xaml.vb)

Option Strict On
Imports System.Xml.Linq
Partial Public Class IchiranPage
  Inherits PhoneApplicationPage
 
  Public Sub New()
    InitializeComponent()
  End Sub

ページがアクティブになった時の処理

新しいWebClientのインスタンスmyWebClientオブジェクトを作成します。WebClientクラスは、データの送受信用のメソッドを提供するクラスです。
String またはUriとして指定したリソースをダウンロードする、DownloadStringAsyncメソッドで、サーバー上のVideoInfo.xmlをダウンロードします。キャッシュから読み込まないように、常に新しいデータを読み込むよう、引数に現在の時間、分、秒を指定しています。
AddHandlerステートメントで、非同期のリソース ダウンロード操作の完了時に発生する、DownloadStringCompletedイベントに、イベントハンドラを追加します。イベントハンドラ内では以下の処理を実行します。
ダウンロードが成功しなかった場合は、警告メッセージを出して、処理を抜けます。成功した場合は、XElement.Parseメソッドでダウンロードした結果(resultArgs.Result)を読み込みます。読み込んだ結果はxmldoc変数に格納されます。文字列型の新しいリストであるvideoListオブジェクトを作成します。
Descendantsメソッドで、子孫要素である全ての <fileName> 要素のコレクションに対して、各要素を変数 result に格納しながら、リストであるvideoListオブジェクトにAddメソッドで、<filenNme>要素の値を追加していきます。ListBoxのItemsSourceプロパティにvideoListオブジェクトを指定します。これで、mp4ファイルの一覧がリストボックスに表示されます。
  Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs)
    Dim myWebClient As New WebClient
 
    AddHandler myWebClient.DownloadStringCompleted, Sub(resultSender As Object, resultArgs As DownloadStringCompletedEventArgs)
        If resultArgs.Error Is Nothing = False Then
                MessageBox.Show("XMLファイルが見つかりません")
                Exit Sub
        Else
                Dim xmldoc As XElement = XElement.Parse(resultArgs.Result)
                Dim videoList As New List(Of String)
                For Each result In From c In xmldoc.Descendants("fileName") Select c
                     videoList.Add(result.Value)
                Next
                  ListBox1.ItemsSource = videoList
        End If
      End Sub
    myWebClient.DownloadStringAsync(New Uri(String.Format(ユーザーの専用サーバーURL/VideoFileUpload/VideoData/VideoInfo.xml?myTime={0}", DateTime.Now.ToLongTimeString), UriKind.Absolute))
    MyBase.OnNavigatedTo(e)
  End Sub

ListBoxより任意のファイル名が選択された時の処理

ListBoxより選択された項目のインデックス番号を引数にして、VideoPlayerPage.xamlに遷移します。
  Private Sub ListBox1_SelectionChanged(sender As Object, e As System.Windows.Controls.SelectionChangedEventArgs) Handles ListBox1.SelectionChanged
    NavigationService.Navigate(New Uri(String.Format("/VideoPlayerPage.xaml?Index={0}", ListBox1.SelectedIndex.ToString), UriKind.Relative))
  End Sub
End Class

次に、VideoPlayerPage.xamlを展開して表示される、VideoPlayerPage.xaml.vbをダブルクリックしてリスト5のコードを記述します。

ロジックコードを記述する

リスト5 (VideoPlayerPage.xaml.vb)

Option Strict On
Imports System.Xml.Linq
Partial Public Class VideoPlayerPage
  Inherits PhoneApplicationPage
 
  Public Sub New()
    InitializeComponent()
  End Sub
  Dim myIndex As Integer

[再生]ボタンがタップされた時の処理

IchiranPage.xamlから渡された文字データを受け取ります。文字データはNavigationContextのQueryStringにDictionary として提供されます。受け取ったインデックスを数値に変換して、変数myIndexに格納しておきます。
新しいWebClientのインスタンスmyWebClientオブジェクトを作成します。WebClientクラスは、データの送受信用のメソッドを提供するクラスです。
String またはUriとして指定したリソースをダウンロードする、DownloadStringAsyncメソッドで、サーバー上のVideoInfo.xmlをダウンロードします。キャッシュから読み込まないように、常に新しいデータを読み込むよう、引数に現在の時間、分、秒を指定しています。
AddHandlerステートメントで、非同期のリソース ダウンロード操作の完了時に発生する、DownloadStringCompletedイベントに、イベントハンドラを追加します。イベントハンドラ内では以下の処理を実行します。
ダウンロードが成功しなかった場合は、警告メッセージを出して、処理を抜けます。成功した場合は、XElement.Parseメソッドでダウンロードした結果(resultArgs.Result)を読み込みます。読み込んだ結果はxmldoc変数に格納されます。
変数myIndexのインデックスに位置する<fileName>要素の値を、変数videoに格納しておきます。MediaElementのSourceプロパティに、ユーザーの専用サーバー保存され、変数videoに格納されているmp4ファイルを指定します。Playメソッドでビデオを再生します。[中止]ボタンの使用を可能にし、[この動画をサーバーから削除]ボタンは使用不可のままです。
  Private Sub playButton_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles playButton.Click
    Dim myParam As IDictionary(Of String, String) = NavigationContext.QueryString
    Dim myIndex As Integer = Integer.Parse(myParam("Index"))
    
    Dim myWebClient As New WebClient
 
    AddHandler myWebClient.DownloadStringCompleted, Sub(resultSender As Object, resultArgs As DownloadStringCompletedEventArgs)
        If resultArgs.Error Is Nothing = False Then
           MessageBox.Show("XMLファイルが見つかりません")
           Exit Sub
        Else
           Dim xmldoc As XElement = XElement.Parse(resultArgs.Result)
           Dim video = xmldoc.Descendants("fileName")(myIndex).Value
        MediaElement1.Source = New Uri(ユーザーの専用サーバーURL/VideoFileUpload/VideoData/" & video, UriKind.Absolute)
                      MediaElement.Play
           End If
      End Sub
  
  myWebClient.DownloadStringAsync(New Uri(String.Format(ユーザーの専用サーバーURLVideoFileUpload/VideoData/VideoInfo.xml?myTime={0}", DateTime.Now.ToLongTimeString), UriKind.Absolute))
    stopButton.IsEnabled = True
    deleteButton.IsEnabled = False
  End Sub

[中止]ボタンがタップされた時の処理

Stopメソッドで再生を中止します。[この動画をサーバーから削除]ボタンの使用を可能にします。[中止]ボタンを[休止]にしたい場合は、Tips集第3弾の「第1回 Videoを撮って分離ストレージに保存し、再生する」を参考にしてください。
  Private Sub stopButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles stopButton.Click
    MediaElement1.Stop()
    deleteButton.IsEnabled = True
  End Sub

[この動画をサーバーから削除]ボタンがタップされた時の処理

削除確認のメッセージを出し、[OK]の場合の処理です。
新しいWebClientのインスタンスmyWebClientオブジェクトを作成します。WebClientクラスは、データの送受信用のメソッドを提供するクラスです。
HeadersのContentTypeにvideo/mpegと指定します。
UploadStringAsyncメソッドで、文字列にしたmyIndexの値をサーバーにアップロードし、サーバー上のDefault2.aspx(作成方法は後述)を実行します。UploadStringAsyncメソッドは、指定したリソースに指定した文字列(この場合、myIndex.ToStringの値)をアップロードします。
AddHanderステートメントで、非同期の文字列アップロード操作の完了時に発生する、UploadStringCompletedイベントに、イベントハンドラを追加します。イベントハンドラ内では、削除した旨のメッセージを表示します。
MainPage.xamlに遷移します。
  Private Sub deleteButton_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles deleteButton.Click
    Dim kakunin = MessageBox.Show("この画像を削除しますか?", "削除確認", MessageBoxButton.OKCancel)
 
    Select Case kakunin
      Case MessageBoxResult.OK
        Dim myWebClient As New WebClient
        myWebClient.Headers(HttpRequestHeader.ContentType) = "video/mpeg"
 
        AddHandler myWebClient.UploadStringCompleted, Sub(resultSender As Object, resultArgs As UploadStringCompletedEventArgs)
                              MessageBox.Show("削除しました")
                   End Sub
        myWebClient.UploadStringAsync(New Uri(ユーザーの専用サーバーのURL/VideoFileUpload/Default2.aspx", UriKind.Absolute), myIndex.ToString)
        NavigationService.Navigate(New Uri("/MainPage.xaml", UriKind.Relative))
      Case Else
        Exit Sub
    End Select
  End Sub

ビデオが最後まで再生された場合の処理

[この動画をサーバーから削除]ボタンの使用を可能にします。[中止]ボタンの使用を不可とします。
  Private Sub MediaElement1_MediaEnded(sender As Object, e As System.Windows.RoutedEventArgs) Handles MediaElement1.MediaEnded
    deleteButton.IsEnabled = True
    stopButton.IsEnabled = False  
  End Sub
End Class

ASP.NETページの作成(VideoFileUpload)

VS2010のメニューから、[ファイル(F)/新規作成(N)/Webサイト(W)]と選択し、表示される画面から「ASP.NET Webサイト」を選択します。「webの場所(L)」に今回は「フォルダ名\VideoFileUpload」と指定し[OK]ボタンをクリックします。

ソリューションエクスプローラー内にVideoDataというフォルダを作成し、というルート要素だけのVideoInfo.xmlを作成しておきます。このファイルを作成していないとエラーになりますので、注意してください。

VS2010メニューの[Webサイト(S)/参照の追加(R)]と選択して、System.Xml.Linqを追加しておいてください。

ソリューションエクスプローラー内のDefault.aspxを展開して表示される、Default.aspx.vbにリスト5のコードを記述します。
※このコードをサーバーに配置した際の、アクセス権の設定やIISの設定は各自が行ってください。

ロジックコードを記述する

リスト5 (Default.aspx.vb)

Option Strict On
Imports System.IO
Imports System.Xml
Partial Class _Default
  Inherits System.Web.UI.Page

ページが読み込まれた時の処理

Windows Phoneのプログラムから送られたfileNameを受け取って、変数fileNameに格納します。
サーバーの物理パスとVideoDataフォルダとfileNameを連結して、変数filePathに格納しておきます。
FileStreamクラスの変数streamを用意し、File.Openメソッドで、filePathに指定したファイルを、Createモードで開き、streamで参照します。Createでは、新しいファイルが作成されます。既にファイルが存在する場合は上書きされます。5120バイトで初期化されたByte型の配列変数bufferを宣言します。POSTされたデータ(Request.InputStream)を取得し、Readメソッドで、リソースに格納されているストリームを読み取りFileStreamにWriteメソッドで書き込みます。これで、現在の「年月日時間分秒.mp4」ファイルが作成されます。InlineAssignHelperヘルパー関数(後述)を使っています。ここで、mp4のビデオファイルが作成されます。次に、作成されたビデオファイルのファイル名を記録する処理を行います。
XElement.LoadメソッドでVideoDataフォルダ内のVideoInfo.xmlを読み込みます。Visual Basic の埋め込み式を用いて、<fileName>要素を作成し、内容テキストとして、埋め込み式の構文である <%= expression %>を用いて変数fileNameの値を指定します。生成したXMLを読み込んだVideoInfo.xmlに追加します。Saveメソッドで保存します。このVideoInfo.xmlファイルが保存された.mp4ファイル名の一覧を記録したファイルです。
  Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    Dim fileName As String = Context.Request.QueryString("fileName").ToString
    Dim filePath As String = Server.MapPath("./") & "VideoData/" & fileName
 
    Using stream As FileStream = File.Open(filePath, FileMode.Create)
      Dim buffer As Byte() = New Byte(5120) {}
      Dim myByteRead As Integer
 
      While (InlineAssignHelper(myByteRead, Context.Request.InputStream.Read(buffer, 0, buffer.Length))) <> 0
        stream.Write(buffer, 0, myByteRead)
      End While
 
      stream.Flush()
      stream.Close()
      Response.Flush()
    End Using
 
    Dim xmldoc As XElement = XElement.Load(Server.MapPath("./") & "VideoData/VideoInfo.xml")
    Dim addXml As XElement = <fileName><%= fileName %></fileName>
    xmldoc.Add(addXml)
    xmldoc.Save(Server.MapPath("./") & "VideoData/VideoInfo.xml")
  End Sub

MSDNのドキュメントで下記のように定義されているInlineAssignHelperヘルパー関数

  Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
    target = value
    Return value
  End Function
End Class

次にVS2010メニューの[Webサイト(S)/新しい項目の追加(W)]と選択して、「Webフォーム」を作成します。「名前(N)」はDefault2.aspxのままにしておきます。ソリューションエクスプローラー内のDefault2.aspxを展開して表示される、Default2.aspx.vbにリスト6のコードを記述します。

このDefault2.aspx.vbは、Windows Phoneで選択されたビデオファイルを削除する処理です。

ロジックコードを記述する

リスト6 (Default2.aspx.vb)

Option Strict On
Imports System.IO
Imports System.Xml.Linq
Partial Class Default2
  Inherits System.Web.UI.Page

ページが読み込まれた時の処理

StreamReaderクラスでPOSTされたデータ(Request.InputStream)を取得します。StreamReaderクラスは、特定のエンコーディングのバイトストリームを読み込むTextReader を実装するクラスです。
取得したデータの内容をReadToEndメソッドで読み取り、変数readStrに格納しておきます。readStrの値を数値に変換してmyIndex変数に格納します。readStrにはWindows Phoneから送られた、選択されたビデオファイルのインデックスが格納されています。
インデックスに該当する.mp4ファイルと、VideoInfo.xml内の該当するビデオファイルの記録されている、<fileName>要素と内容を削除します。削除したVideoInfo.xmlをSaveメソッドで保存します。
  Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    Dim reader As New StreamReader(Me.Request.InputStream(), System.Text.Encoding.UTF8)
    Dim readStr As String = reader.ReadToEnd
    Dim myIndex As Integer = Integer.Parse(readStr)
 
    Dim doc As XElement = XElement.Load(Server.MapPath("./") & "VideoData/VideoInfo.xml")
    Dim deleteData = doc.Descendants("fileName")(myIndex)
    Dim delImage As String = doc.Descendants("fileName")(myIndex).Value
    File.Delete(Server.MapPath("./") & "VideoData/" & delImage)
    deleteData.Remove()
    doc.Save(Server.MapPath("./") & "VideoData/VideoInfo.xml")
  End Sub
End Class
  • 「撮影した動画を専用サーバーに保存し、再生する」サンプルプログラム

四国のSOHO。薬師寺国安(VBプログラマ)と、薬師寺聖(デザイナ、エンジニア)によるコラボレーション・ユニット。1997年6月、Dynamic HTMLとDirectAnimationの普及を目的として結成。共同開発やユニット名義での執筆活動を行う。XMLおよび.NETに関する著書や連載多数。最新刊は「Silverlight実践プログラミング」両名とも、Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。http://www.PROJECTKySS.NET/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています