卒業論文 2004 年度(平成 16 年度)
JPEG2000 を用いた最適動画像配信システムの設計と実装
慶應義塾大学 環境情報学部 氏名 石神 靖弘
指導教員
慶應義塾大学 環境情報学部 村井純
徳田英幸
中村修
楠本博之
Abstract(Japanese)
Abstract(English)
目次
第1章 序論:動画像配信と使用帯域の制御
1.1 背景:ネットワークの広帯域化と動画像配信の一般化 1.2 問題意識:フレーム内圧縮とDV
1.3 研究の目的:より柔軟な輻輳制御を行うシステムの構築 1.3 論文の構成
第2章 本研究における要素技術
2.1 IPネットワーク上における映像・音声転送 2.1.1 ストリーミング
2.1.2 RTP 2.1.3 RTCP 2.2 JPEG2000
2.3 DirectX
第3章 既存技術:DVTS 3.1 DVTSの概要 3.2 フォーマットの特徴 3.3 パケットフォーマット 3.4 DVTSの帯域削減手法 3.4 DVTSの問題点
第4章 より柔軟な帯域制御手法の提案
4.1 圧縮率変更による使用帯域の削減 4.2 フレーム間引きとの併用
第5章 設計
5.1 設計要件 5.2 設計概要
5.3 IMediaSampleインターフェイス 5.4 送信部
5.4.1 送信部設計概要
5.4.2 Network Sendフィルタの設計
5.4.2.2 IPネットワークへの送信 5.5 受信部
5.5.1 受信部設計概要
5.5.2 Network Recvフィルタの設計 5.5.2.1 基底クラス
5.5.2.2 IPネットワークからのデータ受信 5.6 制御部
第6章 実装
6.1 実装環境
6.2 フィルタ間のデータの受け渡し 6.3 送信部分フィルタ
6.3.1 CNetworkSendクラス 6.3.2 UDP Socketの作成
6.3.3 マーカ・マーカセグメントの解析
6.3.4 IPパケット送信 6.4 受信部分Filter
第7章 評価
7.1 評価概要
7.2 本システムの実現した機能 7.3 送出データ量の計測 7.4 より柔軟な輻輳制御の実現
7.4.1 実験: 帯域幅の不足 7.4.2 実験: 遅延時間 7.5 まとめ
第8章 結論
8.1 まとめ 8.2 今後の課題 8.3 展望と応用
第 1 章 序論:動画像配信と使用帯域の制御
1.1 背景:ネットワークの広帯域化と動画像配信の一般化
ネ ッ ト ワ ー ク は 広 帯 域 化 し た 。 バ ッ ク ボ ー ン ネ ッ ト ワ ー ク に お い て は Gigabit Ethernet(IEEE802.11ab)や10Gigabit Ethernet(IEEE802.11ae)が用いられるようなった。
また、家庭においてはADSL(Asymmetric Digital Subscriber Line)やFTTH(Fiber To The Home)が普及し、一般のユーザも高速な通信環境を享受できるようになった。
それに伴い、インターネットを介した動画像配信が一般的なものとなりつつある。例え ば、映画などのコンテンツの商用配信や教育目的での映像の配信が行われるようになった。
また、個人でも手軽に動画像配信が行えるようになり、MSN Messengerでは、USB接続 のカメラを接続するだけで、動画像によるコミュニケーションを楽しむことができる。
1.2 問題意識:フレーム内圧縮とDV
動画像の符号化方式には、フレーム間圧縮とフレーム内圧縮とがある。前者はフレーム とフレームの差分をとり、その差分を圧縮することによって、画像のデータ量を削減する。
しかし、フレーム間の差分を圧縮するため、パケットロスなどのエラーに極めて弱いとい う本質的な欠点がある。後者はフレームを逐一静止画のように圧縮する。フレーム間圧縮 に比べて、画像のサイズが大きくなってしまうが、エラーに強いという利点がある。
フレーム内圧縮の代表的な映像フォーマットとして、DV フォーマットがある。しかし、
DVフォーマットでは圧縮率の変更ができない。したがって、使用帯域の削減や輻輳制御を 行うためには、フレームを間引く必要がある。だが、フレームを間引くと、動きがかくか くとした不自然な映像になってしまうという問題点がある。
1.2 研究の目的:より柔軟な輻輳制御を行うシステムの構築
本研究では、フレーム内圧縮のフォーマットである JPEG2000 を用いる。すでに
JPEG2000 フォーマットでの配信は行われているが、専用の機器を必要とし、いずれも高
価である。また、JPEG2000の本来の規格に正確に準拠していないことが多い。
本研究の目的は、JPEG2000 フォーマットを用いた、安価かつ手軽で、より柔軟な輻輳 制御を意識した動画像配信を行うシステムを構築することである。したがって、フレーム 間引きと複数の圧縮率の変更に対応することが不可欠である。
1.3 論文の構成
本論文は8章から構成されている。第2章では、本研究の要素技術であるIPネットワー
り上げ、既存技術であるDVTSとの比較を行い、既存技術の問題点を明確化する。第4章 では、より柔軟な輻輳制御手法についての提案を述べる。第 5 章では本研究において実装 したアプリケーションの設計を説明する。第6章では、本研究の実装について述べる。第7 章では、評価を行い、既存技術の問題点を克服することができたか否かを判断する。第 8 章では、本研究を総括し、今後の展望について述べる。
第 2 章 本研究における要素技術
本研究を構成する技術として、本章では IP ネットワーク上における音声・映像転送、
JPEG2000、DirectXについての概要を述べる。
2.1 IPネットワーク上における音声・映像転送 2.1.1 ストリーミング
IP ネットワーク上における音声・映像転送の方法として、ダウンロード方式とストリー ミング方式がある。ダウンロード方式は、映像・音声データをいったん全て受信し、ハー ドディスクに保存してから再生する方式である。一方、ストリーミング方式は映像・音声デ ータを受信しながら、再生を行う方式である。一般に、すでに蓄積されたコンテンツの配 信ではダウンとロード方式が、リアルタイムコンテンツの配信ではストリーミング方式が 採用される。
2.1.2 RTP
ストリーミング方式での転送においては、伝送プロトコルとして主に UDP(User Datagram Protocol)が用いられる。UDPはTCP(Transmission Control Protocol)のよ うに、輻輳制御機能やフロー制御機能を有しない。また、パケットの到着順序にも関知せ ず、パケットの再送も行わない。従って、ネットワーク上でのパケットロスや輻輳に対し ては、アプリケーション側で対応する必要がある。
RTP(Real-Time Transport Protocol)は、UDPを用いたリアルタイムコンテンツの配信時 に通常用いられるプロトコルである。個々のパケットにシーケンス番号やタイムスタンプ を付加することにより、パケットの到達順序の検査やパケットロスの検知といったパケッ トの識別に利用される。なお、RTP そのものはパケットの到達順序を保障するプロトコル ではなく、あくまでパケットの識別を補助するものである。
2.1.3 RTCP
RTCP(Real-Time Transport Control Protocol)はRTPセッションを管理するためのプロ トコルである。RTP により受信側が得たパケットの到達順序やパケットロスの有無、受信 品質、参加者の識別などといった情報をセッションの参加者間で交換する。これらの情報 をもとに、送信側は映像の圧縮率やフレームレートの制御を行い、送信するデータ量の増 減を調整する。
なお、RTCPは、RTPと同様に、データの配送やパケットの到達を保障するのではなく、
2.2 JPEG2000
-概要
JPEG2000(Joint Photographic Experts Group 2000)は、JPEG(Joint Photographic
Experts Group)の30%の圧縮率向上を目標として仕様が策定されたフォーマットである。
JPEGにはないJPEG2000の特徴として、以下の点が挙げられる。
階層符号化
高い圧縮効率
ブロックノイズやモスキートノイズが発生しない
高いエラー耐性
ROI(Region Of Interest)
プログレッシブ表示に対応
また、JPEG2000は周波数変換に離散Cosine変換(DCT: Discrete Cosine Transform)で はなく、離散Wavelet変換(DWT: Discrete Wavelet Transform)を用いている。エントロピ ー 符 号 も Huffman 符 号 で は な く 、 係 数 ビ ッ ト モ デ リ ン グ と 算 術 符 号 を 合 わ せ た EBCOT(Embedded Block Coding with Optimized Truncation)を用いている。
-JPEG画像との比較
下の図は上から非圧縮の画像、JPEG 画像、JPEG2000 画像である。JPEG 画像と
JPEG2000画像はほぼ同じデータ量に合わせてある。JPEG画像とJPEG2000画像とを比
較すると、JPEG画像のほうはブロックノイズが目立ち、画質が大きく劣化している。これ に対して、JPEG2000 画像は現画像のシャープさが失われているものの、原形を留めてい る。
図 Mandrill(Bitmap: 196,662 Byte,256×256)
図 Mandrill(JPEG: 2,082 Byte,256×256)
-エンコードとデコードのプロセス
以下にエンコードとデコードの大まかな流れを示す。なお、JPEG2000 はデコーダの規 格であるため、エンコードのプロセスに関しては一例となる。
図 エンコードの流れ
・DCレベルシフト
入力された信号が符号なし整数であった場合に、符号付き整数に変換する。具体的に は、信号値からダイナミックレンジの半分を減算する。
・Wavelet変換
信号値に対して、Wavelet変換を実行する。
・量子化
Implicit と Explicit という2つの量子化方式からどちらか選択し、量子化を行う。こ
こでの量子化は任意である。
・EBCOT(Embedded Block Coding with Optimized Truncation) ビットモデリングした係数を算術符号化する。
・ビット切捨て
与えられたビットレート以上のデータを切り捨てる
図 デコードの流れ
・Packet解析
Packetのデータ部分を抽出する。
・逆EBCOT
算術符号化されたデータを復号化し、係数ビットデモデリングを行う。
・逆量子化
量子化の逆の操作を行う。量子化に落とされた精度を元の精度へと戻す。
・Wavelet逆変換
復号化された係数に対して、Wavelet逆変換を実行し、画像信号値を得る。
・DCレベル逆シフト
出力される信号値を符号なし整数に変換する。具体的には、信号値にダイナミックレ ンジの半分を加算する。
― コードストリームの構造
JPEG2000のコードストリームは、Packetとして符号化されたデータが格納されたタイ
ルパートとヘッダから構成される。ヘッダにはメインヘッダとタイルパートヘッダがある。
ヘッダはマーカとマーカセグメントから成っている。
マーカは2Byte 長の値であり、コードストリーム内のデータのデリミタやコードストリ ームの特徴を示すものである。マーカセグメントは、1つのマーカとそれに付随するマー カセグメントパラメータをひとくくりにしたものである。下の表に、マーカとマーカセグ メントのリストを示す。
表 マーカセグメントのリスト
Name Code Main header Tile-part header
Delimiting marker segments
Start of codestream SOC 0xFF4F required not allowed
Start of tile-part SOT 0xFF90 not allowed required
Start of data SOD 0xFF93 not allowed last marker
End of codestream EOC 0xFFD9 not allowed not allowed
Fixed information marker segments
Image and time size SIZ 0xFF51 required not allowed
Functional marker segments
Coding style default COD 0xFF52 required optional
Coding style component COC 0xFF53 optional optional
Region of interest RGN 0xFF5E optional optional
Quantization default QCD 0xFF5C required optional
Quantization component QCC 0xFF5D optional optional
Progression order default POD 0xFF5F optional optional
Pointer marker segments
Tile-part lengths, main header TLM 0xFF55 optional not allowed Packet length, main header PLM 0xFF57 optional not allowed Packet length, tile-part header PLT 0xFF58 not allowed optional Packed packet headers, main header PPM 0xFF60 optional not allowed Packed packet headers, tile-part header PPT 0xFF61 not allowed optional
In bit stream marker segments
Start of packet SOP 0xFF91 not allowed optional,
in bit stream End of packet header EPH 0xFF92 not allowed optional,
in bit stream
Informational marker segments
Comment and extension CME 0xFF64 optional optional
マーカとマーカセグメントには以下の6タイプが存在する。
Delimiting
Fixed Information
Functional
Pointer
In bit stream
Informational
このうち、DelimitingとFixed Informationマーカ/マーカセグメントについては、出現位 置と順番が厳密に決められている。以下にコードストリームの構造を示す。
図 コードストリームの構造
2.3 DirestShow
DirectShow は、Windows アプリケーションで高品位な映像・音声の再生やキャプチ
ャといったマルチメディアストリームを扱うAPI群である。以下に、DirectShowを構成す る基本要素について説明する。
― フィルタ
DirectShowのフィルタとは、マルチメディアストリームの処理を個々のステップに分割
した、線形フィルタである。アプリケーションが、グラフ上で、エンコーダやデコーダ、
レンダラといったフィルタを組み合わせることで、多様な処理を行うことができる。
― ピン
フィルタは、少なくとも1つのピンをもつ。DirectShowフィルタのピンは、フィルタ間 でメディアデータの受け渡しを行う部分である。ピンには方向があり、入力と出力の2つ がある。フィルタ同士を接続する際には、入力ピンと出力ピンの間にコネクションを張り、
ピンがサポートしているメディアタイプを互いに照合する。
― フィルタの分類
フィルタには、以下の3つがある。
ソースフィルタ
トランスフォームフィルタ
レンダリングフィルタ
ソースフィルタは未処理のメディアデータを出力するフィルタである。ソースフィルタ には、DVカメラなどの映像ソースやAVIファイルなどのファイルソースがある。トランス フォームフィルタは、入力ピンから受け取ったメディアデータを、圧縮伸張などの変換処 理を行ってから、出力ピンへ渡すフィルタである。トランスフォームフィルタには、エン コード/デコードフィルタや、映像や音声をスプリットするフィルタなどがある。レンダリ ングフィルタは、出力デバイスへ完全に処理したメディアデータを書き込むフィルタであ る。レンダリングフィルタには、映像データを画面上にレンダリングするフィルタや音声 データを再生するフィルタなどがある。
― フィルタグラフ
フィルタグラフはピンによって相互に接続されたフィルタの集合である。フィルタグラ フを組み立てる手段として、アプリケーションが手動でフィルタ間のピンとピンとをつな ぐ方法や、グラフビルダなどによって自動的にフィルタグラフを構築する方法がある。
図 フィルタグラフ
第3章 既存技術:DVTS
本章では、既存技術であるDVTS(Digital Video Transport System)について、概要を述 べ、考察する。
3.1 DVTSの概要
DVTSは、IEEE1394を通じて民生用DVカメラから取得した、DV圧縮された映像デー
タと非圧縮の音声に対して、IPデータグラム化を行い、転送するシステムである。
DVTSの主な機能としては、以下の点が挙げられる。
IPV4/IPV6に対応
RTPによる通信
Multicastによる複数地点への転送
フレーム間引きによる帯域制御
3.2 DVフォーマットの特徴
DVTSでは、映像フォーマットとして、DVフォーマットを採用している。
DVフォーマットの特徴を表に示す。
表 DVフォーマットの特徴
圧縮方式 フレーム内圧縮
解像度 720×480
フレームレート 29.97 fps 使用帯域 約30Mbps
圧縮率 一定
DVフォーマットは基本的にJPEGの技術を踏襲している。JPEG同様、周波数変換には
DCT(Discreet Cosine Transform)を、エントロピー符号化には、Huffman符号を用いて
いる。
3.3 パケットフォーマット
DVTSで使われているDV/RTPパケットフォーマットを図に示す。
図 DV/RTPパケットフォーマット
DVフォーマットでは、映像や音声といったデータは80Byte長のDIF Blockというブロ ックで区切られている。したがって、DVのデータ量は80Byte 単位で変化する。DV/RTP パケットはDIF Blockを複数持つことができ、その数は任意に選択できる。
3.4 DVTSの帯域削減手法
DVTSでは、フレームを間引く、すなわちIPデータグラム化する映像フレーム数を減ら すことで、使用帯域の調整を実現している。
図 DVTSのフレーム間引き
フレーム内圧縮であるDVデータは、フレーム全てが、MPEGのI Pictureに相当するキ
下の図はフレーム間引き率と使用帯域の関係を示したものである。
図 フレーム間引き率と使用帯域
フレーム間引きを行っていないときは 30Mbps 強の帯域を占有しているが、フレームを間 引くごとに使用帯域が減少している。なお、音声の間引きは行っていないため、音声パケ ットが占有する帯域は常に一定である。
3.4 DVTSの問題点
前述したように、DVTSは、フレーム間引きを実施することで、使用帯域の削減を行う。
しかしながら、フレームを間引くと、物体の動きがかくかくとした不自然な映像になって しまう。また、DVフォーマットの圧縮率は一定であるため、常に約30Mbpsの帯域を占有 する。それゆえ、映像品質を落とすことなく、使用帯域の調整を行うことでは不可能であ る。
第
4
章 より柔軟な帯域制御手法の提案 4.1 圧縮率変更による使用帯域の削減 4.2 フレーム間引きとの併用第
5
章 設計本章では、設計について述べる。
5.1 設計要件
本研究における設計要件として、以下の事項がある。
RTPによる通信
DirectShowフィルタ化
フレーム間引きに対応
複数の圧縮率に対応
DVデータのデコード
JPEG2000データのエンコード/デコード
5.2 設計概要
本システムは送信部と受信部から構成され、ともにDirectShowのフィルタとなっている。
DVカメラと計算機間の通信にはIEEE1394を用いて DVデータを取得する。計算機間の
JPEG2000データの受け渡しには、IPを利用し、データの送受信を行う。フィルタ間のデ
ータの受け渡しは、IMediaSampleインターフェイスのインスタンスを用いる。
5.3 IMediaSampleインターフェイス
フィルタ間のデータの受け渡しは、IMediaSample インターフェイスのインスタンスを 通じて行う。IMediaSampleインターフェイスはCOMオブジェクトであり、フィルタ間の 共有メモリバッファとして使われる。したがって、データのサイズ、データが入ったバッ フ ァ へ のポ イ ン タな ど は全 て カプ セ ル化 さ れ 、外 部 から 隠 蔽さ れ てい る 。 次に 、
IMediaSampleインターフェイスの定義を示す。
5.4 送信部
5.4.1 送信部設計概要
送信部は以下の機能要素から構成される。
IEEE1394を通じて、DVカメラからDVデータの取得
DVデータのデコード
JPEG2000データにエンコード
IPネットワークに対してデータを送信
圧縮率やフレームレートの変更に対応
上記の機能要素を、DirectShowフィルタとして構成した場合、送信側のフィルタは以下の 4つになる。
― Microsoft DV Camera and VCR
Microsoft DV Camera and VCR フィルタは、DV カメラを表わす Plug and Play
Deviceフィルタである。DV Vid Outピンから、DVデータを送出する。このフィルタは
DirectShow付属のフィルタである。
― DV Video Decoder
DV Video DecoderフィルタはDVデータをデコードするTransformフィルタである。
入力ピンから受信した DV データをデコードし、非圧縮の映像データを出力ピンへ送出 する。このフィルタはDirectShow付属のフィルタである。
― LEAD MJ2K Encoder
LEAD MJ2K Encoderフィルタは、非圧縮の映像データをJPEG2000データへとエン
コードするフィルタである。このフィルタはLead Technologies社が販売しているもので ある。
― Network Send
Network SendはJPEG2000データを分割し、RTPヘッダを付加して、UDPパケッ
トとして送出するフィルタである。本研究で設計実装する。
図 送信側フィルタグラフ
5.4.2 Network Sendフィルタの設計 5.4.2.1 基底クラス
Network Send フィルタは、基底クラスとして、CBaseRenderer クラスを継承する。
CBaseRendererクラスはレンダリングフィルタを作成する際に用いられる基底クラスであ
る。
5.4.2.2 IPネットワークへの送信
IMediaSampleインターフェイスのオブジェクトによって渡されたデータをIPネットワ
ークへ送信する。そのとき、次の2点を考慮に入れ、データを分割する。
― JPEG2000コードストリームの構造
第2章で述べたとおり、JPEG2000コードストリームはヘッダとタイルパートから構成 される。送信用バッファにデータを格納する時には、このコードストリームの特徴を勘案 する。したがって、ヘッダ部分とデータが入ったタイルパート部分を別パケットで送信す る。
― RTPヘッダ
RTPヘッダをバッファの先頭に付加し、IPネットワーク上へパケットを送出する。図に RTPヘッダフォーマットを示す。
図 RTPヘッダ
シーケンス番号(Sequence Number)はIPパケットごとに1ずつインクリメントする。タ イムスタンプ(Timestamp)は90KHzを単位とする。今回はDVカメラから画像を取得して いるため、3003ずつ増加させる。
次に、ペイロードフォーマットを示す。このペイロードフォーマットは、現在internet draft として公開され、議論されているものである。
図 RTPペイロードフォーマット
tp(type): 2bits
走査型の識別
0: プログレッシブ
1: インターレース 奇数フィールド
2: インターレース 偶数フィールド
3: インターレース 奇数フィールドと偶数フィールドの両方
MHF (Main Header Flag) : 2 bits
ペイロード中のメインヘッダの有無
0: ペイロード中にメインヘッダがない
1: メインヘッダがフラグメント化されている
2: フラグメント化されたメインヘッダの最後
3: ペイロード中にメインヘッダが全て含まれている
メインヘッダをロスした場合に、メインヘッダの復元が可能かどうか
メインヘッダの値が変わるごとに1ずつインクリメントする
T (Tile field invalidation flag) : 1 bit
タイルナンバーの有効性
0: タイルナンバーが有効
ペイロード中に1つのタイルパートしか存在しない
1: タイルナンバーが向こう
複数のタイルパートが1つのペイロードに格納されている
ペイロード中にメインヘッダしかない など
priority : 8 bits
JPEG2000 におけるペイロードの重要度
0: メインヘッダやタイルパートヘッダといった最も重要度が高いもの
1-255: その他(番号が大きくなるほど、重要度が低くなる)
tile number : 16 bits
タイルナンバー
T=0 のとき、タイルナンバーを示す
T=1 のとき、受信側はこのフィールドを無視する
R (Reserved) : 8 bits
予約された領域
常に0
fragment offset : 24 bits
JPEG2000コードストリームの先頭からのオフセットバイト数
5.5 受信部
5.5.1 受信部設計概要
受信部は以下の機能要素から構成される。
IPネットワークからのデータ受信
エラー処理
JPEG2000データをデコード
デバイスにレンダリング
上記の機能要素を、DirectShowフィルタとして構成した場合、受信側のフィルタは以下の 3つになる
Network Recvフィルタは、送信側からIPネットワークを通して送られてきたUDPパ
ケットをJPEG2000データとして再構築するフィルタである。また、後述するが、伝送
時のエラー、例えばパケットロスなどに対処する。本研究で、設計実装する。
― LEAD ML2K Decoder
LEAD ML2K Decoderフィルタは、JPEG2000データを非圧縮の映像データへデコード
するフィルタである。LEAD ML2K Encoder同様、LEAD Technologies社が販売してい るフィルタである。
― Video Renderer
Video Rendererフィルタは、非圧縮の映像データをモニタへレンダリングするフィルタ
である。このフィルタはDirectShow付属のフィルタである。
図 受信側フィルタグラフ
5.5.2 Network Recvフィルタの設計 5.5.2.1 基底クラス
Nerwork Recvフィルタは、基底クラスとしてCSourceクラスとCSourceStreamクラス を継承する。CSourceクラスとCSourceStreamクラスはソースフィルタを作成する際に 用いられる基底クラスである。
5.5.2.2 ネットワークからのデータ受信
受信バッファにデータを受信する。受信バッファのデータを出力ピンの出力バッファに 書き込む前に、次に挙げる項目をチェックする。
― パケットの識別
受け取ったパケットのシーケンス番号をもとに、パケットの到着順序やパケットロスの 有無を調べる。仮に正しい順序でパケットが到達していないことが判明した場合、そパケ ットロスとしてカウントする。
― ペイロードの識別
している。到達優先度が最も高いヘッダが欠落してしまった場合、通常のパケットロスと は異なった対応をとる必要がある。というのは、ヘッダとりわけメインヘッダが欠落して しまった場合、当該フレームのデコードは不可能だからである。
メインヘッダが欠落してしまった場合の対応策として、前に受信したメインヘッダが再 利用可能かどうかを調べて、再利用可能であれば、そのヘッダを欠落したメインヘッダの 代替として用いるということが挙げられる。しかしながら、映像フレームに、タイルパー トが1つしかなく、かつタイルパートヘッダが欠落してしまった場合、ヘッダの補完がで きないため、フレームを破棄しなければならない。
5.6 制御部分
第
6
章 実装本章では、前章で述べた設計に基づいて実装した送信部分・受信部分・制御部分のフィ ルタについて述べる。6.1節で実装の概要について述べ、6.2節以降で、実際に実装 したフィルタについての詳らかな説明を行う。
6.1 実装環境
本研究では送信部分・受信部分・制御部分を全てDirectShow フィルタとして実装する。
また、本研究で用いるJPEG2000のエンコーダとデコーダはVideo for Windowsもしくは
DirectShow上のみ利用できるものである。したがって、フィルタの実装環境にはWindows
OSを選択した。表に実装に用いた環境を示す。
表 ソフトウェア環境
OS Windows XP SP1
開発環境 Visual Studio .NET2003
DirectX DirectX 9.0c
言語 Visual C++ .NET2003
表 JPEG2000のエンコーダ・デコーダ
エンコーダ LEAD Technologies デコーダ LEAD Technologies
表 ハードウェア環境。
CPU Pentium4 3.0GHz
メモリ 1024 MBytes
ハードディスク 120GBytes
NIC 100Base-TX
6.2 フィルタ間のデータの受け渡し
フィルタ間でのデータの受け渡しは、IMediaSample インターフェイスのオブジェクト を使って行う。下の表に、受け渡しの際に用いるメソッドを示す。
メソッド 説明
GetPointer バッファのポインタを取得する
GetSize バッファのサイズを取得する
GetActualDataLength バッファ内の有効データサイズを取得する
SetActualDataLength バッファ内の有効データサイズを設定する
図 データの受け渡しの際に用いるメソッド 実際のデータの書き込みは次のようになる。
//pMediaSample: IMediaSample
インターフェイスのインスタンス//
バッファのポインタを格納するポインタ変数BYTE* pData;
//バッファのポインタを取得
pMediaSample->GetPointer( &pData );
//
バッファへデータをコピーCopyMemory( pData,
コピー元, コピーするサイズ );//実データサイズを格納
pMediaSample->SetActualDataLength(
実データサイズ);
図 データの書き込み
実際のデータの読み込みは次のようになる。
// pMediaSample: IMediaSample
インターフェイスのインスタンス//
バッファのポインタを格納するポインタ変数BYTE* pData;
//実データサイズを格納する変数
LONG lDataSize;
//
バッファのポインタを取得pMediaSample->GetPointer( &pData );
//バッファ内の実データサイズを取得
lDataSize = pMediaSample->GetActualDataLength( );
//
バッファからデータをコピーCopyMemory(
コピー先, pData, lDataSize );図 データの読み込み
6.2 送信部分フィルタ
本節では送信部分のフィルタである network send フィルタの詳細について述べる。
network sendフィルタは、CBaseRendererクラスを継承したCNetworkSendクラスとし て実装される。CNetworkSendクラスの機能は次の通りである。
UDP Socketの作成
マーカ・マーカセグメント解析
IPパケット送信
6.2.1 CNetworkSendクラス
下にCNetworkSendクラスの定義を示す。
class CNetworkSend : public CBaseRenderer {
private:
WSADATA wsaData;
SOCKET sock;
SOCKADDR_IN addrTo;
RTP_HEADER rtp_header;
RTP_PAYLOAD_HEADER_JP2 rtp_payload;
char szSendBuf[1500];
private:
CNetworkSend ( IUnknown* pUnk, HRESULT* phr );
~CNetworkSend ( );
public:
static CUnknown* WINAPI CreateInstance( IUnknown* pUnk, HRESULT* phr );
HRESULT CheckMediaType( const CMediaType* pmt);
HRESULT DoRenderSample( IMediaSample* pMediaSample);
int SeekMarker(LPBYTE pBuf);
};
図 CNetworkSendクラス
CNetworkSend クラスはレンダラの基底クラスである CBaseRenderer クラスを継承し
ている。CBaseRendererクラスは下の図の2つの関数を純粋仮想メソッドとしてもってい る。したがって、CBaseRendererクラスを継承したクラスは最低限この2つの関数をオー バーロードしなければならない。なお、”PURE” は純粋仮想メソッドを表わす “=0” のマ クロである。
virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
virtual HRESULT CheckMediaType(const CMediaType *) PURE;
図 CBaseRendererクラスの純粋仮想メソッド
6.2.2 UDP Socketの作成
CNetworkSend クラスのコンストラクタで、UDP Socket を作成する。いったんメモリ
上に生成されたCNetworkSendフィルタがメモリから開放されるのは、アプリケーション 終了時と、Releaseメソッドの呼び出しにより参照カウントが0になった時のみである。し たがって、コンストラクタで作成されたUDPソケットは終了時まで保持される。
作成したSocketはデストラクタで閉じる。
6.2.3 マーカ・マーカセグメントの解析
ヘッダとタイルパートを分割して送信するために、マーカ・マーカセグメントの解析を行 う。解析対象とするマーカは次の通りである。
SOC
JPEG2000コードストリームの先頭
SOT
タイルパートヘッダの先頭
SOD
タイルパートヘッダの終端
EOC
コードストリームの終端
次にマーカ・マーカセグメント解析のフローチャートを示す。コードストリーム中にタイ ルパートは複数存在してもよいから、EOCマーカに到達するまで、コードストリーム中の タイルパートの検出を行う。マーカセグメントが検出され次第、次節で述べるが、IP パケ ット送信を行う。
LEAD Technologies社のエンコーダが出力するJPEG2000データは、タイルパートが1 つしかない。したがって、実際には繰り返し処理を行うことなく、フレーム終端へ到着す る。
この部分はまだ未実装です。
6.2.4 IPパケット送信
マーカセグメントが検出されるごとに、IP パケット送信を行う。以下にメインヘッダ、
タイルパートヘッダ、ビットストリームごとのRTPペイロードヘッダの値と処理を記す。
― メインヘッダ MHF = 3 priority = 0
メインヘッダのサイズはMTU以下であるため、そのままsendto関数を呼び出し、パケ ットを送信する。
― タイルパートヘッダ MHF = 0
priority = 0
タイルパートヘッダのサイズもMTU以下であるため、そのままsendto関数を呼び出し、
パケットを送信する。
― ビットストリーム MHF = 0
priority = 1以上
ビットストリームのサイズは、通常、MTUよりも大きい。したがって、ビットストリー ム部分は分割して送信する。
6.3 受信部分Filter
本節では受信部分のフィルタである network recv フィルタの詳細について述べる。
network recvフィルタは、CSourceクラスを継承したCNetworkRecvクラスによって実装 される。しかし、データの受信や受け渡しといった主要な処理はnetwork recvフィルタの 出力ピンが行う。出力ピンはCSourceStreamクラスを継承したCNetworkRecvPinクラス によって実装される。
図 network recvフィルタの構成
以下にCNetworkRecvクラスとCNetworkRecvPinクラスの構成を示す。
class CNetworkRecv : public CSource {
private:
CNetworkRecv( IUnknown *pUnk, HRESULT *phr );
public:
static CUnknown * WINAPI CreateInstance( IUnknown *pUnk, HRESULT *phr );
};
図 CNetworkRecvクラス
CNetworkRecvクラスはの主要な役割は、そのコンストラクタの中で、CNetworkRecvPin
のオブジェクトを生成することである。IPパケット受信といった具体的なデータの扱いは、
CNetworkRecvPinが担う。
class CNetworkRecvPin : public CSourceStream {
private:
SOCKET sock;
SOCKADDR_IN addrMe;
char szRecvBuf[1500];
BOOL bRecvisFirst;
WORD wBeforeSequenceNum;
WORD wSequenceNum;
DWORD dwTimestamp;
DWORD dwBeforeTimestamp;
DWORD dwPacketLoss;
LONG lTotalByteNoRTP;
LONG lTotalByte;
CCritSec m_cSharedState;
protected:
HRESULT GetMediaType(CMediaType *pMediaType);
HRESULT CheckMediaType(const CMediaType *pMediaType);
HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES
*pRequest);
HRESULT FillBuffer(IMediaSample *pSample);
public:
CNetworkRecvPin (HRESULT *phr, CSource *pFilter);
~ CNetworkRecvPin ( );
};
図 CNetworkRecvPinクラス
6.3.1 IPパケット受信
第7章 評価
7.1 評価概要
7.2 本システムの実現した機能 7.3 送出データ量の計測 7.4 より柔軟な輻輳制御の実現
7.4.1 実験: 帯域幅の不足 7.4.2 実験: 遅延時間 7.5 まとめ
第8章 結論
8.1 まとめ 8.2 今後の課題
付録
RenBase.h内で定義されているCBaseRendererクラス
// Main renderer class that handles synchronisation and state changes
class CBaseRenderer : public CBaseFilter {
protected:
friend class CRendererInputPin;
friend void CALLBACK EndOfStreamTimer(
UINT uID, // Timer identifier UINT uMsg, // Not currently used DWORD_PTR dwUser, // User information DWORD_PTR dw1, // Windows reserved DWORD_PTR dw2); // Is also reserved
CRendererPosPassThru *m_pPosition; // Media seeking pass by object CAMEvent m_RenderEvent; // Used to signal timer events CAMEvent m_ThreadSignal; // Signalled to release worker thread CAMEvent m_evComplete; // Signalled when state complete BOOL m_bAbort; // Stop us from rendering more data BOOL m_bStreaming; // Are we currently streaming DWORD_PTR m_dwAdvise; // Timer advise cookie IMediaSample *m_pMediaSample; // Current image media sample BOOL m_bEOS; // Any more samples in the stream BOOL m_bEOSDelivered; // Have we delivered an EC_COMPLETE CRendererInputPin *m_pInputPin; // Our renderer input pin object
CCritSec m_InterfaceLock; // Critical section for interfaces CCritSec m_RendererLock; // Controls access to internals IQualityControl * m_pQSink; // QualityControl sink
BOOL m_bRepaintStatus; // Can we signal an EC_REPAINT // Avoid some deadlocks by tracking filter during stop
volatile BOOL m_bInReceive; // Inside Receive between PrepareReceive // And actually processing the sample
UINT m_EndOfStreamTimer; // Used to signal end of stream CCritSec m_ObjectCreationLock; // This lock protects the creation and // of m_pPosition and m_pInputPin. It // ensures that two threads cannot create // either object simultaneously.
public:
CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer TCHAR *pName, // Debug ONLY description LPUNKNOWN pUnk, // Aggregated owner object HRESULT *phr); // General OLE return code
~CBaseRenderer();
// Overriden to say what interfaces we support and where
virtual HRESULT GetMediaPositionInterface(REFIID riid,void **ppv);
STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
virtual HRESULT SourceThreadCanWait(BOOL bCanWait);
#ifdef DEBUG
// Debug only dump of the renderer state void DisplayRendererState();
#endif
virtual HRESULT WaitForRenderTime();
virtual HRESULT CompleteStateChange(FILTER_STATE OldState);
// Return internal information about this filter BOOL IsEndOfStream() { return m_bEOS; };
BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };
BOOL IsStreaming() { return m_bStreaming; };
void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };
virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { };
CAMEvent *GetRenderEvent() { return &m_RenderEvent; };
// Permit access to the transition state void Ready() { m_evComplete.Set(); };
void NotReady() { m_evComplete.Reset(); };
BOOL CheckReady() { return m_evComplete.Check(); };
virtual int GetPinCount();
virtual CBasePin *GetPin(int n);
FILTER_STATE GetRealState();
void SendRepaint();
void SendNotifyWindow(IPin *pPin,HWND hwnd);
BOOL OnDisplayChange();
void SetRepaintStatus(BOOL bRepaint);
// Override the filter and pin interface functions STDMETHODIMP Stop();
STDMETHODIMP Pause();
STDMETHODIMP Run(REFERENCE_TIME StartTime);
STDMETHODIMP GetState(DWORD dwMSecs,FILTER_STATE *State);
STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
// These are available for a quality management implementation virtual void OnRenderStart(IMediaSample *pMediaSample);
virtual void OnRenderEnd(IMediaSample *pMediaSample);
virtual HRESULT OnStartStreaming() { return NOERROR; };
virtual HRESULT OnStopStreaming() { return NOERROR; };
virtual void OnWaitStart() { };
virtual void OnWaitEnd() { };
virtual void PrepareRender() { };
#ifdef PERF
REFERENCE_TIME m_trRenderStart; // Just before we started drawing
// Set in OnRenderStart, Used in OnRenderEnd int m_idBaseStamp; // MSR_id for frame time stamp
int m_idBaseRenderTime; // MSR_id for true wait time
int m_idBaseAccuracy; // MSR_id for time frame is late (int)
// Quality management implementation for scheduling rendering virtual BOOL ScheduleSample(IMediaSample *pMediaSample);
virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime);
virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample, REFERENCE_TIME *ptrStart, REFERENCE_TIME *ptrEnd);
// Lots of end of stream complexities void TimerCallback();
void ResetEndOfStreamTimer();
HRESULT NotifyEndOfStream();
virtual HRESULT SendEndOfStream();
virtual HRESULT ResetEndOfStream();
virtual HRESULT EndOfStream();
// Rendering is based around the clock void SignalTimerFired();
virtual HRESULT CancelNotification();
virtual HRESULT ClearPendingSample();
// Called when the filter changes state virtual HRESULT Active();
virtual HRESULT Inactive();
virtual HRESULT StartStreaming();
virtual HRESULT StopStreaming();
virtual HRESULT BeginFlush();
virtual HRESULT EndFlush();
// Deal with connections and type changes virtual HRESULT BreakConnect();
virtual HRESULT SetMediaType(const CMediaType *pmt);
// These look after the handling of data samples
virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);
virtual HRESULT Receive(IMediaSample *pMediaSample);
virtual BOOL HaveCurrentSample();
virtual IMediaSample *GetCurrentSample();
virtual HRESULT Render(IMediaSample *pMediaSample);
// Derived classes MUST override these
virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;
virtual HRESULT CheckMediaType(const CMediaType *) PURE;
// Helper
void WaitForReceiveToComplete();
};
ImediaSampleインターフェイスの定義
IMediaSample : public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE GetPointer(
/* [out] */ BYTE **ppBuffer) = 0;
virtual long STDMETHODCALLTYPE GetSize( void) = 0;
virtual HRESULT STDMETHODCALLTYPE GetTime(
/* [out] */ REFERENCE_TIME *pTimeStart, /* [out] */ REFERENCE_TIME *pTimeEnd) = 0;
virtual HRESULT STDMETHODCALLTYPE SetTime(
/* [in] */ REFERENCE_TIME *pTimeStart, /* [in] */ REFERENCE_TIME *pTimeEnd) = 0;
virtual HRESULT STDMETHODCALLTYPE IsSyncPoint( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetSyncPoint(
BOOL bIsSyncPoint) = 0;
virtual HRESULT STDMETHODCALLTYPE IsPreroll( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetPreroll(
BOOL bIsPreroll) = 0;
virtual long STDMETHODCALLTYPE GetActualDataLength( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetActualDataLength(
long __MIDL_0010) = 0;
virtual HRESULT STDMETHODCALLTYPE GetMediaType(
AM_MEDIA_TYPE **ppMediaType) = 0;
virtual HRESULT STDMETHODCALLTYPE SetMediaType(
AM_MEDIA_TYPE *pMediaType) = 0;
virtual HRESULT STDMETHODCALLTYPE IsDiscontinuity( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetDiscontinuity(
BOOL bDiscontinuity) = 0;
virtual HRESULT STDMETHODCALLTYPE GetMediaTime(
/* [out] */ LONGLONG *pTimeStart, /* [out] */ LONGLONG *pTimeEnd) = 0;
virtual HRESULT STDMETHODCALLTYPE SetMediaTime(
/* [in] */ LONGLONG *pTimeStart,
};
CSourceクラス
public:
CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr);
CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid);
#ifdef UNICODE
CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr);
CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid);
#endif
~CSource();
int GetPinCount(void);
CBasePin *GetPin(int n);
// -- Utilities --
// provide our critical section
CCritSec* pStateLock(void) { return &m_cStateLock; } HRESULT AddPin(CSourceStream *);
HRESULT RemovePin(CSourceStream *);
STDMETHODIMP FindPin(
LPCWSTR Id, IPin ** ppPin );
int FindPinNumber(IPin *iPin);
protected:
// The number of pins on this filter. Updated by CSourceStream int m_iPins;
// constructors & destructors.
CSourceStream **m_paStreams; // the pins on this filter.
CCritSec m_cStateLock; // Lock this to serialize function accesses to the filter state
};
CSourceStreamクラス
public:
CSourceStream(TCHAR *pObjectName, HRESULT *phr,
CSource *pms, LPCWSTR pName);
#ifdef UNICODE
CSourceStream(CHAR *pObjectName, HRESULT *phr, CSource *pms, LPCWSTR pName);
#endif
virtual ~CSourceStream(void); // virtual destructor ensures derived class destructors are called too.
protected:
CSource *m_pFilter; // The parent of this stream
// *
// * Data Source // *
// * The following three functions: FillBuffer, OnThreadCreate/Destroy, are // * called from within the ThreadProc. They are used in the creation of // * the media samples this pin will provide
// *
// Override this to provide the worker thread a means // of processing a buffer
virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE;
// Called as the thread is created/destroyed - use to perform // jobs such as start/stop streaming mode
// If OnThreadCreate returns an error the thread will exit.
virtual HRESULT OnThreadCreate(void) {return NOERROR;};
virtual HRESULT OnThreadStartPlay(void) {return NOERROR;};
// *
// * Worker Thread // *
HRESULT Active(void); // Starts up the worker thread HRESULT Inactive(void); // Exits the worker thread.
public:
// thread commands
enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT};
HRESULT Init(void) { return CallWorker(CMD_INIT); } HRESULT Exit(void) { return CallWorker(CMD_EXIT); } HRESULT Run(void) { return CallWorker(CMD_RUN); } HRESULT Pause(void) { return CallWorker(CMD_PAUSE); } HRESULT Stop(void) { return CallWorker(CMD_STOP); }
protected:
Command GetRequest(void) { return (Command) CAMThread::GetRequest(); } BOOL CheckRequest(Command *pCom) { return
CAMThread::CheckRequest( (DWORD *) pCom); }
// override these if you want to add thread commands
virtual DWORD ThreadProc(void); // the thread function
virtual HRESULT DoBufferProcessingLoop(void); // the loop executed whilst running
// *
// * AM_MEDIA_TYPE support // *
virtual HRESULT CheckMediaType(const CMediaType *pMediaType);
virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); //
List pos. 0-n
// If you support only one type then override this fn.
// This will only be called by the default implementations // of CheckMediaType and GetMediaType(int, CMediaType*) // You must override this fn. or the above 2!
virtual HRESULT GetMediaType(CMediaType *pMediaType) {return E_UNEXPECTED;}
STDMETHODIMP QueryId(
LPWSTR * Id );
};
謝辞
本研究を進めるにあたり、御指導を頂きました、慶應義塾大学環境情報学部教授の 村井純博士、徳田英幸博士、同学部助教授の楠本博之博士、中村修博士、同大学環境 情報学部専任講師の南政樹氏に感謝致します。
絶えずご指導とご助言を頂きました慶應義塾大学院政策・メディア研究科助手の杉浦一徳 博士に感謝致します。