gpt4 book ai didi

c++ - 使用libavcodec和VAAPI从C++编码视频

转载 作者:行者123 更新时间:2023-12-02 10:37:18 30 4
gpt4 key购买 nike

我正在尝试使用libavcodec(版本3.4.6)在H.264中编码视频。当我使用软件编码器“libx264”时,它可以工作,但是当我尝试将我的Intel cpu的硬件编码器与VAAPI一起使用时,它却无法工作。通过VAAPI使用ffmpeg进行硬件编码可从命令行(使用here的命令)进行。

显然,没有示例或教程如何使用VAAPI和libav *进行编码。我通读了ffmpeg's examples中涉及相关用例(硬件解码,软件编码,混合)的内容,并尝试相应地进行调整。

当我设置VAAPI编码器时,avcodec_open2()返回AVERROR(EINVAL)(-22),并将以下错误消息打印到控制台:

Mismatching AVCodecContext.pix_fmt and AVHWFramesContext.format



您可以在我的代码中 Encoder::setupEncoder()的末尾找到它。我想念什么?

以下是我的代码,该代码分为三个文件:

编码器
#ifndef ENCODER_H
#define ENCODER_H
#include <cassert>

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/hwcontext.h>
}

class Encoder
{
public:
Encoder(const bool hwAccel);
void addFrame(AVFrame* frame);
void flush();

static constexpr int s_width = 640;
static constexpr int s_height = 480;
static constexpr int s_fps = 25;
private:
void setup();
void setupEncoder();
void encodeFrame(AVFrame* frame);

// members
int m_frameId = 1;
const bool m_hardwareAcceleration = false;

AVCodecContext* m_encoder = nullptr;
AVFormatContext* m_muxer = nullptr;
AVStream* m_avStream = nullptr;
AVBufferRef* m_device = nullptr;

AVFrame* m_hwFrame = nullptr;
};

#endif // ENCODER_H

编码器
#include "encoder.h"

extern "C" {

static enum AVPixelFormat get_vaapi_format(AVCodecContext*, const enum AVPixelFormat *pix_fmts)
{
const enum AVPixelFormat *p;
for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
if (*p == AV_PIX_FMT_VAAPI)
return *p;
}
fprintf(stderr, "Failed to get HW surface format.\n");
return AV_PIX_FMT_NONE;
}

}

Encoder::Encoder(const bool hwAccel)
: m_hardwareAcceleration(hwAccel)
{
setup();
}
void Encoder::addFrame(AVFrame* frame)
{
AVFrame* frameToEncode = frame;
if(m_hardwareAcceleration) {
assert(frame->format == AV_PIX_FMT_NV12);
av_hwframe_transfer_data(m_hwFrame, frame, 0);
assert(m_hwFrame->format == AV_PIX_FMT_VAAPI);
frameToEncode = m_hwFrame;
}

frameToEncode->pts = m_frameId++;
encodeFrame(frameToEncode);
}
void Encoder::flush()
{
encodeFrame(nullptr);
av_write_trailer(m_muxer);
}

void Encoder::setup()
{
assert(avformat_alloc_output_context2(&m_muxer, nullptr, "matroska", nullptr) == 0);
assert(m_muxer != nullptr);

setupEncoder();

m_avStream = avformat_new_stream(m_muxer, nullptr);
assert(m_avStream != nullptr);
m_avStream->id = m_muxer->nb_streams-1;
m_avStream->time_base = m_encoder->time_base;

// Some formats want stream headers to be separate.
if(m_muxer->oformat->flags & AVFMT_GLOBALHEADER)
m_encoder->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

assert(avcodec_parameters_from_context(m_avStream->codecpar, m_encoder) == 0);
assert(avio_open(&m_muxer->pb, m_hardwareAcceleration? "hardware.mkv": "software.mkv", AVIO_FLAG_WRITE) == 0);
assert(avformat_write_header(m_muxer, nullptr) == 0);
}
void Encoder::setupEncoder()
{
const char* encoderName = m_hardwareAcceleration? "h264_vaapi": "libx264";
AVCodec* videoCodec = avcodec_find_encoder_by_name(encoderName);
m_encoder = avcodec_alloc_context3(videoCodec);
m_encoder->bit_rate = s_width * s_height * s_fps * 2;
m_encoder->width = s_width;
m_encoder->height = s_height;
m_encoder->time_base = (AVRational){1, s_fps};
m_encoder->framerate = (AVRational){s_fps, 1};

m_encoder->gop_size = s_fps; // have at least 1 I-frame per second
m_encoder->max_b_frames = 1;
m_encoder->pix_fmt = AV_PIX_FMT_YUV420P;

if(m_hardwareAcceleration) {
m_encoder->pix_fmt = AV_PIX_FMT_VAAPI;
m_encoder->get_format = get_vaapi_format;

assert(av_hwdevice_ctx_create(&m_device, AV_HWDEVICE_TYPE_VAAPI, "/dev/dri/renderD128", nullptr, 0) == 0);

AVHWDeviceContext* deviceCtx = (AVHWDeviceContext*) m_device->data;
assert(deviceCtx->type == AV_HWDEVICE_TYPE_VAAPI);

m_encoder->hw_device_ctx = av_hwframe_ctx_alloc(m_device);
m_encoder->hw_frames_ctx = av_buffer_ref(m_device);
m_hwFrame = av_frame_alloc();
av_hwframe_get_buffer(m_encoder->hw_device_ctx, m_hwFrame, 0);
}

assert(avcodec_open2(m_encoder, videoCodec, nullptr) == 0); // <-- returns -22 (EINVAL) for hardware encoder

m_muxer->video_codec_id = videoCodec->id;
m_muxer->video_codec = videoCodec;
}
void Encoder::encodeFrame(AVFrame* frame)
{
assert(avcodec_send_frame(m_encoder, frame) == 0);

AVPacket packet;
av_init_packet(&packet);
int ret = 0;
while(ret >= 0) {
ret = avcodec_receive_packet(m_encoder, &packet);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return; // nothing to write
}
assert(ret >= 0);

av_packet_rescale_ts(&packet, m_encoder->time_base, m_avStream->time_base);
packet.stream_index = m_avStream->index;
av_interleaved_write_frame(m_muxer, &packet);
av_packet_unref(&packet);
}
}

main.cpp
#include "encoder.h"

AVFrame* createFrame(const int format)
{
AVFrame* frame = av_frame_alloc();
frame->format = format;
frame->width = Encoder::s_width;
frame->height = Encoder::s_height;
assert(av_frame_get_buffer(frame, 0) == 0);
assert(av_frame_make_writable(frame) == 0);

// Y
for(int y=0; y<frame->height; y++) {
for(int x=0; x<frame->width; x++) {
frame->data[0][y * frame->linesize[0] + x] = 0;
}
}

// CbCr
const int widthCbCr = frame->width / 2;
const int heightCbCr = frame->height / 2;

if(format == AV_PIX_FMT_YUV420P) {
for(int y=0; y<heightCbCr; y++) {
for(int x=0; x<widthCbCr; x++) {
frame->data[1][y * frame->linesize[1] + x] = 0; // Cb
frame->data[2][y * frame->linesize[2] + x] = 0; // Cr
}
}
return frame;
}
else if(format == AV_PIX_FMT_NV12) {
for(int y=0; y<heightCbCr; y++) {
for(int x=0; x<widthCbCr; x++) {
frame->data[1][y * frame->linesize[0] + x] = 0;
}
}
return frame;
}

return nullptr;
}

int main()
{
av_register_all();

AVFrame* yuv420pFrame = createFrame(AV_PIX_FMT_YUV420P);
AVFrame* nv12Frame = createFrame(AV_PIX_FMT_NV12);

// works well
Encoder softwareEncoder(false);
for(int i=0; i<100; ++i)
softwareEncoder.addFrame(yuv420pFrame);
softwareEncoder.flush();

// does not work
Encoder hardwareEncoder(true);
for(int i=0; i<100; ++i)
hardwareEncoder.addFrame(nv12Frame);
hardwareEncoder.flush();

return 0;
}

请注意,我故意省略了各种free()函数和析构函数,以使代码简短。

最佳答案

该摘录所在的avcodec.h文件中有一个很大的注释:

  • Mixing new and old function calls on the same AVCodecContext is not allowed,

  • and will result in undefined behavior.

  • Some codecs might require using the new API; using the old API will return

  • an error when calling it. All codecs support the new API.


这(以及周围的内容)表明您看到一个错误而不是另一个错误的可能原因。

关于c++ - 使用libavcodec和VAAPI从C++编码视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59666753/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com