创建可用的H.264视频文件
Creating a usable H.264 video file
我正在尝试使用libavcodec
从单个帧中生成MP4视频文件。每个输入帧都是QT QImage
,输出文件写入使用QT QFile
类。
我已经通过VideoTarget
类完成了此操作,该类别初始化时打开给定的"目标"文件,在调用addFrame(image)
时记录帧,然后在调用destructor时保存/关闭该文件。
类有以下字段:
AVCodec* m_codec = nullptr;
AVCodecContext *m_context = nullptr;
AVPacket* m_packet = nullptr;
AVFrame* m_frame = nullptr;
QFile m_target;
看起来像这样:
VideoTarget::VideoTarget(QString target, QObject *parent) : QObject(parent), m_target(target)
{
// Find video codec
m_codec = avcodec_find_encoder_by_name("libx264rgb");
if (!m_codec) throw std::runtime_error("Unable to find codec.");
// Make codec context
m_context = avcodec_alloc_context3(m_codec);
if (!m_context) throw std::runtime_error("Unable to allocate codec context.");
// Make codec packet
m_packet = av_packet_alloc();
if (!m_packet) throw std::runtime_error("Unable to allocate packet.");
// Configure context
m_context->bit_rate = 400000;
m_context->width = 1280;
m_context->height = 720;
m_context->time_base = (AVRational){1, 60};
m_context->framerate = (AVRational){60, 1};
m_context->gop_size = 10;
m_context->max_b_frames = 1;
m_context->pix_fmt = AV_PIX_FMT_RGB24;
if (m_codec->id == AV_CODEC_ID_H264)
av_opt_set(m_context->priv_data, "preset", "slow", 0);
// Open Codec
int ret = avcodec_open2(m_context, m_codec, nullptr);
if (ret < 0) {
throw std::runtime_error("Unable to open codec.");
}
// Open file
if (!m_target.open(QIODevice::WriteOnly))
throw std::runtime_error("Unable to open target file.");
// Allocate frame
m_frame = av_frame_alloc();
if (!m_frame) throw std::runtime_error("Unable to allocate frame.");
m_frame->format = m_context->pix_fmt;
m_frame->width = m_context->width;
m_frame->height = m_context->height;
m_frame->pts = 0;
ret = av_frame_get_buffer(m_frame, 24);
if (ret < 0) throw std::runtime_error("Unable to allocate frame buffer.");
}
void VideoTarget::addFrame(QImage &image)
{
// Ensure frame data is writable
int ret = av_frame_make_writable(m_frame);
if (ret < 0) throw std::runtime_error("Unable to make frame writable.");
// Prepare image
for (int y = 0; y < m_context->height; y++) {
for (int x = 0; x < m_context->width; x++) {
auto pixel = image.pixelColor(x, y);
int pos = (y * 1024 + x) * 3;
m_frame->data[0][pos] = pixel.red();
m_frame->data[0][pos + 1] = pixel.green();
m_frame->data[0][pos + 2] = pixel.blue();
}
}
m_frame->pts++;
// Send the frame
ret = avcodec_send_frame(m_context, m_frame);
if (ret < 0) throw std::runtime_error("Unable to send AV frame.");
while (ret >= 0) {
ret = avcodec_receive_packet(m_context, m_packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) throw std::runtime_error("Error during encoding.");
m_target.write((const char*)m_packet->data, m_packet->size);
av_packet_unref(m_packet);
}
}
VideoTarget::~VideoTarget()
{
int ret = avcodec_send_frame(m_context, nullptr);
if (ret < 0) throw std::runtime_error("Unable to send AV null frame.");
while (ret >= 0) {
ret = avcodec_receive_packet(m_context, m_packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) throw std::runtime_error("Error during encoding.");
m_target.write((const char*)m_packet->data, m_packet->size);
av_packet_unref(m_packet);
}
// Magic number at the end of the file
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
m_target.write((const char*)endcode, sizeof(endcode));
m_target.close();
// Free codec stuff
avcodec_free_context(&m_context);
av_frame_free(&m_frame);
av_packet_free(&m_packet);
}
使用时,类似乎可以正常工作,并且数据写入文件,除了我无法在任何应用程序中播放结果文件。
我的主要嫌疑人是这些行:
// Prepare image
for (int y = 0; y < m_context->height; y++) {
for (int x = 0; x < m_context->width; x++) {
auto pixel = image.pixelColor(x, y);
int pos = (y * 1024 + x) * 3;
m_frame->data[0][pos] = pixel.red();
m_frame->data[0][pos + 1] = pixel.green();
m_frame->data[0][pos + 2] = pixel.blue();
}
}
libavcodec
文档在图像数据的布局上非常含糊,因此我有效地猜测并对没有崩溃的第一件事感到满意,因此很可能我写了这篇文章错误。我选择的pixel
颜色数据调用(给出int
值(和我选择的24位-PIXEL RGB格式之间的尺寸不匹配也是不匹配的问题。
如何调整此代码以输出实际,运行的视频文件?
libavcodec文档在布局上非常模糊 图像数据
这是因为每个编解码器都是不同的。我建议您使用YUV420P,而不是RGB24。许多玩家无法玩H264 RGB。您可以使用libswscale进行转换。
接下来,您要生产的流是什么格式?可以直接播放附件B,但是如果您使用的是fettradata nalu大小(AVCC(,则需要将流包装在容器中。
最后,为什么要使用libavcodec?我认为LiBX264提供了更清洁的API。除非您稍后播放切换编解码器,否则请避免抽象。
相关文章:
- 如何在 c++ 中通过 http 发送大型视频文件?
- 媒体基础将音频流添加到视频文件
- 帧提取 使用OpenCV的视频文件开头只有几秒钟
- 使用 OpenCV C++ 每 1 分钟创建新的视频文件
- 如何检查给定文件是否是C++中的有效视频文件?
- 媒体基础获得视频文件的精确帧(样本)计数
- 如何在不重写整个视频文件的情况下仅替换流中的一个数据框架?(ffmpeg)
- 创建可用的H.264视频文件
- 如何避免在 c++ 中使用 libssh scp 命令压缩从远程 ssh 下载的图像、音频和视频文件?
- 如何使用Windows Media Foundation直接将视频文件直接解码为Direct3D11纹理
- ARtoolkit加载从4k相机索尼捕获的视频文件
- 读取视频文件"Open CV WARNING: Couldn't read movie file bird.avi"时出现问题
- 将视频文件的帧总数保存到.txt文件中
- 从OpenCV C 中的视频文件中检索每三个帧
- 使用来自视频文件或网络摄像头的 Kinect 面部跟踪
- 来自视频文件的QVideo帧列表
- 如何在OpenCV 2.4.3中编写视频文件
- 如何在不使用QMediaPlayer的情况下获取Qt中音频和视频文件的持续时间
- 无法使用 QFileSystemModel 解析不同音频/视频文件的系统目录
- 如何在QT中播放视频文件