この記事では、参照用に実行可能なソース コードを提供します。
この記事の特典として、無料の C++ オーディオおよびビデオ学習教材パッケージ + 学習ルートの概要、技術ビデオ/コード (オーディオおよびビデオの開発、面接の質問、FFmpeg、webRTC、rtmp、hls、rtsp、ffplay、コーデック、プッシュプルストリーミング、SRS)↓↓↓以下からご覧ください↓↓記事下部をクリックして無料で入手してください↓↓
効果
RTSPストリームを生成する
ファイルの再生よりも少し複雑なのは、RTSP ストリームを受信するために RTSP ストリームを生成する必要があることです。RTSP ストリーミング環境を構築するだけです。
EasyDarwin を使用して、RTSP サービスを RTSP サーバーとして有効にします。
ffmpeg コマンド ラインをクライアントとして使用し、ループ内でビデオ ファイルを EasyDarwin にプッシュします。
./ffmpeg.exe -re -stream_loop -1 -i test.mp4 -c copy -f rtsp rtsp://127.0.0.1/stream
これにより、EasyDarwin から RTSP ストリームを受信できるようになります。
vlc を使って RTSP ストリームを受信して見てみましょう。
正常に受信されました。
FFmepg が RTSP ストリーム コードを受信する
FFmpeg を使用して RTSP ストリームを受信して再生するプロセスは、mp4 ファイルを再生するプロセスと似ていますが、mp4 ファイルを再生する場合はファイルが再生ソースとして使用され、RTSP ストリームを受信する場合は RTSP ストリームが再生ソースとして使用される点が異なります。プレイソース:
引き続き、プロセス内のキー コードを見てみましょう。
if (avformat_open_input(&fileFmtCtx, url.toStdString().c_str(), nullptr, nullptr) != 0) {
qDebug() << "avformat_open_input() failed";
return;
}
RTSP アドレスを開くために使用されます。ファイルを開く場合と比較して、ストリーム情報を見つける必要があるだけでなく、RTSP サーバーとの接続を確立し、RTSP サーバーがストリームのプッシュを開始できるようにする必要もあります。
上記の RTSP ストリームを受信した後、AVFormatContext の関連プロパティを出力します。
qDebug() << "stream name: " << streamFmtCtx->url;
qDebug() << "stream iformat: " << streamFmtCtx->iformat->name;
qDebug() << "stream duration: " << streamFmtCtx->duration << " microseconds";
qDebug() << "stream bit_rate: " << streamFmtCtx->bit_rate;
/*
stream name: rtsp://127.0.0.1/stream
stream iformat: rtsp
stream duration: -9223372036854775808 microseconds
stream bit_rate: 0
*/
今回はRTSPストリームなので正確な長さは取得できません。ストリーム関連情報の出力を続行します。
qDebug() << "nb_streams:";
for (unsigned int i = 0; i < streamFmtCtx->nb_streams; i++) {
AVStream *stream = streamFmtCtx->streams[i];
qDebug() << "Stream " << i + 1 << ":";
qDebug() << " Codec: " << avcodec_get_name(stream->codecpar->codec_id);
qDebug() << " Duration: " << stream->duration << " microseconds";
}
/*
nb_streams:
Stream 1 :
Codec: h264
Duration: -9223372036854775808 microseconds
Stream 2 :
Codec: aac
Duration: -9223372036854775808 microseconds
*/
前回ファイルを直接読み取ったときと同じ結果 (1 つの H264 ビデオ ストリームと 1 つの AAC オーディオ ストリームを含む) が表示されます。
swsCtx = sws_getContext(decoderCtx->width, decoderCtx->height, decoderCtx->pix_fmt,
decoderCtx->width, decoderCtx->height, FMT_PIC_SHOW,
SWS_BICUBIC, NULL, NULL, NULL);
qDebug() << "decoderCtx->pix_fmt:" << av_get_pix_fmt_name(decoderCtx->pix_fmt);
//decoderCtx->pix_fmt: yuv420p
sws_getContext()は、RTSPストリーム形式を表示形式(ここではyuv420p=>AV_PIX_FMT_RGB24)に変換するために使用されます。
int numBytes = av_image_get_buffer_size(FMT_PIC_SHOW, decoderCtx->width, decoderCtx->height, 1);
showBuffer = (unsigned char*)av_malloc(static_cast<unsigned long long>(numBytes) * sizeof(unsigned char));
if(av_image_fill_arrays(showFrame->data, showFrame->linesize,
showBuffer, FMT_PIC_SHOW, decoderCtx->width, decoderCtx->height, 1) < 0)
{
qDebug() << "av_image_fill_arrays() failed";
return;
}
av_image_get_buffer_size は、画像データを計算するためのバッファ サイズを計算します。av_malloc は、showBuffer に 1 つのメモリ ブロックを割り当てます。av_image_fill_arrays は、AVFrame のデータ メンバーとラインサイズ メンバーを画像パラメーターと showBuffer で初期化し、AVFrame を showBuffer に関連付けます。
while(av_read_frame(streamFmtCtx, packet) >= 0){
if(packet->stream_index == nVideoIndex){
if(avcodec_send_packet(decoderCtx, packet)>=0){
while((ret = avcodec_receive_frame(decoderCtx, decodedFrame)) >= 0){
//...
}
}
}
}
デコード手順は mp4 ファイルの再生と同様で、RTSP ストリームからデータ パケット AVPacket を読み取り、デコードのために AVPacket をデコーダに送信し、デコーダからデコードされたビデオ フレームの受信を試み、受信したフレーム データを decodedFrame に保存します。
上記の基本的な手順を完了すると、コードは RTSP サーバーから RTSP ストリームを受信し、VLC のように再生できるようになります。
RTSPプロトコルの簡単な説明と検証
FFmpeg は内部で RTSP 接続の確立を非常にうまく処理しますが、RTSP プロトコルについてさらに学習する必要があります。RTSP の正式名称は Real Time Sreaming Protocol で、TCP/IP プロトコル システムのアプリケーション層プロトコルです。データ送信は RTP/RTCP によって完了し、最下層は TCP/UDP によって実装されます。
標準の RTSP ストリーミング プロトコル層の対話プロセスは次のとおりです。
言うことはあまりありませんが、上記のプッシュ環境を直接使用し (EasyDarwin が特定の情報を暗号化するようだったので、別の RTSP サーバーを選択しました。効果は同じです)、VLC を使用してストリームを収集し、Wireshark を使用してパケットをキャプチャして確認します。契約プロセスは次のようになりますか?
各情報が何であるかを直接見てみましょう。
クライアント => サーバー
Real Time Streaming Protocol
Request: OPTIONS rtsp://127.0.0.1:554/stream RTSP/1.0\r\n
Method: OPTIONS
URL: rtsp://127.0.0.1:554/stream
CSeq: 2\r\n
User-Agent: LibVLC/3.0.18 (LIVE555 Streaming Media v2016.11.28)\r\n
\r\n
クライアントは OPTIONS を rtsp://127.0.0.1:554/stream に送信して、サーバーがどの RTSP メソッドをサポートするかを尋ねます。
サーバー=>クライアント
Real Time Streaming Protocol
Response: RTSP/1.0 200 OK\r\n
Status: 200
CSeq: 2\r\n
Session: 4J_bOCNSg
Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS, ANNOUNCE, RECORD\r\n
\r\n
サーバー応答は DESCRIBE、SETUP、TEARDOWN、PLAY、PAUSE、OPTIONS、ANNOUNCE、RECORD をサポートします
クライアント => サーバー
Real Time Streaming Protocol
Request: DESCRIBE rtsp://127.0.0.1:554/stream RTSP/1.0\r\n
Method: DESCRIBE
URL: rtsp://127.0.0.1:554/stream
CSeq: 3\r\n
User-Agent: LibVLC/3.0.18 (LIVE555 Streaming Media v2016.11.28)\r\n
Accept: application/sdp\r\n
\r\n
クライアントは、application/sdp 形式のメディア記述ファイルを要求します。
通常、サーバーはユーザー認証を実行します。認可認証情報が伝送されていない場合、または認証が失敗した場合、サーバーはエラー番号 401 の応答を返します。クライアントは 401 応答を受信すると、認証情報に基づいて認可を生成する必要があります。既知のユーザ認証情報を送信して DESCRIBE を送信し、認証が成功すると、サーバは SDP を付加した応答情報を返します。
認証を行うかどうかはRTSPサーバーに依存しますが、ここではEasyDarwinには認証を設定していません。
サーバー=>クライアント
Real Time Streaming Protocol
Response: RTSP/1.0 200 OK\r\n
CSeq: 3\r\n
Session: _ZLZ7_NSR
Content-type: application/sdp
Content-length: 511
\r\n
Session Description Protocol
Session Description Protocol Version (v): 0
Owner/Creator, Session Id (o): - 0 0 IN IP4 127.0.0.1
Session Name (s): No Name
Connection Information (c): IN IP4 127.0.0.1
Time Description, active time (t): 0 0
Session Attribute (a): tool:libavformat 58.76.100
Media Description, name and address (m): video 0 RTP/AVP 96
Bandwidth Information (b): AS:1894
Media Attribute (a): rtpmap:96 H264/90000
Media Attribute (a): fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKqwspQFAFumoCAgKAAADAAIAAAMAYcTAAc/YABW+f4xwEA==,aOkJNSU=; profile-level-id=64002A
Media Attribute (a): control:streamid=0
Media Description, name and address (m): audio 0 RTP/AVP 97
Bandwidth Information (b): AS:317
Media Attribute (a): rtpmap:97 MPEG4-GENERIC/48000/2
Media Attribute (a): fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1190
Media Attribute (a): control:streamid=1
サーバーは SDP 情報を返し、現在利用可能なオーディオおよびビデオのストリームと属性をクライアントに伝えます。SDP プロトコルは拡張されていません。ここで注意する必要があるより重要な情報は、サーバーが streamid=0 の H264 ビデオ ストリームと streamid=1 の AAC オーディオ ストリームを送信できることです。
クライアント => サーバー
Real Time Streaming Protocol
Request: SETUP rtsp://127.0.0.1:554/stream/streamid=0 RTSP/1.0\r\n
Method: SETUP
URL: rtsp://127.0.0.1:554/stream/streamid=0
CSeq: 4\r\n
User-Agent: LibVLC/3.0.18 (LIVE555 Streaming Media v2016.11.28)\r\n
Transport: RTP/AVP;unicast;client_port=52024-52025
\r\n
クライアントは SETUP を送信して、ビデオ ストリームである streamid=0 の接続を確立する必要があることをサーバーに伝えます。ここで、RTP/AVP は UDP 経由の送信を意味し、unicast はユニキャストを意味し、client_port=52024-52025 については別途説明する必要があります。前述したように、RTSP プロトコル データは、RTP+ RTCP を介して送信されます。RTP と RTCP はどちらも UDP 上に構築されており、RTP はデフォルトで偶数のポート番号を使用しますが、RTCP はデフォルトで RTP ポートの次の奇数のポート番号 (ここでは 52024 と 52025) を使用します。
サーバー => クライアント
Real Time Streaming Protocol
Response: RTSP/1.0 200 OK\r\n
Status: 200
CSeq: 4\r\n
Session: 4J_bOCNSg
Transport: RTP/AVP;unicast;client_port=52024-52025
\r\n
サーバーはクライアントに確認を返します。
クライアント => サーバー
Real Time Streaming Protocol
Request: SETUP rtsp://127.0.0.1:554/stream/streamid=1 RTSP/1.0\r\n
Method: SETUP
URL: rtsp://127.0.0.1:554/stream/streamid=1
CSeq: 5\r\n
User-Agent: LibVLC/3.0.18 (LIVE555 Streaming Media v2016.11.28)\r\n
Transport: RTP/AVP;unicast;client_port=52028-52029
Session: 4J_bOCNSg
\r\n
クライアントはサーバーに、streamid=1 のオーディオ ストリームへの接続を確立する必要があることを伝えます。RTP と RTCP のポートはそれぞれ 52028 と 52029 です。
サーバー => クライアント
Real Time Streaming Protocol
Response: RTSP/1.0 200 OK\r\n
Status: 200
Transport: RTP/AVP;unicast;client_port=52028-52029
CSeq: 5\r\n
Session: 4J_bOCNSg
\r\n
サーバーはクライアントに確認を返します。
クライアント=>サーバー
Real Time Streaming Protocol
Request: PLAY rtsp://127.0.0.1:554/stream RTSP/1.0\r\n
Method: PLAY
URL: rtsp://127.0.0.1:554/stream
CSeq: 6\r\n
User-Agent: LibVLC/3.0.18 (LIVE555 Streaming Media v2016.11.28)\r\n
Session: 4J_bOCNSg
Range: npt=0.000-\r\n
\r\n
クライアントは PLAY を送信してサーバーに送信を開始するように指示します。範囲はメディアの再生時間を表します。サーバーは範囲の値に従って、指定されたセグメントのデータ ストリームを再生します。リアルタイム ストリームの場合、通常は開始点のみが指定されますつまり、範囲: npt=0.000-
サーバー=>クライアント
Real Time Streaming Protocol
Response: RTSP/1.0 200 OK\r\n
Status: 200
CSeq: 6\r\n
Session: 4J_bOCNSg
Range: npt=0.000-\r\n
\r\n
サーバーは確認を返し、同じセッションを使用します。
クライアント=>サーバー
Real Time Streaming Protocol
Request: TEARDOWN rtsp://127.0.0.1:554/stream RTSP/1.0\r\n
Method: TEARDOWN
URL: rtsp://127.0.0.1:554/stream
CSeq: 7\r\n
User-Agent: LibVLC/3.0.18 (LIVE555 Streaming Media v2016.11.28)\r\n
Session: 4J_bOCNSg
\r\n
クライアントは TEARDOWN を送信してストリーミング停止リクエストを開始します。
サーバー=>クライアント
Real Time Streaming Protocol
Response: RTSP/1.0 200 OK\r\n
Status: 200
CSeq: 7\r\n
Session: 4J_bOCNSg
\r\n
サーバーは確認を返し、同じセッションを使用し、ストリーミングを停止します。
ダイジェスト認証環境を構築する
上で述べたように、サーバーがユーザー認証を実行する可能性があるため、認証が必要な環境を作成する必要があります。EasyDarwin が認証を直接選択して easydarwin.ini を開くことができるかどうかを確認してみましょう。
[http]
port=10008
default_username=admin
default_password=admin
#...
;是否使能向服务器推流或者从服务器播放时验证用户名密码. [注意] 因为服务器端并不保存明文密码,所以推送或者播放时,客户端应该输入密码的md5后的值。
;password should be the hex of md5(original password)
authorization_enable=0
#...
authorization_enable 変数が認証を制御していることがわかります。その値を 1 に変更し、サービスを再起動します。この時点で、元の ffmpeg コマンドがストリームのプッシュに失敗していることがわかりました。
つまり、EasyDarwin にプッシュする際にも認証が必要になります。コメントから判断すると、ユーザー名とパスワードの md5 値を追加する必要があります。正しいパラメーターを使用してストリームをプッシュします (以下の mad5ofpassword はパスワードの md5 に置き換えられます)。
./ffmpeg.exe -re -stream_loop -1 -i test.mp4 -c copy -f rtsp rtsp://admin:[email protected]/stream
出来た:
このとき、vlc を使って受信してみると、案の定、認証が必要となり、ユーザー名とパスワードが要求されます。
ここでのパスワードには、md5 の後の値も入力する必要があることに注意してください。正しいパスワードを入力すると、vlc は RTSP ストリームを受信できるようになります。
同様に、Wireshark を使用してパケットをキャプチャし、認証プロセスがどのようなものかを確認します。
クライアント=>サーバー
Real Time Streaming Protocol
Request: DESCRIBE rtsp://127.0.0.1:554/stream RTSP/1.0\r\n
Method: DESCRIBE
URL: rtsp://127.0.0.1:554/stream
CSeq: 6\r\n
User-Agent: LibVLC/3.0.18 (LIVE555 Streaming Media v2016.11.28)\r\n
Accept: application/sdp\r\n
\r\n
まず、クライアントも DESCRIBE を開始します。
サーバー=>クライアント
Real Time Streaming Protocol
Response: RTSP/1.0 401 Unauthorized\r\n
Status: 401
CSeq: 6\r\n
Session: ayQBojNIg
WWW-Authenticate: Digest realm="EasyDarwin", nonce="539c6afee35b8edd354e983a6af947bf", algorithm="MD5"\r\n
\r\n
WWW-Authenticate: Digest は、ダイジェスト認証が必要であることを示します。応答の生成にはレルムとノンスが使用されます。algorithm="MD5" は、応答の生成に md5 アルゴリズムが必要であることを示します。
クライアント=>サーバー
Real Time Streaming Protocol
Request: DESCRIBE rtsp://127.0.0.1:554/stream RTSP/1.0\r\n
Method: DESCRIBE
URL: rtsp://127.0.0.1:554/stream
CSeq: 7\r\n
Authorization: Digest username="admin", realm="EasyDarwin", nonce="539c6afee35b8edd354e983a6af947bf", uri="rtsp://127.0.0.1:554/stream", response="d6a48b37f2010b3ddfad1eef18692648"\r\n
User-Agent: LibVLC/3.0.18 (LIVE555 Streaming Media v2016.11.28)\r\n
Accept: application/sdp\r\n
\r\n
クライアントは対応するアルゴリズムを使用してレスポンスを生成し、サーバーに返します (レスポンスの計算方法については別途説明します)。
サーバー=>クライアント
Real Time Streaming Protocol
Response: RTSP/1.0 200 OK\r\n
Status: 200
Content-length: 511
CSeq: 7\r\n
Session: ayQBojNIg
\r\n
Data (511 bytes)
サーバーは応答が合格したことを確認し、200 を返します。
実際には、上記のように SDP 情報 (Data 511 バイトの情報) がここに返されますが、EasyDarwin がそれを暗号化しているのか、それとも別の何かで暗号化されているのかを解析できません。
以降の処理はダイジェスト認証を行わない場合と同様です。
ダイジェスト認証を処理するようにコードを改善する
認証が存在する可能性があるため、サーバーに認証がある状況をコードで処理する必要があります。そうしないと、RTSP ストリームを受信できなくなります。まず、サーバーの戻り値がキャプチャされた場所を特定します。いくつかの試みの後、メソッド avformat_open_input で次のことがわかりました。
if ((ret = avformat_open_input(&streamFmtCtx, url.toStdString().c_str(), nullptr, nullptr)) != 0) {
qDebug() << "ret:" << ret;
}
//打印输出
//ret: -825242872
//ffmpeg日志输出
//[rtsp @ 000001d2d3940ec0] method DESCRIBE failed: 401 Unauthorized
認証が必要な場合、avformat_open_input は負の数を直接返します。ffmpeg のログと組み合わせると、サーバーが Unauthorized を返した場合の状況であると大まかに結論付けることができます。ただし、より具体的な確認が必要なので、avformat_open_input の宣言を見てください。
//avformat.h
/*
* @return 0 on success, a negative AVERROR on failure.
*/
int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options);
戻り値は int で、失敗した場合は AVERROR が返されるとコメントに記載されているので、ffmpeg のソース コードにアクセスして AVERROR に関する情報を見つけることができます。
ffmpeg ソース コードをコンパイルすると、それを直接デバッグして、最終的にどのように返されるかを確認できます。しかし、ここではソース コードのコンパイルに余分な時間を費やしたくないため、世界初の IDE、Visual Studio を使用します。 ffmpeg ソース コード フォルダーに移動し、AVERROR を直接検索すると、AVERROR の定義を簡単に見つけることができます。
//error.h
#define AVERROR(e) (-(e)) ///< Returns a negative error code from a POSIX error code, to return from library functions.
AVERROR は、POSIX の標準エラーの逆の数値を取得するために使用されるマクロであることがわかります。追跡を続けても、関連する戻り値は見つかりませんでした。ただし、ヘッダー ファイルには Unauthorized の関連する定義が含まれています。
//error.h
#define AVERROR_HTTP_UNAUTHORIZED FFERRTAG(0xF8,'4','0','1')
#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d))
//common.h
#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))
定義によれば、AVERROR_HTTP_UNAUTHORIZED は実際には (0xF8,'4','0','1') の組み合わせのシフトであり、定義に従って計算すると、AVERROR_HTTP_UNAUTHORIZED は実際に -825242872 に等しくなります。確認するには、ffmpeg ソース コードからマクロ定義をコピーし、プロジェクトに直接出力します。
//mainwindow.h
#define AVERROR_HTTP_UNAUTHORIZED FFERRTAG(0xF8,'4','0','1')
#define MKTAG(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))
#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d))
//mainwindow.cpp
qDebug() << "AVERROR_HTTP_UNAUTHORIZED:" <<FFERRTAG(0xF8,'4','0','1');
//输出
//AVERROR_HTTP_UNAUTHORIZED: -825242872
出力は前のログ出力と計算結果と同じであり、この時点では、AVERROR_HTTP_UNAUTHORIZED エラーが確実に報告されています。error.h 内の他のマクロ定義を簡単に出力できます。一般的な ffmpeg エラー コードのエラー コード テーブルは次のとおりです。
エラーコードマクロ定義 |
エラーコード |
エラーの説明 |
AVERROR_BSF_NOT_FOUND |
-1179861752 |
ビットストリームフィルターが見つかりません |
AVERROR_BUG |
-558323010 |
内部バグ。AVERROR_BUG2 も参照 |
AVERROR_BUFFER_TOO_SMALL |
-1397118274 |
バッファが小さすぎます |
AVERROR_DECODER_NOT_FOUND |
-1128613112 |
デコーダが見つかりません |
AVERROR_DEMUXER_NOT_FOUND |
-1296385272 |
デマルチプレクサが見つかりません |
AVERROR_ENCODER_NOT_FOUND |
-1129203192 |
エンコーダが見つかりません |
AVERROR_EOF |
-541478725 |
ファイルの終わり |
AVERROR_EXIT |
-1414092869 |
即時退出が要求されました。呼び出された関数を再起動しないでください |
AVERROR_EXTERNAL |
-542398533 |
外部ライブラリの一般的なエラー |
AVERROR_FILTER_NOT_FOUND |
-1279870712 |
フィルターが見つかりません |
AVERROR_INVALIDDATA |
-1094995529 |
入力の処理中に無効なデータが見つかりました |
AVERROR_MUXER_NOT_FOUND |
-1481985528 |
マクサーが見つかりません |
AVERROR_OPTION_NOT_FOUND |
-1414549496 |
オプションが見つかりません |
AVERROR_PATCHWELCOME |
-1163346256 |
FFmpeg にはまだ実装されていません。パッチは歓迎です |
AVERROR_PROTOCOL_NOT_FOUND |
-1330794744 |
プロトコルが見つかりません |
AVERROR_STREAM_NOT_FOUND |
-1381258232 |
ストリームが見つかりません |
AVERROR_BUG2 |
-541545794 |
|
AVERROR_UNKNOWN |
-1313558101 |
|
AVERROR_EXPERIMENTAL |
-733130664 |
|
AVERROR_INPUT_CHANGED |
-1668179713 |
|
AVERROR_OUTPUT_CHANGED |
-1668179714 |
|
AVERROR_HTTP_BAD_REQUEST |
-808465656 |
|
AVERROR_HTTP_UNAUTHORIZED |
-825242872 |
|
AVERROR_HTTP_FORBIDDEN |
-858797304 |
|
AVERROR_HTTP_NOT_FOUND |
-875574520 |
|
AVERROR_HTTP_OTHER_4XX |
-1482175736 |
|
AVERROR_HTTP_SERVER_ERROR |
-1482175992 |
したがって、コードに不正な状況の処理を追加することができ、不正な場合、ユーザーはユーザー名とパスワードの入力を求められます。
//ffmpegmanager.cpp
if ((ret = avformat_open_input(&streamFmtCtx, url.toStdString().c_str(), nullptr, nullptr)) != 0) {
if (ret == AVERROR_HTTP_UNAUTHORIZED)
{
//...
return;
}else{
//...
return;
}
}
vlc では、入力されたユーザー名とパスワードが検証に合格できない場合、入力が正しいか入力がキャンセルされるまで、検証ボックスが再度ポップアップ表示されます (ユーザー名を再入力する必要はありません) (詳細については冒頭を参照)の効果)。したがって、RTSP アドレスの有効性をチェックするなどの操作も追加します。
//ffmpegmanager.cpp
int rtspIndex = url.indexOf("rtsp://");
int atIndex = url.lastIndexOf("@");
if(rtspIndex != -1 && atIndex != -1){
QString couple = url.mid(rtspIndex + 7, atIndex - rtspIndex - 7);
username = couple;
if(couple.contains(':')){
username = couple.mid(0, couple.lastIndexOf(':'));
}
}
この時点で、コードはダイジェスト認証が必要な状況に適応できます。
エラーウィンドウを追加
vlc は、RTSP アドレスを開けない場合、エラー ウィンドウをポップアップ表示します。
また、エラー ウィンドウを追加し、すべてのエラーをアドレスを開けないものとして分類し、印刷します。
メモリリークを修正する
最後に、プログラムは RTSP ストリームを正常に受信できるようになりますが、これまでにないことが起こり、メモリが増加し続けます。この場合、通常、メモリ リークが発生します。以前に MP4 ファイルを読み取ったときにはメモリ リークが見つかりませんでした。ファイル サイズが固定されており、現在はストリーミングされ続けているためである可能性があります。この現象はより明白であり、次のことを行う必要があります。コードを確認してください。簡単に場所を特定した結果、次のコード ブロックが漏洩したことが判明しました。
while(av_read_frame(streamFmtCtx, packet) >= 0){
if(packet->stream_index == nVideoIndex){
if(avcodec_send_packet(decoderCtx, packet)>=0){
while((ret = avcodec_receive_frame(decoderCtx, decodedFrame)) >= 0){
//...
}
}
}
}
次に、文ごとに調査し、まず av_read_frame の宣言を確認します。
//avformat.h
/**
*.....
* On success, the returned packet is reference-counted (pkt->buf is set) and
* valid indefinitely. The packet must be freed with av_packet_unref() when
* it is no longer needed.
*.....
*/
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
ここで役立つ情報をいくつか紹介します。pkt は参照カウントされており、av_packet_unref() を実行しない場合は永久に有効になります。定義の確認を続けて、私たちの目標は、pkt に関連する参照カウントされたステートメントを見つけることです。
//avformat.cpp
int av_read_frame(AVFormatContext *s, AVPacket *pkt){
//...
ret = read_frame_internal(s, pkt);
ret = avpriv_packet_list_put(&s->internal->packet_buffer,
&s->internal->packet_buffer_end,
pkt, NULL, 0);
//...
}
最終的に、pkt はこれら 2 つの関数を実行します。探しているのは avpriv_packet_list_put です。引き続きその宣言と定義を見てみましょう。
//packet_internal.h
/**
* Append an AVPacket to the list.
*
* @param head List head element
* @param tail List tail element
* @param pkt The packet being appended. The data described in it will
* be made reference counted if it isn't already.
*/
int avpriv_packet_list_put(PacketList **head, PacketList **tail,
AVPacket *pkt,
int (*copy)(AVPacket *dst, const AVPacket *src),
int flags);
//avpacket.c
int avpriv_packet_list_put(PacketList **packet_buffer,
PacketList **plast_pktl,
AVPacket *pkt,
int (*copy)(AVPacket *dst, const AVPacket *src),
int flags)
{
//...
if (*packet_buffer)
(*plast_pktl)->next = pktl;
else
*packet_buffer = pktl;
*plast_pktl = pktl;
return 0;
}
最後に、pkt がバッファリングされたパケットに追加されます。他の詳細を調べる必要はありませんが、pkt がリストに追加されるため、ここで実際にメモリ リークが発生することだけを理解する必要があります。前のステートメントのヒントによると、av_packet_unref() を使用して pkt の参照を解放し、1 つの AVPacket を読み取って使用して終了した後、直接 av_packet_unref() を呼び出す必要があります。
while(av_read_frame(streamFmtCtx, packet) >= 0){
//...
av_packet_unref(packet);
}
av_packet_unref(packet);
追加後、メモリリークの問題は解決したことがわかったので、これ以上の調査は続けません。
この記事の特典として、無料の C++ オーディオおよびビデオ学習教材パッケージ + 学習ルートの概要、技術ビデオ/コード (オーディオおよびビデオの開発、面接の質問、FFmpeg、webRTC、rtmp、hls、rtsp、ffplay、コーデック、プッシュプルストリーミング、SRS)↓↓↓以下からご覧ください↓↓記事下部をクリックして無料で入手してください↓↓