Software decoding — ソフトウェアライブラリを使用して中央プロセッサ(CPU)がメディアデータを展開するプロセスであり、SoCのハードウェアブロックを使用しません。ソフトウェアデコーダはクロスプラットフォームライブラリとして実装されています:AV1用のFFmpegとlibavcodec、dav1d。FFmpegドキュメント(2026年)によると、libavcodecは200以上のコーデックをサポートしており、software decodingが希少な形式を再生する唯一の方法となっています。
主要ポイント
Software decodingは、すべての計算処理を汎用CPUコアで実行するメディアデータの展開方法です。各コーデックに専用の物理ブロックがあるハードウェアデコーディングとは異なり、ソフトウェアデコーダはプロセッサ命令を使用して同じアルゴリズムを実行する通常のコードです。
ソフトウェアデコーダはC/C++で記述され、特定のCPUアーキテクチャ向けに最適化されています:モバイルデバイス向けARM NEON SIMD命令、デスクトップ向けIntel SSE/AVX。FFmpegのlibavcodecライブラリには、さまざまなプラットフォーム向けに最適化されたアセンブリコードが数万行含まれており、ソフトウェアデコーディングは強力なCPU上でAV1のような重い形式でも十分なパフォーマンスを達成できます。
ソフトウェアデコーディングの主な利点は汎用性です。ハードウェアデコーダが4〜5の主要形式(H.264、H.265、VP9、AV1)のみをサポートする場合、FFmpegは200以上のコーデックをデコードできます:最新のAV1やH.265から、過去のSorenson Spark、RealVideo、Motion JPEGまで。これにより、ソフトウェアデコーディングは、プロフェッショナルなビデオエディタ、ビデオ監視システム、専用プレーヤーなど、標準的でないメディアデータを扱うアプリケーションにとって不可欠なツールとなっています。
ソフトウェアデコーディングはハードウェアデコーディングと同じ段階を踏みますが、汎用CPU上で実行されます。各段階は関数として実装され、各マクロブロックまたはフレームに対して順次呼び出されます。重要な違いは柔軟性です:開発者はパイプラインを変更し、デコード段階の間にフィルターや後処理を追加できます。
典型的なソフトウェアデコーダは、アルゴリズムの個別の段階を実装するモジュールで構成されています。エントロピーデコードモジュールはビットストリームを読み取り、量子化されたDCT係数を復元します。H.264の場合、このモジュールはCABAC(文脈適応型二値算術符号化)を実装します。これは条件分岐を持つ複雑なアルゴリズムで、ハードウェアでの高速化は難しいですが、優れた分岐予測子を持つCPUでは効率的に実行されます。
逆量子化モジュールは係数に量子化ステップを乗算し、逆DCTモジュールは離散コサイン変換を適用します。逆DCTのソフトウェア実装は、高速ChenアルゴリズムまたはLoefflerアルゴリズムを使用し、8x8ブロックの乗算累積演算回数を4096から256に削減します。NEON SIMD命令(ARM)またはSSE(x86)は、1命令あたり4〜8個の係数を処理できるため、スカラーコードと比較して4〜8倍の高速化を実現します。
動き補償モジュールが最もメモリを消費します。動きベクトルに従って参照フレームから領域を抽出し、サブピクセル補間を適用します。H.265の場合、補間精度は1/8ピクセルに達し、輝度には8タップFIRフィルター、色差には4タップが必要です。ソフトウェア実装ではキャッシュから大量の参照フレームデータをロードする必要があり、CPUでの高解像度デコード時に動き補償がボトルネックになります。
Apple A17やQualcomm Snapdragon 8 Gen 2などの最新のモバイルプロセッサは、フレーム落ちなしで1080p H.264のソフトウェアデコーディングに十分なパフォーマンスを持つ6〜8コアを備えています。しかし、4Kコンテンツ、特にH.265やAV1形式の場合、CPUでのソフトウェアデコーディングは困難になる可能性があります:全コアの一般的な負荷は70〜90%に達し、マルチタスクにとって重要です。高性能コア(Apple Performance、Qualcomm Kryo Prime)は小型の効率的なコアと比較して約4〜5倍のパフォーマンスを提供しますが、比例して多くの電力を消費します。
ソフトウェアデコーダ市場にはいくつかの主要なライブラリがあり、それぞれがニッチに最適化されています。デコーダの選択は、必要な形式、プラットフォーム、ライセンス制約によって異なります。
FFmpegは業界におけるソフトウェアデコーディングの事実上の標準です。libavcodecライブラリには、すべての主要およびほとんどの希少なコーデック用のデコーダが含まれており、すべてのコンテナ(MP4、MKV、AVI、MOV、WebM)をサポートし、すべてのプラットフォームで動作します。FFmpegはLGPL/GPLの下でライセンスされており、商用利用にはライセンス条件の遵守が必要です。モバイルデバイスでは、FFmpegはラッパーを介して使用されます:iOSおよびAndroid向けffmpeg-kit、React Native向けmobile-ffmpeg。
Dav1dはVideoLAN(VLCの開発元)によるソフトウェアAV1デコーダで、SIMD最適化を施したC言語で記述されています。その主な目標は、ハードウェアサポートのないCPU上でAV1を可能な限り高速にソフトウェアデコードすることです。Dav1dは、積極的な最適化(手動キャッシュ管理、後処理フィルターのJITコンパイル、重要な関数のベクトル化)のおかげで、Alliance for Open Mediaのリファレンスlibaomデコーダよりも30〜50%高速です。
モバイルデバイスでは、dav1dはフラッグシップSoC(Apple A16+、Snapdragon 8 Gen 2+)で1080p AV1をリアルタイムデコードできますが、4Kには強力なCPUが必要です。例えば、Apple M1ではソフトウェアdav1dは4K AV1で約60 FPSを達成し、Snapdragon 8 Gen 2では約35 FPSに達します。モバイルデバイスでの安定した4K AV1再生には、ハードウェアサポートが依然として推奨されています。
| デコーダ | 形式 | プラットフォーム | ライセンス |
|---|---|---|---|
| libavcodec | 200以上のコーデック | すべて | LGPL/GPL |
| dav1d | AV1 | すべて | BSD 2-Clause |
| libaom | AV1 | すべて | BSD 2-Clause |
| MediaFoundation | H.264, H.265 | Windows | プロプライエタリ |
ソフトウェアデコーディングとハードウェアデコーディングの選択は、互換性と効率性のトレードオフです。以下の表は、主要な特性の詳細な比較を示しています。
| パラメータ | Software Decoding | Hardware Decoding |
|---|---|---|
| サポートされる形式 | 200以上のコーデック | 4〜6のコーデック |
| 消費電力 | 1.5〜5 W | 0.2〜0.8 W |
| カスタマイズ | パイプラインを完全に制御 | API経由のみ |
| レイテンシ | 30〜80 ms | 5〜15 ms |
| 放熱 | 高い(45〜50 C) | 低い(35〜40 C) |
| コーデックの更新 | ライブラリの更新による | 新しいSoCのみ |
ソフトウェアデコーディングは最大の柔軟性を提供します:開発者はアルゴリズムを変更し、カスタムフィルターを追加し、独自の処理パイプラインを実装できます。例えば、ビデオ編集アプリケーションでは、各デコード段階を色補正やエフェクトオーバーレイのためにGPUにリダイレクトできます — これはデコードに対するソフトウェア制御があって初めて可能です。
しかし、柔軟性の代償は消費電力です。3000〜5000 mAhのバッテリーを搭載したモバイルデバイスの場合、継続的なソフトウェアデコーディングは視聴時間を10〜15時間(ハードウェア)から2〜4時間に短縮します。45〜50度へのCPUの加熱はスロットリング(過熱を防ぐためのプロセッサ周波数の低下)を引き起こし、フレーム落ちやユーザーエクスペリエンスの低下につながる可能性があります。
両方のモバイルプラットフォームでのソフトウェアデコーディングの実践的な実装を見てみましょう。iOSではソフトウェアデコーディングはFFmpegを介して使用され、AndroidではJava/Kotlinラッパーを持つ同じライブラリを介して使用されます。
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
class SoftwareDecoder {
AVCodecContext* codecCtx;
public:
bool init(const char* filename) {
AVFormatContext* fmtCtx = nullptr;
avformat_open_input(&fmtCtx, filename, nullptr, nullptr);
avformat_find_stream_info(fmtCtx, nullptr);
int videoStream = av_find_best_stream(
fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0
);
AVCodec* decoder = avcodec_find_decoder(
fmtCtx->streams[videoStream]->codecpar->codec_id
);
codecCtx = avcodec_alloc_context3(decoder);
avcodec_parameters_to_context(codecCtx,
fmtCtx->streams[videoStream]->codecpar);
avcodec_open2(codecCtx, decoder, nullptr);
return true;
}
AVFrame* decodePacket(AVPacket* packet) {
avcodec_send_packet(codecCtx, packet);
AVFrame* frame = av_frame_alloc();
int ret = avcodec_receive_frame(codecCtx, frame);
return (ret >= 0) ? frame : nullptr;
}
};
このコードは、ソフトウェアデコーディングのための最小限のFFmpegパイプラインを示しています。avformat_open_inputはファイルを開き、コンテナ形式を判別します。avcodec_find_decoderは任意のコーデックに適したデコーダを自動的に見つけます。decodePacketメソッドは新しいAPI(avcodec_send_packet / avcodec_receive_frame)を使用し、リアルタイムアプリケーション向けにAV_CODEC_FLAG_LOW_DELAYフラグを有効にしたマルチスレッドデコードをサポートします。
class SoftwareDecoder(private val context: Context) {
fun decodeVideo(inputPath: String, outputFolder: String) {
val cmd = "-i $inputPath -vf fps=1 $outputFolder/frame_%04d.jpg"
FFmpegExecutor(context).executeCommand(cmd) { rc ->
Log.d("Decoder", "Finished with rc: $rc")
}
}
fun getFrameCount(filePath: String): Int {
val probe = MediaMetadataRetriever()
probe.setDataSource(filePath)
val duration = probe.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_DURATION
)?.toIntOrNull() ?: 0
val fps = probe.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT
)?.toIntOrNull() ?: 0
probe.release()
return fps
}
}
Kotlinの例では、FFmpegExecutorを使用してビデオから1秒あたり1フレームを抽出します。パラメータ -vf fps=1 は60フレーム中59フレームをスキップするフィルターを作成し、CPU負荷を軽減します。このアプローチは、モバイルアプリケーションでプレビューやプレースホルダーを作成するのに役立ちます。リアルタイムのソフトウェアデコーディングには、JNIを介して直接低レベルのlibavcodec APIを使用することをお勧めします。
#include <dav1d/dav1d.h>
int decode_av1_frame(const uint8_t* data, size_t size) {
Dav1dContext* ctx = nullptr;
Dav1dSettings settings = { 0 };
dav1d_default_settings(&settings);
settings.n_threads = 4;
dav1d_open(&ctx, &settings);
Dav1dData dav1d_data = { 0 };
dav1d_data_wrap(&dav1d_data, data, size, nullptr, nullptr);
Dav1dPicture pic = { 0 };
if (dav1d_send_data(ctx, &dav1d_data) == 0) {
dav1d_get_picture(ctx, &pic);
}
dav1d_close(&ctx);
return pic.p.w;
}
Dav1dはミニマルなAPIを提供します:dav1d_openは指定されたスレッド数でデコーダコンテキストを作成し、dav1d_send_dataは圧縮されたビットストリームを受け入れ、dav1d_get_pictureはYUV420形式でデコードされたフレームを返します。モバイルデバイスの場合、最適なスレッド数(n_threads)はパフォーマンスCPUコアの数から1を引いた数で、UIスレッドにリソースを残します。Dav1dはDav1dPicAllocatorもサポートしており、メモリ管理とGPUへのフレーム転送時の不要なコピーを回避します。
消費電力が高いにもかかわらず、ハードウェアデコーディングが要求される機能を提供できないいくつかのシナリオでは、ソフトウェアデコーディングが不可欠です。これらのシナリオを理解することで、開発者はアーキテクチャ上の決定を下すのに役立ちます。
ハードウェアデコーダは最新の形式のみをサポートします。アプリケーションがアーカイブ録画、ビデオ監視(MJPEG、H.263)、プロフェッショナルコーデック(ProRes、DNxHD、CineForm)、またはサードパーティソースからのコンテンツを扱う場合、FFmpegを介したソフトウェアデコーディングが唯一の選択肢となります。ProResは、ハードウェアサポートのあるApple A13+チップを除くすべてのデバイスでソフトウェアのみでデコードされます。H.263の場合、最新のSoCにはハードウェアサポートがなく、ソフトウェアデコーディングのみが可能です。
ソフトウェアデコーディングは、各フレーム処理段階への完全なアクセスを提供します。これは、出力前にデコードされたデータに直接フィルター(ぼかし、ノイズ低減、シャープ化)を適用する必要があるアプリケーションにとって重要です。FFmpegフィルターを使用すると、複雑なチェーンを構築できます:デコード -> 色補正 -> スケーリング -> 字幕オーバーレイ -> エンコード — すべて単一のライブラリ内で、異なるAPI間でデータを転送することなく実行できます。
推奨されるメディアプレーヤーのアーキテクチャはハイブリッドです:ハードウェアデコーディングをプライマリとし、ソフトウェアデコーディングをフォールバックとします。再生前に、アプリケーションは指定されたコーデックのハードウェアデコーダの利用可能性を確認します。デコーダが見つからない場合は、FFmpegを介してソフトウェアデコーディングが開始されます。この戦略により、主要な形式のパフォーマンスを犠牲にすることなく、最大の互換性が確保されます。ハードウェアサポートは同じモデルのデバイスでもSoCのリビジョンによって異なる可能性があるため、可用性チェックは起動のたびに実行する必要があります。
よくある質問
CPUは多くの異なるタスクを実行する汎用プロセッサです。デコードでは、共有の計算ユニットとキャッシュメモリを使用し、単一のタスクを実行している場合でもエネルギーを消費します。ハードウェアデコーダは固定パイプラインを持つ高度に専門化された回路であり、すべてのトランジスタがデコードのみに専念しているため、消費電力を根本的に削減します。
H.264/H.265の場合 — SIMD最適化を有効にしたFFmpegのlibavcodec。AV1の場合 — dav1dで、リファレンスlibaomよりも30〜50%高速です。モバイルデバイスでは、dav1dのパフォーマンスにより、フラッグシップSoC(A16+、Dimensity 9200+)でリアルタイムの1080p AV1デコードが可能です。
はい、FFmpegは両方のプラットフォームに移植されています。iOSの場合は、ffmpeg-kit — すべてのコーデックと形式をサポートする既製ビルドを使用します。Androidの場合は、mobile-ffmpegを使用するか、NDKを介してFFmpegをビルドします。商用配布の場合はGPL/LGPLライセンスの制約に注意してください。
リアルタイムデコードとは、CPUが画面に表示されるよりも速くフレームをデコードできることを意味します(通常30または60 FPS)。1080p H.264の場合、最新のモバイルCPUは余裕を持って処理し、1つのパフォーマンスコアの約30〜50%を使用します。4K H.265の場合、CPUでのリアルタイム処理は、全コアで70〜90%の負荷があるフラッグシップSoCでのみ可能です。
thread_countフラグを使用してFFmpegでマルチスレッドデコード(フレームレベルの並列処理)を使用し、シナリオで許容される場合はskip_frameをBフレームに設定し、デコード前にscaleフィルターで解像度を下げます。dav1dを使用したAV1の場合は、n_threads = CPUコア数から1を引いた値を使用します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。