最初の記事
「オブジェクトまでの距離とその速度の測定」で、 Video4Linux2およびDirectXを使用したWebカメラからの画像のキャプチャについて検討しました。 次の記事
「ネットワークカメラからのビデオのキャプチャ、パート1」では 、ネットワークMotion-JPEGカメラの使用方法を検討しました。 次に、ネットワークRTSPカメラ、特にRTSPを介したMotion-JPEGから画像をキャプチャする方法について説明します。
このタスクは、より多くのアクション、より多くの接続が必要であるため、Motion-JPEG over HTTPよりも複雑ですが、見返りに、柔軟性、速度、機能性、さらには何らかの汎用性が得られます。 正直なところ、RTSPは単純なタスクには冗長ですが、必要になる状況があることは間違いありません。
RTSPとは何ですか?
RTSPは、リアルタイムストリーミングプロトコル(リアルタイムストリーミングプロトコル)の略です。実際にはブロードキャスト制御プロトコルであり、「開始」、「停止」、「特定の時間への移行」などの複数のコマンドを実行できます。 このプロトコルは実装のHTTPに似ていますが、ヘッダーもあり、すべてがテキスト形式でも送信されます。
仕様からの主なコマンドは次のとおりです。
- OPTIONS-サポートされているメソッド(OPTIONS、DESCRIBEなど)のリストを返します。
- DESCRIBE-コンテンツの説明の要求。各トラックをSDP形式で説明します。
- SETUP-ストリームの接続とトランスポートを確立する要求。
- PLAY-放送を開始します。
- TEARDOWN-ブロードキャストを停止します。
RTSPの特徴は、RTSP自体が必要なビデオデータを送信しないことです! プロトコル全体は通信専用です。 ここで、MVCとの類似性を見ることができます。データとその説明は分離されています。
主力製品は別のプロトコルです。RTP-リアルタイム転送プロトコル-リアルタイム転送プロトコル。 その助けにより、必要なデータが送信されます。 このプロトコルを使用することは非常に快適であり、データリンク層でのフラグメンテーション後にクライアントソフトウェアがデータを簡単に回復できることは注目に値します。 また、送信されるデータの形式、タイムスタンプ、同期フィールド(たとえば、オーディオとビデオが同時に送信される場合)など、いくつかの有用なフィールドがあります。 このプロトコルはTCPを介して機能しますが、速度指向のアプローチのため、通常UDPで使用されます。 つまり、RTPデータは、メディアコンテンツのヘッダーとペイロードデータを含むUDPデータグラムです。
他に何も必要ないようです。 RTSP経由で接続され、RTP経由でピックアップします。 しかし、そこには、スマートおじさんが3番目のプロトコル、
RTCP-リアルタイムトランスポートコントロールプロトコル-リアルタイムトランスポートコントロールプロトコルを思いつきました。 このプロトコルは、サービスの品質を判断するために使用され、その助けにより、クライアントとサーバーは、コンテンツの送信の良し悪しを知ることができます。 このデータに従って、サーバーは、たとえば、ビットレートを下げるか、別のコーデックに切り替えることができます。
RTPは偶数のポート番号を使用し、RTCPは次の奇数のポート番号を使用することが認められています。
RTSP通信の例
RTSPストリームのソースは
eVidence APIX Box M1カメラのみであるため、すべての例はそれに関連しています。
以下は、VLCプレーヤー(これは私の研究で非常に役立ちます)とこのカメラの間の通信のログです。 VLCからカメラのポート554への最初の要求。 答えは空の行であり、「RTSP / 1.0」で始まります。
01: OPTIONS rtsp://192.168.0.254/jpeg RTSP/1.0 02: CSeq: 1 03: User-Agent: VLC media player (LIVE555 Streaming Media v2008.07.24) 04: 05: RTSP/1.0 200 OK 06: CSeq: 1 07: Date: Fri, Apr 23 2010 19:54:20 GMT 08: Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE 09: 10: DESCRIBE rtsp://192.168.0.254/jpeg RTSP/1.0 11: CSeq: 2 12: Accept: application/sdp 13: User-Agent: VLC media player (LIVE555 Streaming Media v2008.07.24) 14: 15: RTSP/1.0 200 OK 16: CSeq: 2 17: Date: Fri, Apr 23 2010 19:54:20 GMT 18: Content-Base: rtsp://192.168.0.254/jpeg/ 19: Content-Type: application/sdp 20: Content-Length: 442 21: x-Accept-Dynamic-Rate: 1 22: 23: v=0 24: o=- 1272052389382023 1 IN IP4 0.0.0.0 25: s=Session streamed by "nessyMediaServer" 26: i=jpeg 27: t=0 0 28: a=tool:LIVE555 Streaming Media v2008.04.09 29: a=type:broadcast 30: a=control:* 31: a=range:npt=0- 32: a=x-qt-text-nam:Session streamed by "nessyMediaServer" 33: a=x-qt-text-inf:jpeg 34: m=video 0 RTP/AVP 26 35: c=IN IP4 0.0.0.0 36: a=control:track1 37: a=cliprect:0,0,720,1280 38: a=framerate:25.000000 39: m=audio 7878 RTP/AVP 0 40: a=rtpmap:0 PCMU/8000/1 41: a=control:track2 42: 43: 44: SETUP rtsp://192.168.0.254/jpeg/track1 RTSP/1.0 45: CSeq: 3 46: Transport: RTP/AVP;unicast;client_port=41760-41761 47: User-Agent: VLC media player (LIVE555 Streaming Media v2008.07.24) 48: 49: RTSP/1.0 200 OK 50: CSeq: 3 51: Cache-Control: must-revalidate 52: Date: Fri, Apr 23 2010 19:54:20 GMT 53: Transport: RTP/AVP;unicast;destination=192.168.0.4;source=192.168.0.254;client_port=41760-41761; server_port=6970-6971 54: Session: 1 55: x-Transport-Options: late-tolerance=1.400000 56: x-Dynamic-Rate: 1 57: 58: SETUP rtsp://192.168.0.254/jpeg/track2 RTSP/1.0 59: CSeq: 4 60: Transport: RTP/AVP;unicast;client_port=7878-7879 61: Session: 1 62: User-Agent: VLC media player (LIVE555 Streaming Media v2008.07.24) 63: 64: RTSP/1.0 200 OK 65: CSeq: 4 66: Cache-Control: must-revalidate 67: Date: Fri, Apr 23 2010 19:54:20 GMT 68: Transport: RTP/AVP;unicast;destination=192.168.0.4;source=192.168.0.254;client_port=7878-7879; server_port=6972-6973 69: Session: 1 70: x-Transport-Options: late-tolerance=1.400000 71: x-Dynamic-Rate: 1 72: 73: PLAY rtsp://192.168.0.254/jpeg/ RTSP/1.0 74: CSeq: 5 75: Session: 1 76: Range: npt=0.000- 77: User-Agent: VLC media player (LIVE555 Streaming Media v2008.07.24) 78: 79: RTSP/1.0 200 OK 80: CSeq: 5 81: Date: Fri, Apr 23 2010 19:54:20 GMT 82: Range: npt=0.000- 83: Session: 1 84: RTP-Info: url=rtsp://192.168.0.254/jpeg/track1;seq=20730; rtptime=3869319494,url=rtsp://192.168.0.254/jpeg/track2;seq=33509;rtptime=3066362516 85: 86: # 87: 88: TEARDOWN rtsp://192.168.0.254/jpeg/ RTSP/1.0 89: CSeq: 6 90: Session: 1 91: User-Agent: VLC media player (LIVE555 Streaming Media v2008.07.24) 92: 93: RTSP/1.0 200 OK 94: CSeq: 6 95: Date: Fri, Apr 23 2010 19:54:25 GMT
VLCがカメラに最初に要求すること:
「私はあなたと何ができる?」 (オプション)
-こんにちは。 そして、オプション、説明、セットアップ、分解、再生、一時停止のいずれかを行うようにお願いできますか。
-では、「/ jpeg」の内容を教えてください。 (説明)
-ここには、最初のトラックが行くビデオ、M-JPEG、および2番目のトラックはオーディオシンプルです。
-ビデオ、最初のトラックを見て、ポケット番号41760で私に注いでください。ポケット番号41761の殻を捨てることができます。 (SETUPトラック1)
「OK、あなたの命令で...」
-そして、音を聞きたいのですが、7878、7879ポケットでの発疹。 (SETUPトラック2)
-はい、問題ありません。
-まあ、振りかけた。 (プレイ)
しばらくして:
-さて、十分、私は十分に見ました。 (分解)
-あなたが言うように。
ここで余談が終わります。 最初のリクエストでは、「
OPTIONS rtsp://192.168.0.254/jpeg RTSP/1.0
」は「
GET /jpeg HTTP/1.1
」に似ており、会話はこれで始まり、HTTPプロトコルにも
OPTIONSメソッドがあります。 ここで、192.168.0.254はカメラのIPアドレスです。
CSeq
は要求のシーケンス番号を反映します。サーバーからの応答には同じ
CSeq
が含まれている必要があります。
サーバーからの応答は「
RTSP/1.0 200 OK
」で始まり、「
HTTP/1.1 200 OK
」のようになり
HTTP/1.1 200 OK
すべてが正常であるという兆候です。リクエストは受け入れられ、リクエストは明確で、実装に問題はありませんでした。 また、プレーンテキストでは、使用可能なすべてのメソッドのリストが続きます。
次に、リクエスト/ jpegで私たちを待っている情報を収集します。これは、彼をフォローして、リンク「
rtsp://192.168.0.254/jpeg
」に
rtsp://192.168.0.254/jpeg
です。 また、SDPの形式で回答を受け取りたいことを示します(行12)。
応答として、
Content-Type
および
Content-Length
示すRTSPヘッダーを取得し、ヘッダーの後に空の行を介して、SDP形式のコンテンツ自体が直接:
v=0 o=- 1272052389382023 1 IN IP4 0.0.0.0 s=Session streamed by "nessyMediaServer" i=jpeg t=0 0 a=tool:LIVE555 Streaming Media v2008.04.09 a=type:broadcast a=control:* a=range:npt=0- a=x-qt-text-nam:Session streamed by "nessyMediaServer" a=x-qt-text-inf:jpeg m=video 0 RTP/AVP 26 c=IN IP4 0.0.0.0 a=control:track1 a=cliprect:0,0,720,1280 a=framerate:25.000000 m=audio 7878 RTP/AVP 0 a=rtpmap:0 PCMU/8000/1 a=control:track2
ここではすべてが明らかです。 次の行が必要です。
# m=video 0 RTP/AVP 26 # RTP/AVP, , 26, Motion-JPEG a=control:track1 # a=cliprect:0,0,720,1280 # a=framerate:25.000000 # # m=audio 7878 RTP/AVP 0 # 7878, , 0 - PCM a=control:track2 #
ビデオのみを受信する場合、オーディオデータからは、トラックの名前を除くすべてを無視します。 ストリームを設定するために必要ですが、このストリームを強制的に受け入れることはありませんが、オーディオを完全に無視するとカメラは動作を拒否します(
SETUP
ビデオトラックに対してのみ行われる場合)。
正直に言うと、オーディオストリームのポート番号(7878)を無視すると、異なるカメラがどのように反応するかわかりません。これは、
SETUP
コマンドで指定するためです。
次に2つの
SETUP
要求があり、ビデオおよびオーディオストリームを受信するポートを示します。 最初の番号はRTPのポート、2番目の番号はRTCPのポートです。 カメラの応答にはポートに関する情報が含まれています。ポートを参照して、すべてが正しく構成されていることを確認できます。 また、
Session
識別子を覚えておく必要があります。 後続のすべての呼び出しでそれを示す必要があります。
PLAY
コマンドの後、ビデオはポート41760に、オーディオはポート7878に転送され始めます。そして
TEARDOWN
コマンド
TEARDOWN
ブロードキャストが停止し、接続が切断されます。
RTP上のMJPEG
RTPパケットが届くので、それらを解読する必要があります。 このために、すべてのフィールドの説明を含むこのようなパッケージの表をここに示します。
+ビットオフセット | 0-1 | 2 | 3 | 4-7 | 8 | 9-15 | 16-31 |
---|
0 | V | P | X | CC | M | PT | シーケンス番号 |
---|
32 | タイムスタンプ |
---|
64 | SSRC識別子 |
---|
96 | ... CSRC識別子... |
---|
96+(CC×32) | 拡張ヘッダーID | 拡張ヘッダー長(EHL) |
---|
96+(CC×32)+(X×32) | ...拡張ヘッダー... |
---|
96+(CC×32)+(X×32)+(X×EHL) | ペイロード |
---|
- V (バージョン):(2)プロトコルバージョン。 バージョン2になりました。
- P (パディング、サプリメント):(1)たとえば、暗号化アルゴリズムの場合、RTPパケットの最後に空のバイトが埋め込まれるときに使用されます。
- X (拡張):(1)は、アプリケーションによって決定される拡張ヘッダーの存在を示します。 私たちの場合、これは使用されません。
- CC (CSRCカウント):(4)CSRC識別子の数が含まれています。 私たちも使われていません。
- M (マーカー):(1)はアプリケーションレベルで使用されます。この場合、RTPパケットにJPEGフレームの終わりが含まれている場合、このビットは1に設定されます。
- PT (ペイロードタイプ):(7)は、ペイロードの形式-送信データを示します。 MJPEGの場合は26です。
- シーケンス番号 :(16)RTPパケット番号。失われたパケットの検出に使用されます。
- タイムスタンプ (32):タイムスタンプ、この場合は90,000ヘルツ(90000 = 1秒)。
- SSRC (Synchronization Source):(32)シンクロナイザーの識別子。ただし、とんでもないことに聞こえるかもしれません。 ストリームのソースを指定します。
- CSRC (Contributing Source):(32)複数の場所からのストリームがある場合に使用される追加ソースの識別子。
- Extension Header ID :(16)拡張機能の識別子。これがある場合は、それが何であるかを知る必要があります。 私たちの場合、それは使用されません。
- 拡張ヘッダーの長さ :(16)このヘッダーの長さ(バイト単位)。
- 拡張ヘッダー :タイトル自体。 コンテンツは、コンテキストに応じて非常に異なる場合があります。
- ペイロード : ペイロードデータは、まったく同じJPEGフレームです。 もちろん、断片化されています。
CSRCで始まるフィールドはオプションです。 私の知る限り、カメラからのMJPEGの送信には使用されません。
上記の1レベルのカプセル化を転送しました。 ここでのタスクは、受信したビデオデータを本格的なJPEGイメージに変換することです。 MJPEG over HTTPの場合、すべてが単純です-ストリームの一部を切り取り、JPEGイメージの場合と同様にすぐに操作します。 RTPの場合、画像は完全に送信されず、JPEGヘッダーはトラフィックを節約するために省略されます。 添付データとは別に復元する必要があります。
MJPEG仕様のRTPペイロードは
RFC2435で説明されてい
ます 。 また、次の形式のすべてのフィールドの説明を記載した表を提供します。
+ビットオフセット | 0-7 | 8-15 | 16-23 | 24-31 |
---|
0 | タイプ固有 | フラグメントオフセット |
---|
32 | 種類 | Q | 幅 | 身長 |
---|
タイプ64..127の場合 | マーカーヘッダーを再起動 |
---|
Qが128..255の場合 | MBZ | 精度 | 長さ |
---|
量子化テーブルデータ |
- タイプ固有 ( タイプに依存):(8)フィールドの意味は実装に依存しますが、この場合は適用されません。
- フラグメントオフセット :(24)は、フレーム全体における現在のフレームフラグメントの位置を示します。
- タイプ :(8)イメージの復元方法はタイプによって異なります。
- Q (品質):(8)画質。
- 幅 :(8)フレーム幅。
- 高さ :(8)および高さ。
- Restart Marker header :(32)は、RSTマーカーが使用されている場合、JPEGのデコード時に使用されます。 カメラが使用されているかどうかはわかりませんが、このヘッダーは無視します。 このフィールドは、タイプ64〜127でのみ表示されます。
- 量子化テーブルデータ :存在する場合、それらを個別に計算する必要はありません。 また、JPEGデータから写真を適切に再作成するために必要です。 これらのテーブルが正しくない場合、画像は間違った色とコントラストになります。 2つのテーブルが必要です。輝度と色のそれぞれのルミナとクロマです。
- MBZ、精度、長さ :(32)量子化テーブルのパラメーター、私はそれらを無視し、長さを128-それぞれ64バイトの2つのテーブルに設定します。 そうでなければ、私は彼らと働く方法を知りません。
RSTマーカーヘッダーと量子化テーブルが存在しない場合があります。 最初のものがなければ、それは非常に良いことです。私は別のものを期待していないからです。 秒がない場合-必要なテーブルはパラメータQに基づいて計算されます。
RTCPパケットにはそのサブセットが含まれ、4つのタイプがあります。201-送信元レポート、202-受信者レポート、203-送信元の説明、204-送信先はアプリケーションによって決定されます。 まず、タイプ201を受け入れ、次にタイプ202を送信する必要があります。 203と204はオプションですが、それらも考慮に入れています。 1つのUDPパケットに複数のRTCPパケットが存在する場合があります。
すべてのタイプの構造は類似しています。 RTCPパケットは、次のデータで始まります。
+ビットオフセット | 0-1 | 2 | 3-7 | 8-15 | 16-31 |
---|
0 | バージョン | パディング | SCまたはRCまたはサブタイプ | パケットタイプ | 長さ |
---|
- バージョン :(2)RTPバージョン。
- パディング :(1)RTPと同じ。
- SCまたはRCまたはサブタイプ :(5) タイプに応じて、受信者と送信元のレポートに含まれる送信元の数(送信元数)または受信者の数(受信者数)になります。 APPパケットの場合、このフィールドはそのようなパケットのサブタイプを定義します。
- パケットタイプ :(8)パケットタイプ、201-送信元レポート(送信者のレポートSS)、202-受信者レポート(受信者のレポートRR)、203-ソースの説明SDESおよび204-宛先はアプリケーション(APP)によって決定されます。
- 長さ :(16)ヘッダーに続くデータのサイズ。32ビット単位で測定されます。
さらに、各サブタイプのフィールドは提供しません
。RFC3550で表示できます。 SSおよびRRタイプは、送信/受信パケットおよび時間遅延に関する情報を伝えるとだけ言っておきましょう。 SDES自体には、名前、電子メール、電話、場所など、ソースを定義するさまざまなテキストフィールドがあります。
これで紹介は終わりです。
RTSPクライアントを介したPython MJPEG
それでpythonに行きました。 クライアントはいくつかのファイルで構成され、
main.py
には受信した画像を処理するコールバック関数が含まれます。また、Twistedネットワークフレームワークメカニズムを開始し、カメラに接続するための設定を保存します。 私が短縮したすべてのリスト、フルバージョンは、記事の最後にあるリンクからダウンロードできます。
main.py 20: def processImage(img): 21: 'This function is invoked by the MJPEG Client protocol' 22:
原則として、RTCPプロトコルを実装せずにオーディオデータを受信することなく作業できます。 この場合、カメラは約1分後に切断されます。 常に再接続する必要があります。これは自動的に行われるため、問題は発生しません。 ただし、この記事では、RTCP部分を追加し、オーディオデータを受信するための空白を作成しました。
次に重要なファイルは
rtsp_client.py
です。 彼は最も混乱していますが、彼の目標は明白です-上記の接続を正しく確立すること。
rtsp_client.py 012: class RTSPClient(Protocol): 013: def __init__(self): 014: self.config = {} 015: self.wait_description = False 016: 017: def connectionMade(self): 018: self.session = 1 019:
オーディオトラックが存在する場合、このモジュールは
rtp_audio_client.py
および対応するRTCPクライアントも起動します。
接続に成功すると、
rtp_mjpeg_client.py
作業に使用され、着信データストリームが処理されます。
rtp_mjpeg_client.py 08: class RTP_MJPEG_Client(DatagramProtocol): 09: def __init__(self, config): 10: self.config = config 11:
彼は理解しやすい。 別のデータグラムを受け入れるたびに、
rtp_datagram.py
モジュールを使用して解析し、その結果を
rfc2435jpeg.py
モジュールに
rfc2435jpeg.py
ます。これにより、本格的なJPEGイメージが作成されます。 次に、
rtp_dg.Marker
マーカーが表示されるのを待ち、その表示が復元された画像でコールバック関数を呼び出す様子を確認します。
RTPデータグラムパーサーは次のようになります。
rtp_datagram.py 26: def parse(self): 27: Ver_P_X_CC, M_PT, self.SequenceNumber, self.Timestamp, self.SyncSourceIdentifier = unpack('!BBHII', self.Datagram[:12]) 28: self.Version = (Ver_P_X_CC & 0b11000000) >> 6 29: self.Padding = (Ver_P_X_CC & 0b00100000) >> 5 30: self.Extension = (Ver_P_X_CC & 0b00010000) >> 4 31: self.CSRCCount = Ver_P_X_CC & 0b00001111 32: self.Marker = (M_PT & 0b10000000) >> 7 33: self.PayloadType = M_PT & 0b01111111 34: i = 0 35: for i in range(0, self.CSRCCount, 4): 36: self.CSRS.append(unpack('!I', self.Datagram[12+i:16+i])) 37: if self.Extension: 38: i = self.CSRCCount * 4 39: (self.ExtensionHeaderID, self.ExtensionHeaderLength) = unpack('!HH', self.Datagram[12+i:16+i]) 40: self.ExtensionHeader = self.Datagram[16+i:16+i+self.ExtensionHeaderLength] 41: i += 4 + self.ExtensionHeaderLength 42: self.Payload = self.Datagram[12+i:]
JPEGリカバリモジュールには、いくつかのテーブルとかなり長いヘッダー生成機能が含まれているため、十分に大きいです。 したがって、ここではそれらを省略し、RTPペイロードを解析して最終的なJPEGイメージを作成する機能のみを提供します。
rfc2435jpeg.py 287: def parse(self): 288: HOffset = 0 289: LOffset = 0 290:
rtp_audio_client.py
オーディオデータ受信モジュールも実装しましたが、再生可能なデータに変換しませんでした。 誰かがそれを必要とするならば、私はすべてがそうであるようにこのファイルでスケッチを作りました。
rfc2435jpeg.py
似た構文解析を行う必要があるだけです。 オーディオデータは断片化されていないため、簡単です。 各パッケージには、複製するのに十分なデータが含まれています。 この記事はすでに非常に長いので、ここではこのモジュールを取り上げません(すぐにHabrafoldを実装することになります)。
正しく動作するには、RTCPパケットを送受信し、送信者のレポートを受け入れ、受信者のレポートを送信する必要があります。 タスクを簡素化するために、カメラからSRを受信した直後にRRを送信し、すべてが正常であるという理想化されたデータを組み込みます。
rtcp_client.py 09: class RTCP_Client(DatagramProtocol): 10: def __init__(self): 11:
ただし、モジュールはRTCPデータグラムと直接連携します。 それも十分に大きかった。
rtcp_datagram.py 049: def parse(self): 050:
解析はRFCに厳密に従っています。関数unpack
を使用してデータを数値変数に変換しoff
、現在のオフセットを含む変数を使用してデータ配列を移動します。また、リンクは次のとおりです。PythonMJPEG over RTSP client。ロシア語のコメント付きのリスティングのバージョンを作成することはできなくなったため、誰にとっても都合が悪い場合はご容赦ください。読むのに便利
- インターネット上のマルチメディア
- オーディオとビデオのRTPプロファイルのリスト
この記事で終わり、そして誰がマスターした-よくやった!