c++ - 使用 H264 编码 RGB 帧数据
问题描述
所以我正在尝试使用 ffmpeg c++ 来编码 rgb h264 视频,我看到了两个选项:
使用 sws_scale 将 rgb 帧数据转换为 yuv420 并正常编码(这可行,但它似乎有某种内存泄漏并在大约 20 秒后冻结)
使用 libx264rgb 直接使用 rgb 数据进行编码(据我所知,将 rgb 转换为 yuv 并没有丢失和颜色偏移,但是当我尝试使用这种方法时,输出的视频完全是黑色的)
这是在 android 上,所以我使用的是 ffmpeg 移动套件(https://github.com/tanersener/ffmpeg-kit)完整的 gpl 构建,我认为它包括 libx264rgb 并且当我尝试打开编解码器时首先通过名称找到它成功打开,使用 264rgb 编码时是否需要做其他不同的事情?
这是我当前的代码:
#include "include/video_recorder.hpp"
void VideoCapture::Encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt, FILE *outfile, int framesToWrite = 1) {
int ret;
/* send the frame to the encoder */
// if (frame)
// {
// log("Send frame %i at time %li", frameCounter, frame->pts);
// }
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0)
{
log("Error sending a frame for encoding\n");
return;
}
while (ret >= 0)
{
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
return;
}
else if (ret < 0)
{
log("Error during encoding\n");
return;
}
// log("Write packet %li (size=%i)\n", pkt->pts, pkt->size);
for (int i = 0; i < framesToWrite; i++)
{
fwrite(pkt->data, 1, pkt->size, outfile);
}
av_packet_unref(pkt);
}
}
void VideoCapture::AddFrame(rgb24 *data) {
if(!initialized) return;
if(startTime == 0) {
startTime = UnityEngine::Time::get_time();
log("Video global time start is %f", startTime);
}
int framesToWrite = 1;
// if (stabilizeFPS) {
// framesToWrite = std::max(0, int(TotalLength() / (1.0f / float(fps * 2))) - frameCounter);
// log("Frames to write: %i, equation is int(%f / (1 / %i)) - %i", framesToWrite, TotalLength(), fps, frameCounter);
// }
if(framesToWrite == 0) return;
frameCounter += framesToWrite;
int ret;
/* make sure the frame data is writable */
ret = av_frame_make_writable(frame);
if (ret < 0)
{
log("Could not make the frame writable: %s", av_err2str(ret));
return;
}
// if (!swsCtx)
// {
// swsCtx = sws_getContext(c->width, c->height, AV_PIX_FMT_RGB24, c->width, c->height, AV_PIX_FMT_YUV420P, SWS_POINT, 0, 0, 0);
// }
// int inLinesize[1] = {3 * c->width};
// sws_scale(swsCtx, (const uint8_t *const *)&data, inLinesize, 0, c->height, frame->data, frame->linesize);
frame->pts = frameCounter;
/* encode the image */
Encode(c, frame, pkt, f, framesToWrite);
}
void VideoCapture::Finish()
{
//DELAYED FRAMES
// Encode(c, NULL, pkt, f);
fclose(f);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
initialized = false;
}
void VideoCapture::Init(int videoWidth, int videoHeight, int fpsrate, int videoBitrate, bool stabilizeFPS, std::string encodeSpeed, std::string filepath)
{
log("Setting up video at path " + filepath);
fps = fpsrate;
width = videoWidth;
height = videoHeight;
bitrate = videoBitrate * 1000;
filename = filepath.c_str();
this->stabilizeFPS = stabilizeFPS;
frameCounter = 0;
int ret;
codec = avcodec_find_encoder_by_name("libx264rgb");
if (!codec)
{
log("Codec not found");
return;
}
c = avcodec_alloc_context3(codec);
if (!c)
{
log("Could not allocate video codec context\n");
return;
}
pkt = av_packet_alloc();
if (!pkt)
return;
c->bit_rate = bitrate * 1000;
c->width = width;
c->height = height;
c->time_base = (AVRational){1, fps};
c->framerate = (AVRational){fps, 1};
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_RGB24;
if (codec->id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", encodeSpeed.c_str(), 0);
ret = avcodec_open2(c, codec, NULL);
if (ret < 0)
{
log("Could not open codec: %s\n", av_err2str(ret));
return;
}
log("Successfully opened codec");
f = fopen(filename, "wb");
if (!f)
{
log("Could not open %s\n", filename);
return;
}
frame = av_frame_alloc();
if (!frame)
{
log("Could not allocate video frame\n");
return;
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
ret = av_frame_get_buffer(frame, 0);
if (ret < 0)
{
log("Could not allocate the video frame data\n");
return;
}
initialized = true;
log("Finished initializing video at path %s", filename);
encodingThread = std::thread(&VideoCapture::encodeFrames, this);
}
void VideoCapture::encodeFrames()
{
log("Starting encoding thread");
while (initialized || !framebuffers.empty())
{
if (!framebuffers.empty())
{
std::unique_lock lock(framebuffer_mutex);
std::list<void*> listCopy(framebuffers); // copy the list
framebuffers.clear();
lock.unlock();
// Unlock and use the copy
// Now we use the copied list and it should be ours only
while (!listCopy.empty()) {
// log("size is %i", framebuffers.size());
auto it = listCopy.begin();
auto frameData = (rgb24 *) *it;
this->AddFrame(frameData);
// free(*it);
free(frameData);
listCopy.pop_front();
}
}
}
log("Ending encoding thread");
}
void VideoCapture::queueFrame(void *frame) {
std::unique_lock lock(framebuffer_mutex);
framebuffers.push_back(frame);
}
VideoCapture::~VideoCapture()
{
initialized = false; // should we force it to stop or force it to wait?
if (encodingThread.joinable())
encodingThread.join();
}```
解决方案
推荐阅读
- pysimplegui - 在 Jupyter 上运行 PySimpleGUI
- pine-script - RSI 松树脚本
- java - Amazon Linux AMI 上的“bash: jstack: command not found”错误
- python - Scipy UnivariateSpline 崩溃
- python - NoneType 实际上是什么意思?
- javascript - 如何向客户端发送响应并将响应放入客户端的变量中(快递)
- php - ESP8266 到 000webhost 没有获取数据一个 php 文件
- java - 如何从 S3 存储桶添加 jar 文件的依赖项?
- python - awscli 1.18.34 要求 botocore==1.15.34,但您将拥有不兼容的 botocore 1.15.49
- javascript - 如何从数组中删除特定元素?