ffmpeg c++ 多线程Mat 转 h264编码
时间: 2024-06-08 21:12:11 浏览: 343
Qt基于FFmpeg读取摄像头并进行H264编码
4星 · 用户满意度95%
你可以使用 FFmpeg 库来进行 H.264 编码。
首先,你需要用 OpenCV 来读取视频并把每一帧转换为 `cv::Mat` 格式。然后,你可以使用多线程来并行处理视频帧。
下一步是使用 FFmpeg 库来编码每个 `cv::Mat` 帧。你可以使用 `AVCodecContext` 来设置编码器参数,然后使用 `avcodec_encode_video2()` 方法将帧编码为 H.264 格式。最后,你需要使用 `AVPacket` 将编码数据写入输出文件。
以下是一个简单的代码示例,可以将 `cv::Mat` 帧编码为 H.264 格式:
```c++
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
// Video parameters
const int WIDTH = 640;
const int HEIGHT = 480;
const int FPS = 30;
// FFmpeg variables
AVFormatContext *pFormatCtx = nullptr;
AVOutputFormat *pOutputFmt = nullptr;
AVCodec *pCodec = nullptr;
AVCodecContext *pCodecCtx = nullptr;
AVStream *pStream = nullptr;
uint8_t *pFrameBuffer = nullptr;
AVFrame *pFrame = nullptr;
AVPacket pkt;
// Thread synchronization variables
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
bool quit = false;
// Thread function to encode a frame
void encodeFrame(cv::Mat frame)
{
// Convert OpenCV Mat to FFmpeg AVFrame
cv::cvtColor(frame, frame, cv::COLOR_BGR2YUV_I420);
memcpy(pFrame->data[0], frame.data, WIDTH*HEIGHT);
memcpy(pFrame->data[1], frame.data + WIDTH*HEIGHT, WIDTH*HEIGHT/4);
memcpy(pFrame->data[2], frame.data + WIDTH*HEIGHT*5/4, WIDTH*HEIGHT/4);
// Encode the frame
int ret = avcodec_send_frame(pCodecCtx, pFrame);
if (ret < 0) {
std::cerr << "Error sending frame to encoder: " << av_err2str(ret) << std::endl;
return;
}
while (ret >= 0) {
ret = avcodec_receive_packet(pCodecCtx, &pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
std::cerr << "Error receiving packet from encoder: " << av_err2str(ret) << std::endl;
return;
}
// Write the packet to the output file
av_interleaved_write_frame(pFormatCtx, &pkt);
}
}
int main()
{
// Initialize FFmpeg
av_register_all();
avformat_network_init();
// Open output file
int ret = avformat_alloc_output_context2(&pFormatCtx, nullptr, nullptr, "output.mp4");
if (ret < 0) {
std::cerr << "Error allocating output format context: " << av_err2str(ret) << std::endl;
return 1;
}
pOutputFmt = pFormatCtx->oformat;
// Find H.264 encoder
pCodec = avcodec_find_encoder_by_name("libx264");
if (!pCodec) {
std::cerr << "Error finding H.264 encoder" << std::endl;
return 1;
}
// Create codec context
pCodecCtx = avcodec_alloc_context3(pCodec);
if (!pCodecCtx) {
std::cerr << "Error allocating codec context" << std::endl;
return 1;
}
pCodecCtx->width = WIDTH;
pCodecCtx->height = HEIGHT;
pCodecCtx->time_base = {1, FPS};
pCodecCtx->framerate = {FPS, 1};
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
pCodecCtx->gop_size = 10;
pCodecCtx->bit_rate = 1000000;
// Open codec
ret = avcodec_open2(pCodecCtx, pCodec, nullptr);
if (ret < 0) {
std::cerr << "Error opening codec: " << av_err2str(ret) << std::endl;
return 1;
}
// Add video stream to output file
pStream = avformat_new_stream(pFormatCtx, pCodec);
if (!pStream) {
std::cerr << "Error creating new stream" << std::endl;
return 1;
}
ret = avcodec_parameters_from_context(pStream->codecpar, pCodecCtx);
if (ret < 0) {
std::cerr << "Error setting codec parameters: " << av_err2str(ret) << std::endl;
return 1;
}
av_dump_format(pFormatCtx, 0, "output.mp4", 1);
// Open output file
ret = avio_open(&pFormatCtx->pb, "output.mp4", AVIO_FLAG_WRITE);
if (ret < 0) {
std::cerr << "Error opening output file: " << av_err2str(ret) << std::endl;
return 1;
}
// Write header to output file
ret = avformat_write_header(pFormatCtx, nullptr);
if (ret < 0) {
std::cerr << "Error writing header to output file: " << av_err2str(ret) << std::endl;
return 1;
}
// Allocate frame buffer and AVFrame
pFrameBuffer = (uint8_t*) av_malloc(av_image_get_buffer_size(pCodecCtx->pix_fmt, WIDTH, HEIGHT, 32));
av_image_fill_arrays(pFrame->data, pFrame->linesize, pFrameBuffer, pCodecCtx->pix_fmt, WIDTH, HEIGHT, 32);
pFrame->width = WIDTH;
pFrame->height = HEIGHT;
pFrame->format = pCodecCtx->pix_fmt;
// Start encoding thread
std::thread encoder([&]() {
while (!quit) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready || quit; });
if (ready) {
// Encode the frame
encodeFrame(frame);
// Notify main thread that encoding is done
ready = false;
cv.notify_one();
}
}
});
// Start reading video frames
cv::VideoCapture cap("input.mp4");
if (!cap.isOpened()) {
std::cerr << "Error opening input file" << std::endl;
return 1;
}
cv::Mat frame;
while (cap.read(frame)) {
// Wait for encoding thread to finish previous frame
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !ready || quit; });
if (!quit) {
// Start encoding new frame
ready = true;
cv.notify_one();
}
}
// Wait for encoding thread to finish last frame
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !ready; });
// Clean up FFmpeg
av_write_trailer(pFormatCtx);
avcodec_free_context(&pCodecCtx);
av_frame_free(&pFrame);
av_free(pFrameBuffer);
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
quit = true;
cv.notify_one();
encoder.join();
return 0;
}
```
请注意,此代码示例省略了许多错误检查和清理代码,并且可能需要进行适当修改以适应你的需求。
阅读全文