Itseezのコンピュータービジョンプロジェクトの1
つでは 、
Raspberry Piを使用してWebカメラからのビデオストリームを処理していますが、最近、ビデオをフラッシュカードに記録する問題に遭遇しました。 問題は、CPUリソースが他のより重要なタスクによって消費されることでしたが、それでもビデオを保存する必要がありました。 また、これがfps(1秒あたりのフレーム数)に影響を与えない場合にのみ、どのコーデックを圧縮し、どのフォーマットを使用するかについての好みはありませんでした。 RAWからH.264までの多数のソフトウェアコーデックを試してみました(FFmpeg上の
OpenCVラッパーが使用されていました)。 高負荷では、fpsは20〜5フレーム/秒でサグしますが、画像は320x240の解像度で白黒です。 少しグーグルで、Raspberry PiプロセッサーにはH.264標準をサポートするハードウェアエンコーダーがあることがわかりました(私が知る限り、ライセンスはそのためだけに購入されました)。 さらに、OpenMAX標準に従ってエンコーダとの対話が実装されたため、OpenMAXを使用してコードを記述し、何が起こるかを確認することが決定されました。 ところで、それは非常に悪くないことが判明しました!
以下は、ハードウェアアクセラレーションを適用する前のサンプルビデオです。
。
OpenMAX(Open Media Acceleration)は、OSやハードウェアプラットフォームに依存せずに使用できるように設計された、ビデオおよびオーディオ処理のハードウェアアクセラレーションおよびさまざまなマルチメディアシステムと連携するツールセットを提供するクロスプラットフォームAPIです。 Raspberry Piは「クリーンな」OpenMAX IL(Integration Layer)APIを実装していないが、Broadcomチップに適合したバージョンを実装していることをすぐに予約します。 したがって、別のボードでコードを再利用しようとすると失敗する場合があります。 さらに、Raspberry Pi開発者が提供するOpenMAXラッパーを使用することも決定されました。
Raspbianのwheezyディストリビューションには、デフォルトのライブラリとOpenMAXの使用例が既にあり、これらは/ opt / vc /ディレクトリにあります。 / opt / vc / src / hello_pi / libs / ilclient /サブディレクトリには、OpenMAX上のラッパーのソースコードが含まれています。 これらは、ilclient.c ilclient.hおよびilcore.cファイルです。
タスクに戻りましょう。 320x240の解像度を持つ単一チャネル(つまり、白黒)のカメライメージがあります。この場合、OpenCVのIplImage構造であり、H.264コーデックを実行した後、AVIコンテナーに保存する必要があります。 ここから、次のサブタスクとそれらが解決された方法に従ってください。
- エンコードする前に、イメージを何らかのカラーモデル(YUV420pなど)に取り込む必要があります。これは、FFmpegライブラリセットバージョン0.7.1のswscaleモジュールを使用して行います。
- 結果のバッファをOpenMAXを使用してエンコードします。YUV420pの画像を含む入力バッファが入力バッファになり、H.264コーデックで処理した後の画像を含む出力バッファになるように事前に設定しました。
- 同じFFmpegを使用して、圧縮された画像をAVIコンテナに保存します。
ポイントについて:
変換
ここではすべてが簡単です。変換コンテキストと2つのAVPicture構造を作成します。 1つ目はシングルチャンネル画像用、2つ目はYUV420p用です:
#define WIDTH 320 #define HEIGHT 240 AVFrame *input_frame = avcodec_alloc_frame(); r = avpicture_alloc((AVPicture *) input_frame, PIX_FMT_GRAY8, WIDTH, HEIGHT); AVFrame *omx_input_frame = avcodec_alloc_frame(); r = avpicture_alloc((AVPicture *) omx_input_frame, PIX_FMT_YUV420P, WIDTH, HEIGHT); SwsContext *img_convert_ctx = sws_getContext(WIDTH, HEIGHT, PIX_FMT_GRAY8, WIDTH, HEIGHT, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
変換は、それぞれ次のとおりです。
avpicture_fill ((AVPicture *) input_frame, (uint8_t *) frame->imageData, PIX_FMT_GRAY8, WIDTH, HEIGHT); buf->nFilledLen = avpicture_fill ((AVPicture *) omx_input_frame, buf->pBuffer, PIX_FMT_YUV420P, WIDTH, HEIGHT); sws_scale(img_convert_ctx, (const uint8_t* const*)input_frame->data, input_frame->linesize, 0, HEIGHT, omx_input_frame->data, omx_input_frame->linesize);
bufはコーデック入力バッファで、frameはカメラからのIplImage *です。
コーディング
ここではより複雑です。エンコーダーを正しく正しい順序で初期化することが特に重要です。
OMX_VIDEO_PARAM_PORTFORMATTYPE format; OMX_PARAM_PORTDEFINITIONTYPE def; COMPONENT_T *video_encode; ILCLIENT_T *client; OMX_BUFFERHEADERTYPE *buf;
ここで、クライアントが作成され、入力バッファのパラメータが設定されます。画像の高さと幅、fps、カラースキームです。 ポート200は、video_encodeコンポーネントドライバー用に開発者が定義した入力ポート、201はこのコンポーネントの出力ポートです。 他の操作(ビデオのデコード、オーディオのエンコード、デコードなど)には、それに応じて他のポートが使用されます。
memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); format.nVersion.nVersion = OMX_VERSION; format.nPortIndex = VIDEO_ENCODE_PORT_OUT; format.eCompressionFormat = OMX_VIDEO_CodingAVC; r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoPortFormat, &format); OMX_VIDEO_PARAM_BITRATETYPE bitrateType; memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE)); bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); bitrateType.nVersion.nVersion = OMX_VERSION; bitrateType.eControlRate = OMX_Video_ControlRateVariable; bitrateType.nTargetBitrate = BITRATE; bitrateType.nPortIndex = VIDEO_ENCODE_PORT_OUT; r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoBitrate, &bitrateType); ilclient_change_component_state(video_encode, OMX_StateIdle);
上記では、出力バッファとビットレートが設定されています。 パラメーターformat.eCompressionFormat = OMX_VIDEO_CodingAVCは、画像がH.264でエンコードされることを決定するだけです。 ここで説明されているように、最適なビットレートは手動で計算されました:
www.ezs3.com/public/What_bitrate_should_I_use_when_encoding_my_video_How_do_I_optimize_my_video_for_the_web.cfm ilclient_enable_port_buffers(video_encode, VIDEO_ENCODE_PORT_IN, NULL, NULL, NULL); ilclient_enable_port_buffers(video_encode, VIDEO_ENCODE_PORT_OUT, NULL, NULL, NULL); ilclient_change_component_state(video_encode, OMX_StateExecuting);
次に、バッファーをオンにして、ドライバーを実行状態にします。
実際、エンコード自体:
buf = ilclient_get_input_buffer(video_encode, VIDEO_ENCODE_PORT_IN, 1); OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_encode), buf); out = ilclient_get_output_buffer(video_encode, VIDEO_ENCODE_PORT_OUT, 1); OMX_FillThisBuffer(ILC_GET_HANDLE(video_encode), out);
ビデオを保存
ここでも、FFmpegを使用した人にとって複雑なことはありません。 出力形式のコンテキストの初期化:
AVCodecContext *cc; char *out_file_name;
次に、記録用のファイルを開き、ヘッダーとコンテンツ形式に関する情報を書き込みます。
avio_open(&oc->pb, out_file_name, URL_WRONLY); avformat_write_header(oc, NULL); if (oc->oformat->flags & AVFMT_GLOBALHEADER) oc->streams[0]->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; av_dump_format(oc, 0, out_file_name, 1);
エンコードされた画像を保存するプロセス:
AVPacket pkt; AVRational omxtimebase = { 1, FPS}; OMX_TICKS tick = out->nTimeStamp; av_init_packet(&pkt); pkt.stream_index = video_st->index; pkt.data= out->pBuffer; pkt.size= out->nFilledLen; if (out->nFlags & OMX_BUFFERFLAG_SYNCFRAME) pkt.flags |= AV_PKT_FLAG_KEY; pkt.pts = av_rescale_q(((((uint64_t)tick.nHighPart)<<32) | tick.nLowPart), omxtimebase, oc->streams[video_st->index]->time_base); pkt.dts = AV_NOPTS_VALUE; av_write_frame(oc, &pkt); out->nFilledLen = 0;
av_rescale_q関数は、コーデックタイムスタンプをコンテナ内の対応するフレームタイムスタンプにキャストします。
ビルドするには、次のヘッダーファイルを含める必要があります。
#include "opencv2/core/core_c.h" #include "opencv2/imgproc/imgproc_c.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavutil/opt.h" #include "libavutil/avutil.h" #include "libavutil/mathematics.h" #include "libavformat/avio.h" #include "bcm_host.h" #include "ilclient.h"
したがって、FFmpegとOpenCVをビルドまたはインストールする必要もありますが、ビデオをファイルに保存するために他のライブラリを使用することを妨げるものは何もありません。 ファイル「bcm_host.h」および「ilclient.h」は、パス/ opt / vc /のサブディレクトリにあります。 OpenMAXクライアントコードが配置されているilclient.cおよびilcore.は、プロジェクトとともにアセンブルされます。
リンクするには、次のライブラリが必要です。
-L/opt/vc/lib -lbcm_host -lopenmaxil -lbcm_host -lvcos -lvchiq_arm –lpthread
さらに、たとえば、次のようにFFmpegおよびOpenCVライブラリを指定する必要があります。
-L/usr/local/lib -lavcodec -lavformat -lavutil -lswscale \ -L/usr/local/lib -lopencv_imgproc -lopencv_core
実際、それがすべてです。 保存されたビデオ機能をオンまたはオフにしてシステムの組み込みfpsエンコーダーを使用する場合、実質的に違いはありませんが、ソフトウェアコーデックを使用する場合、fpsは40-60%低下しました。 自分で見てください: