一.命令行介绍
FFmpeg 支持多种平台的硬件解码,包括 H.264 (AVC) 和 H.265 (HEVC) 编解码器。以下是在不同平台上使用 FFmpeg 进行硬件解码的概述:
1. NVIDIA GPU (NVDEC)
适用于 Windows、Linux 和 macOS(较旧的 Mac 设备)。
- H.264: `-c:v h264_cuvid`
- H.265: `-c:v hevc_cuvid`
示例:
```
ffmpeg -hwaccel cuvid -c:v h264_cuvid -i input.mp4 -c:v h264_nvenc output.mp4
```
2. Intel Quick Sync Video
适用于 Windows 和 Linux,支持搭载 Intel 集成显卡的处理器。
- H.264: `-c:v h264_qsv`
- H.265: `-c:v hevc_qsv`
示例:
```
ffmpeg -hwaccel qsv -c:v h264_qsv -i input.mp4 -c:v h264_qsv output.mp4
```
3. AMD GPU (AMD AMF)
主要适用于 Windows。
- H.264: `-c:v h264_amf`
- H.265: `-c:v hevc_amf`
示例:
```
ffmpeg -hwaccel amf -c:v h264_amf -i input.mp4 -c:v h264_amf output.mp4
```
4. Apple Video Toolbox
适用于 macOS 和 iOS 设备。
- H.264: `-c:v h264_videotoolbox`
- H.265: `-c:v hevc_videotoolbox`
示例:
```
ffmpeg -hwaccel videotoolbox -c:v h264_videotoolbox -i input.mp4 -c:v h264_videotoolbox output.mp4
```
5. VAAPI (Video Acceleration API)
主要用于 Linux 系统。
- H.264: `-c:v h264_vaapi`
- H.265: `-c:v hevc_vaapi`
示例:
```
ffmpeg -hwaccel vaapi -vaapi_device /dev/dri/renderD128 -i input.mp4 -c:v h264_vaapi output.mp4
```
6. DXVA2 (DirectX Video Acceleration)
适用于 Windows 系统。
使用 `-hwaccel dxva2` 选项。
示例:
```
ffmpeg -hwaccel dxva2 -i input.mp4 -c:v h264_nvenc output.mp4
```
注意事项:
1. 确保你的 FFmpeg 版本支持所需的硬件加速。可以使用 `ffmpeg -hwaccels` 命令查看支持的硬件加速方法。
2. 某些硬件加速可能需要特定的驱动程序或额外的库。
3. 不同的 GPU 和驱动版本可能支持不同的功能,请查阅相关文档以获取特定配置的详细信息。
4. 在某些情况下,可能需要同时指定 `-hwaccel` 和 `-c:v` 选项以获得最佳性能。
5. 硬件解码的效果可能因具体的硬件和视频内容而异,建议进行测试以确定最佳配置。
使用硬件加速可以显著提高解码性能,特别是对于高分辨率或高帧率的视频。然而,输出质量可能会略有不同,因此在对质量要求极高的场景中,可能仍需要使用软件解码。
二.相关代码
以下是在不同平台上使用 FFmpeg 进行 H.264 和 H.265 硬件解码的 C++ 示例。这些示例使用 FFmpeg 的 C API,并假设你已经正确安装和链接了 FFmpeg 库。
首先,这里是一个通用的函数框架,我们将在每个平台特定的示例中使用:
```cpp
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/hwcontext.h>
#include <libavutil/opt.h>
}
#include <iostream>
#include <stdexcept>
void decode_video(const char* filename, enum AVHWDeviceType type) {
AVFormatContext *input_ctx = nullptr;
AVCodecContext *decoder_ctx = nullptr;
AVStream *video = nullptr;
const AVCodec *decoder = nullptr;
AVPacket *packet = nullptr;
AVFrame *frame = nullptr;
int video_stream, ret;
// 打开输入文件
if (avformat_open_input(&input_ctx, filename, nullptr, nullptr) < 0) {
throw std::runtime_error("Could not open input file");
}
if (avformat_find_stream_info(input_ctx, nullptr) < 0) {
throw std::runtime_error("Could not find stream information");
}
// 找到视频流
video_stream = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (video_stream < 0) {
throw std::runtime_error("Could not find video stream");
}
video = input_ctx->streams[video_stream];
// 根据视频流的编解码器ID找到合适的解码器
decoder = avcodec_find_decoder(video->codecpar->codec_id);
if (!decoder) {
throw std::runtime_error("Failed to find decoder");
}
// 分配解码器上下文
decoder_ctx = avcodec_alloc_context3(decoder);
if (!decoder_ctx) {
throw std::runtime_error("Failed to allocate decoder context");
}
if (avcodec_parameters_to_context(decoder_ctx, video->codecpar) < 0) {
throw std::runtime_error("Failed to copy codec parameters to decoder context");
}
// 设置硬件加速
if (hw_decoder_init(decoder_ctx, type) < 0) {
throw std::runtime_error("Failed to initialize hardware decoder");
}
if (avcodec_open2(decoder_ctx, decoder, nullptr) < 0) {
throw std::runtime_error("Failed to open codec");
}
// 分配数据包和帧
packet = av_packet_alloc();
if (!packet) {
throw std::runtime_error("Failed to allocate packet");
}
frame = av_frame_alloc();
if (!frame) {
throw std::runtime_error("Failed to allocate frame");
}
// 读取数据包并解码
while (av_read_frame(input_ctx, packet) >= 0) {
if (packet->stream_index == video_stream) {
ret = avcodec_send_packet(decoder_ctx, packet);
if (ret < 0) {
throw std::runtime_error("Error sending packet for decoding");
}
while (ret >= 0) {
ret = avcodec_receive_frame(decoder_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
throw std::runtime_error("Error during decoding");
}
// 处理解码后的帧
process_frame(frame);
}
}
av_packet_unref(packet);
}
// 清理资源
avformat_close_input(&input_ctx);
av_frame_free(&frame);
av_packet_free(&packet);
avcodec_free_context(&decoder_ctx);
}
// 处理解码后的帧(这里只是打印一些信息)
void process_frame(AVFrame *frame) {
std::cout << "Processed frame: " << frame->pts << std::endl;
}
```
现在,让我们看看如何为不同的平台实现 `hw_decoder_init` 函数:
1. NVIDIA GPU (NVDEC)
```cpp
static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
int err = 0;
if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, nullptr, nullptr, 0)) < 0) {
fprintf(stderr, "Failed to create specified HW device.\n");
return err;
}
ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
return err;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <input file>" << std::endl;
return 1;
}
try {
decode_video(argv[1], AV_HWDEVICE_TYPE_CUDA);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
```
2. Intel Quick Sync Video
```cpp
static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
int err = 0;
if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, nullptr, nullptr, 0)) < 0) {
fprintf(stderr, "Failed to create specified HW device.\n");
return err;
}
ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
return err;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <input file>" << std::endl;
return 1;
}
try {
decode_video(argv[1], AV_HWDEVICE_TYPE_QSV);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
```
3. AMD GPU (AMD AMF)
```cpp
static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
int err = 0;
if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, nullptr, nullptr, 0)) < 0) {
fprintf(stderr, "Failed to create specified HW device.\n");
return err;
}
ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
return err;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <input file>" << std::endl;
return 1;
}
try {
decode_video(argv[1], AV_HWDEVICE_TYPE_DRM);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
```
4. Apple Video Toolbox
```cpp
static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
int err = 0;
if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, nullptr, nullptr, 0)) < 0) {
fprintf(stderr, "Failed to create specified HW device.\n");
return err;
}
ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
return err;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <input file>" << std::endl;
return 1;
}
try {
decode_video(argv[1], AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
```
5. VAAPI (Video Acceleration API)
```cpp
static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
int err = 0;
if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, "/dev/dri/renderD128", nullptr, 0)) < 0) {
fprintf(stderr, "Failed to create specified HW device.\n");
return err;
}
ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
return err;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <input file>" << std::endl;
return 1;
}
try {
decode_video(argv[1], AV_HWDEVICE_TYPE_VAAPI);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
```
6. DXVA2 (DirectX Video Acceleration)
```cpp
static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
int err = 0;
if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, nullptr, nullptr, 0)) < 0) {
fprintf(stderr, "Failed to create specified HW device.\n");
return err;
}
ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
return err;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <input file>" << std::endl;
return 1;
}
try {
decode_video(argv[1], AV_HWDEVICE_TYPE_DXVA2);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
```
注意:
1. 这些示例仅展示了基本的硬件解码流程,实际应用中可能需要更多的错误处理和资源管理。
2. 编译时需要链接相应的 FFmpeg 库,例如:
```
g++ -o decoder decoder.cpp -lavformat -lavcodec -lavutil -lswscale
```
3. 某些平台可能需要额外的设置或库,请参考 FFmpeg 文档和相应平台的开发指南。
4. 这些示例主要关注解码过程,没有包括视频渲染或其他后续处理步骤。
5. 在使用硬件加速时,可能需要在系统中安装相应的驱动程序和支持库。
通过这些示例,你应该能够在不同平台上使用 FFmpeg 进行 H.264 和 H.265 的硬件解码。根据具体需求,你可能需要进一步优化或扩展这些代码。