WebRTCに基づいて、カスタムコーディング解像度の送信を実現します

2020年に最もホットなテクノロジー分野は何ですか?間違いありません:オーディオとビデオ。2020年には、在宅勤務とオンライン教育の強力な発展は、音声とビデオから切り離せません。ビデオ会議、オンライン教育、ライブエンターテインメントはすべて、音声とビデオの典型的なアプリケーションシナリオです。
より豊富な使用シナリオでは、より良いユーザーエクスペリエンスを実現するために、解像度、フレームレート、ビットレートなど、より構成可能な項目を提供する方法を検討する必要があります。この記事では、特定の共有の「解決策」に焦点を当てます。

カスタムエンコーディング解像度を実装する方法

まず、「解像度」の定義を見てみましょう。解像度:画像内のピクセルデータの量を測定するパラメーター、および画像またはビデオのフレームの品質を測定するための主要なインジケーター。解像度が高いほど、画像のボリューム(バイト数)が大きくなり、画質が向上します。YUV i420フォーマットと解像度1080pのビデオストリームの場合、画像のフレームのボリュームは1920x1080x1.5x8 / 1024/1024≈23.73Mbitで、フレームレートは30なので、1のサイズは30x23.73≈711.9Mbitです。 。大量のデータには高いビットレートが必要であることがわかります。したがって、実際の送信プロセスでは、ビデオを圧縮してエンコードする必要があります。したがって、ビデオキャプチャデバイスによって収集された元のデータの解像度は取得解像度と呼ばれ、エンコーダに実際に送信されるデータの解像度はエンコーディング解像度と呼ばれます。
ビデオ画像が鮮明で比率が適切であるかどうかにかかわらず、これらはユーザーエクスペリエンスに直接影響します。カメラキャプチャ解像度の選択は限られており、必要な解像度をカメラで直接キャプチャできない場合があります。次に、シーンに応じて適切なエンコーディング解像度を構成する機能が重要です。キャプチャしたビデオを送信したいエンコーディング解像度に変換するにはどうすればよいですか?これが私たちが今日主に共有していることです。
WebRTCは、Googleのオープンソースで強力なリアルタイムのオーディオおよびビデオプロジェクトです。市場のほとんどの開発者は、WebRTCに基づいてリアルタイムのオーディオおよびビデオ通信ソリューションを構築しています。WebRTCの各モジュールには、優れた抽象化デカップリングプロセスがあり、これは2次開発に非常に適しています。リアルタイムのオーディオおよびビデオ通信ソリューションを構築するときは、WebRTCの設計アイデアとコードモジュールを理解して学習し、開発および拡張する能力を備えている必要があります。この記事では、WebRTCリリース72バージョンに基づいており、カスタムエンコーディング解決を実装する方法について説明します。
まず、次の質問について考えてみましょう。
収集からエンコードおよび送信までのビデオデータのパイプラインは何ですか?
設定されたエンコーディング解像度に応じて適切な取得解像度を選択するにはどうすればよいですか?
どうすれば希望のエンコーディング解像度を得ることができますか?
この記事の内容も上記の3点から共有します。

インストール環境とさまざまなビデオ資料がすべての人のために用意されており、資料はそれぞれのグループに配置されています:832218493(あなたはそれを拾う必要があります)

ここに画像の説明を挿入
ここに画像の説明を挿入

ビデオデータのパイプライン

まず、ビデオデータのパイプラインを見てみましょう。ビデオデータはVideoCapturerによって生成されます。VideoCapturerがデータを収集した後、VideoAdapterによって処理され、VideoSourceのVideoBroadcasterを介して登録済みのVideoSinkに配布されます。VideoSinkはエンコーダーエンコーダーシンクであり、ローカルプレビュープレビューシンクです。

ビデオ解像度の場合、プロセスは次のとおりです。目的の解像度をVideoCapturerに設定し、VideoCapturerがキャプチャする適切な解像度を選択し、元のキャプチャ解像度データがVideoAdapterによって計算され、期待に合わなくなった後、スケーリングおよびトリミングされてエンコードが取得されます。解像度のビデオデータは、エンコード後にエンコーダに送信されます。

ここに画像の説明を挿入

ここには2つの重要な質問があり
ます。VideoCapturerはどのようにして適切なキャプチャ解像度を選択しますか?
VideoAdapterはキャプチャ解像度をエンコード解像度にどのように変換しますか?

適切な取得解像度の選択方法

キャプチャ解像度の選択
WebRTCは、ビデオキャプチャの基本クラスであるvideocapturer.ccを抽象化します。抽象化VideoCapturerと呼びます。ビデオ解像度、フレームレート、サポートされているピクセル形式など、VideoCapturerのパラメータ属性を設定します。パラメータを設定し、最適な収集形式を計算してから、この収集形式を使用して、各プラットフォームのVDM(ビデオデバイスモジュール)を呼び出します。具体的な設定は次のとおりです。
コードはWebRTCのsrc / media / base /videocapturer.hから取得されます。

VideoCapturer.h
bool GetBestCaptureFormat(const VideoFormat& desired, VideoFormat* best_format);//内部遍历设备支持的所有采集格式调用GetFormatDistance()计算出每个格式的distance,选出distance最小的那个格式
int64_t GetFormatDistance(const VideoFormat& desired, const VideoFormat& supported);//根据算法计算出设备支持的格式与我们想要的采集格式的差距,distance为0即刚好满足我们的设置
void SetSupportedFormats(const std::vector<VideoFormat>& formats);//设置采集设备支持的格式fps,resolution,NV12, I420,MJPEG等       

設定されたパラメーターによると、デバイスごとにキャプチャ機能が異なるため、GetBestCaptureFormat()が設定に沿ったキャプチャ形式を取得できない場合があります。iOS、Android、PC、Macネイティブカメラキャプチャ、および外部USBカメラキャプチャは解像度をサポートします。特に外部USBカメラのキャプチャ能力が不均一です。したがって、ニーズに合わせてGetFormatDistance()を少し調整する必要があります。ニーズに合わせてコードを調整する方法について説明しましょう。

選択戦略のソースコード分析

最初にGetFormatDistance()のソースコードを分析し、コードの一部を抽出しましょう。
コードはWebRTCのsrc / media / base /videocapturer.ccから取得されます。

// Get the distance between the supported and desired formats.
int64_t VideoCapturer::GetFormatDistance(const VideoFormat& desired,
                                         const VideoFormat& supported) {
    
    
  //....省略部分代码
  // Check resolution and fps.
  int desired_width = desired.width;//编码分辨率宽
  int desired_height = desired.height;//编码分辨率高
  int64_t delta_w = supported.width - desired_width;//宽的差
  
  float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval);//采集设备支持的帧率
  float delta_fps = supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval);//帧率差
  int64_t aspect_h = desired_width
                         ? supported.width * desired_height / desired_width
                         : desired_height;//计算出设置的宽高比的高,采集设备的分辨率支持一般宽>高
  int64_t delta_h = supported.height - aspect_h;//高的差
  int64_t delta_fourcc;//设置的支持像素格式优先顺序,比如优先设置了NV12,同样分辨率和帧率的情况优先使用NV12格式采集
  
  //....省略部分降级策略代码,主要针对设备支持的分辨率和帧率不满足设置后的降级策略
  
  int64_t distance = 0;
  distance |=
      (delta_w << 28) | (delta_h << 16) | (delta_fps << 8) | delta_fourcc;

  return distance;
}

主にDistanceパラメータに焦点を当てます。距離はWebRTCの概念であり、設定された取得形式と、特定のアルゴリズム戦略に従ってデバイスでサポートされている取得形式の違いです。違いが小さいほど、デバイスでサポートされている取得形式は目的の形式に近くなります。 、これは0です。つまり、一致するだけです。

距離は4つの部分で構成されます:delta_w、delta_h、delta_fps、delta_fourcc、delta_w(解像度の幅)が最も重く、delta_h(解像度が高い)が2番目、delta_fps(フレームレート)が再び、delta_fourcc(ピクセル形式)が最後です。これによって引き起こされる問題は、ワイドの比率が高すぎ、ハイの比率が低すぎて、より正確にサポートされている解像度に一致しないことです。

例:
iPhone xs Max 800x800 fps:10を例にとると、いくつかの取得フォーマットの距離を抽出します。元のGetFormatDistance()アルゴリズムは需要を満たしていません。必要なのは800x800です。下の図から、結果は、期待どおりではなく、ベスト960x540です。

Supported NV12 192x144x10 distance 489635708928
Supported NV12 352x288x10 distance 360789835776
Supported NV12 480x360x10 distance 257721630720
Supported NV12 640x480x10 distance 128880476160
Supported NV12 960x540x10 distance 43032248320
Supported NV12 1024x768x10 distance 60179873792
Supported NV12 1280x720x10 distance 128959119360
Supported NV12 1440x1080x10 distance 171869470720
Supported NV12 1920x1080x10 distance 300812861440
Supported NV12 1920x1440x10 distance 300742082560
Supported NV12 3088x2316x10 distance 614332104704
Best NV12 960x540x10 distance 43032248320

選択戦略の調整

必要な解像度を得るには、分析によると、GetFormatDisctance()アルゴリズムを明確に調整し、解像度の重みを最大に調整し、フレームレートを2番目に調整する必要があります。ピクセル形式が指定されていない場合は、ピクセル形式が最後で、次のように詳細を変更します。

int64_t VideoCapturer::GetFormatDistance(const VideoFormat& desired,
const VideoFormat& supported) {
    
    
 //....省略部分代码
  // Check resolution and fps.
int desired_width = desired.width; //编码分辨率宽
int desired_height = desired.height; //编码分辨率高
  int64_t delta_w = supported.width - desired_width;
  int64_t delta_h = supported.height - desired_height;
  int64_t delta_fps = supported.framerate() - desired.framerate();
  distance = std::abs(delta_w) + std::abs(delta_h);
  //....省略降级策略, 比如设置了1080p,但是摄像采集设备最高支持720p,需要降级
  distance = (distance << 16 | std::abs(delta_fps) << 8 | delta_fourcc);
return distance;
}

変更後:距離は、解像度(delta_w + delta_h)、フレームレートdelta_fps、ピクセルdelta_fourccの3つの部分で構成されます。ここで、(delta_w + delta_h)の比率が最も高く、delta_fpsが2番目、delta_fourccが最後です。

例:
iPhone xs Max 800x800 fps:10を例として取り上げます。いくつかの取得フォーマットの距離を抽出します。GetFormatDistance()を変更した後、800x800が必要で、選択したベストは1440x1080です。ズームして800x800を取得できます。トリミング:期待に沿って(解像度要件が特に正確でない場合は、ダウングレード戦略を調整して1024x768を選択できます):

Supported NV12 192x144x10 distance 828375040
Supported NV12 352x288x10 distance 629145600
Supported NV12 480x360x10 distance 498073600
Supported NV12 640x480x10 distance 314572800
Supported NV12 960x540x10 distance 275251200
Supported NV12 1024x768x10 distance 167772160
Supported NV12 1280x720x10 distance 367001600
Supported NV12 1440x1080x10 distance 60293120
Supported NV12 1920x1080x10 distance 91750400
Supported NV12 1920x1440x10 distance 115343360
Supported NV12 3088x2316x10 distance 249298944
Best NV12 1440x1080x10 distance 60293120

取得解像度からエンコード解像度を達成する方法

ビデオデータが収集された後、VideoAdapter(WebRTCで抽象化)によって処理され、対応するシンク(WebRTCで抽象化)に配布されます。VideoAdapterを少し調整して、スケーリングとトリミングに必要なパラメータを計算し、LibYUVでビデオデータをスケーリングしてから、エンコード解像度にトリミングします(できるだけ多くの画像画像情報を保持するために、最初にスケーリングを使用します) 、余分なピクセル情報をトリミングすると、アスペクト比に一貫性がなくなります)。ここでは、2つの問題に焦点を当てます。

上記の例を引き続き使用して、解像度を800x800にしますが、取得する最適なキャプチャ解像度は1440x1080です。次に、1440x1080キャプチャ解像度から設定されたエンコード解像度800x800を取得するにはどうすればよいですか?
ビデオデータは、VideoCaptureからVideoSinkへのストリーミングプロセス中にVideoAdapterによって処理されますが、VideoAdapterは正確に何をしますか?

これら2つの問題について具体的な分析を始めましょう。まず、VideoAdapterとは何かを理解しましょう。

VideoAdapterの紹介

これは、VideoAdapterがWebRTCでどのように記述されているかです。

VideoAdapter adapts an input video frame to an output frame based on the specified input and output formats. The adaptation includes dropping frames to reduce frame rate and scaling frames.VideoAdapter is
thread safe.

VideoAdapterは、データの入出力を制御するためのモジュールであり、フレームレートと解像度を制御し、それに応じて解像度を低下させることができます。VQC(Video Quality Control)ビデオ品質管理モジュールでは、VideoAdapterの構成により、低帯域幅および高CPU条件下でフレームレートを動的に下げることができ、解像度を動的にスケーリングしてスムーズなビデオを確保できます。ユーザーエクスペリエンスを向上させるため。
src / media / base /videoadapter.hから取得

VideoAdapter.h
bool AdaptFrameResolution(int in_width,
int in_height,
                            int64_t in_timestamp_ns,
int* cropped_width,
int* cropped_height,
int* out_width,
int* out_height);
void OnOutputFormatRequest(
const absl::optional<std::pair<int, int>>& target_aspect_ratio,
const absl::optional<int>& max_pixel_count,
const absl::optional<int>& max_fps);
void OnOutputFormatRequest(const absl::optional<VideoFormat>& format);

VideoAdapterソースコード分析

VideoAdapterは、設定されたdesried_formatに従ってAdaptFrameResolution()を呼び出し、キャプチャ解像度からエンコード解像度にスケーリングおよびトリミングする必要があるcropped_width、cropped_height、out_width、out_heightパラメーターを計算します。WebRTCのネイティブadaptFrameResolutionは、計算されたものに基づいてズームパラメーターを計算します。ピクセル領域、および正確な幅と高さを取得できません:
src / media / base /videoadapter.ccから取得

bool VideoAdapter::AdaptFrameResolution(int in_width,
int in_height,
                                        int64_t in_timestamp_ns,
int* cropped_width,
int* cropped_height,
int* out_width,
int* out_height) {
    
    
//.....省略部分代码
// Calculate how the input should be cropped.
if (!target_aspect_ratio || target_aspect_ratio->first <= 0 ||
        target_aspect_ratio->second <= 0) {
    
    
      *cropped_width = in_width;
      *cropped_height = in_height;
    } else {
    
    
const float requested_aspect =
          target_aspect_ratio->first /
static_cast<float>(target_aspect_ratio->second);
      *cropped_width =
          std::min(in_width, static_cast<int>(in_height * requested_aspect));
      *cropped_height =
          std::min(in_height, static_cast<int>(in_width / requested_aspect));
    }
const Fraction scale;//vqc 缩放系数 ....省略代码
    // Calculate final output size.
    *out_width = *cropped_width / scale.denominator * scale.numerator;
    *out_height = *cropped_height / scale.denominator * scale.numerator;
 }

例:
iPhone xs Max 800x800 fps:10を例にとると、エンコード解像度を800x800に、取得解像度を1440x1080に設定します。ネイティブアルゴリズムによると、計算される新しい解像度は720x720であり、期待を満たしていません。
VideoAdapterの調整
VideoAdapterは、VQC(Video Quality Control Module)のビデオ品質調整の重要な部分です。VQCがフレームレート制御、解像度スケーリング、およびその他の操作を完了できる理由は、主にVideoAdapterに依存しているため、変更ではVQCへの影響を考慮する必要があります。 。
VQCモジュールの解像度制御に影響を与えることなく、目的の解像度を正確に取得するために、AdaptFrameResolution()に次の調整を行います。

bool VideoAdapter::AdaptFrameResolution(int in_width,
int in_height,
                                        int64_t in_timestamp_ns,
int* cropped_width,
int* cropped_height,
int* out_width,
int* out_height) {
    
    
  //....省略部分代码
bool in_more =
        (static_cast<float>(in_width) / static_cast<float>(in_height)) >=
        (static_cast<float>(desired_width_) /
static_cast<float>(desired_height_));
if (in_more) {
    
    
        *cropped_height = in_height;
        *cropped_width = *cropped_height * desired_width_ / desired_height_;
    } else {
    
    
      *cropped_width = in_width;
      *cropped_height = *cropped_width * desired_height_ / desired_width_;
    }
    *out_width = desired_width_;
    *out_height = desired_height_;
    //....省略部分代码
return true;
}

例:
iPhone xs Max 800x800 fps:10を例として、エンコード解像度を800x800に、取得解像度を1440x1080に設定します。調整されたアルゴリズムによると、計算されたエンコード解像度は800x800であり、予想どおりです。

総括する

この記事では、主にWebRTCに基づくエンコーディング解像度の構成を実現する方法を紹介します。ビデオエンコーディングの解像度を変更する場合は、ビデオデータの収集、送信、処理、エンコーディングなどのプロセス全体を理解する必要があります。解像度を送信するときにカスタムエンコーディングを実装する場合、今日共有するいくつかの重要な手順を次に示します。 :
まず、目的のエンコード解像度を設定
します。VideoCapturer.ccを変更して、エンコード解像度に応じて適切な取得解像度を選択します。VideoAdapter.ccを
変更して、取得解像度のスケーリングとトリミングをエンコード解像度パラメーターに計算し
ます。libyuvを使用して、スケーリングとトリミングを行います。スケーリングとトリミングのパラメータに従って元のデータをエンコード解像度に変換し、エンコードと
送信のために新しいデータをエンコーダに送信します。
最後に、完了です。
同様に、この考えに基づいて他の調整を行うこともできます。以上がこの記事の紹介です。オーディオおよびビデオ関連の技術的な実装をさらに共有し、関連する技術を交換するためのメッセージを残していきます。
5Gの時代が到来し、オーディオとビデオのアプリケーション分野はますます広くなり、すべてが有望です。

おすすめ

転載: blog.csdn.net/lingshengxueyuan/article/details/112986451