ffmpeg + opencv 把摄像头画面保存为mp4文件(Ubuntu24.04)

参考链接

ffmpeg + opencv 把摄像头画面保存为mp4文件_ffmpeg转化摄像头mp4-CSDN博客

调试环境

Ubuntu24.04

ffmpeg 6.1.1

opencv 4.6

g++ 13.2.0

C++源码

cpp 复制代码
#include <iostream>
#include <sys/time.h>
#include <string>

#ifdef __cplusplus
extern "C"
{
#endif

#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>

#ifdef __cplusplus
} // endof extern "C"
#endif

#include <opencv2/opencv.hpp>
using namespace cv;

AVFrame *videoFrame = nullptr;
AVCodecContext *cctx = nullptr;
SwsContext *swsCtx = nullptr;
int frameCounter = 0;
AVFormatContext *ofctx = nullptr;
int fps = 30;
int width = 640;
int height = 480;
int bitrate = 2000;
long start_time = 0;
const std::string output_filename = "out.mp4";
// const std::string output_filename = "out.ts";

long getCurrentTime()
{
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

static void pushFrame(uint8_t *data, long currentTime)
{
	int err;
	AVPacket pkt = {0};

	int inLinesize[1] = {3 * cctx->width};
	// From RGB to YUV
	sws_scale(swsCtx, (const uint8_t *const *)&data, inLinesize, 0, cctx->height, videoFrame->data, videoFrame->linesize);
	videoFrame->pts = ((currentTime - start_time) / 1000.0) * 90000;
	std::cout << videoFrame->pts << " " << cctx->time_base.num << " " << cctx->time_base.den << " " << frameCounter << std::endl;
	if ((err = avcodec_send_frame(cctx, videoFrame)) < 0)
	{
		std::cout << "Failed to send frame" << err << std::endl;
		return;
	}

	pkt.buf = NULL;
	pkt.side_data = NULL;
	pkt.data = NULL;
	pkt.size = 0;
	pkt.flags |= AV_PKT_FLAG_KEY;
	if (avcodec_receive_packet(cctx, &pkt) == 0)
	{
		static int counter = 0;
		if (counter == 0)
		{
			FILE *fp = fopen("dump_first_frame1.dat", "wb");
			fwrite(pkt.data, pkt.size, 1, fp);
			fclose(fp);
		}
		std::cout << "pkt key: " << (pkt.flags & AV_PKT_FLAG_KEY) << " " << pkt.size << " " << (counter++) << std::endl;
		uint8_t *size = ((uint8_t *)pkt.data);
		std::cout << "first: " << (int)size[0] << " " << (int)size[1] << " " << (int)size[2] << " " << (int)size[3] << " " << (int)size[4] << " " << (int)size[5] << " " << (int)size[6] << " " << (int)size[7] << std::endl;
		av_interleaved_write_frame(ofctx, &pkt);
		av_packet_unref(&pkt);
	}
}

static void finish()
{
	// DELAYED FRAMES
	AVPacket pkt = {0};
	pkt.data = NULL;
	pkt.size = 0;

	while (true)
	{
		avcodec_send_frame(cctx, NULL);
		if (avcodec_receive_packet(cctx, &pkt) == 0)
		{
			av_interleaved_write_frame(ofctx, &pkt);
			av_packet_unref(&pkt);
		}
		else
		{
			break;
		}
	}

	av_write_trailer(ofctx);
	avformat_close_input(&ofctx);
}

static void free()
{
	if (videoFrame)
	{
		av_frame_free(&videoFrame);
	}
	if (cctx)
	{
		avcodec_free_context(&cctx);
	}
	if (ofctx)
	{
		avformat_free_context(ofctx);
	}
	if (swsCtx)
	{
		sws_freeContext(swsCtx);
	}
}

int main()
{
	// VideoCapture capture("road_test.mp4");
	VideoCapture capture(0);
	Mat frame;
	capture >> frame;

	width = frame.cols;
	height = frame.rows;

	avdevice_register_all();

	int err = avformat_alloc_output_context2(&ofctx, nullptr, nullptr, output_filename.c_str());
	if (err)
	{
		std::cout << "can't create output context" << std::endl;
		return -1;
	}

	const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
	if (!codec)
	{
		std::cout << "can't create codec" << std::endl;
		return -1;
	}

	AVStream *stream = avformat_new_stream(ofctx, codec);
	if (!stream)
	{
		std::cout << "can't find format" << std::endl;
		return -1;
	}

	cctx = avcodec_alloc_context3(codec);

	if (!cctx)
	{
		std::cout << "can't create codec context" << std::endl;
		return -1;
	}

	stream->codecpar->codec_id = AV_CODEC_ID_H264;
	stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
	stream->codecpar->width = width;
	stream->codecpar->height = height;
	stream->codecpar->format = AV_PIX_FMT_YUV420P;
	stream->codecpar->bit_rate = bitrate * 1000;
	avcodec_parameters_to_context(cctx, stream->codecpar);
	cctx->time_base = (AVRational){1, 1};
	cctx->max_b_frames = 2;
	cctx->gop_size = 12;
	cctx->framerate = (AVRational){fps, 1};

	if (stream->codecpar->codec_id == AV_CODEC_ID_H265)
	{
		av_opt_set(cctx, "preset", "ultrafast", 0);
	}

	avcodec_parameters_from_context(stream->codecpar, cctx);

	if ((err = avcodec_open2(cctx, codec, NULL)) < 0)
	{
		std::cout << "Failed to open codec" << err << std::endl;
		return -1;
	}

	if ((err = avio_open(&ofctx->pb, output_filename.c_str(), AVIO_FLAG_WRITE)) < 0)
	{
		std::cout << "Failed to open file" << err << std::endl;
		return -1;
	}

	if ((err = avformat_write_header(ofctx, NULL)) < 0)
	{
		std::cout << "Failed to write header" << err << std::endl;
		return -1;
	}

	av_dump_format(ofctx, 0, output_filename.c_str(), 1);

	videoFrame = av_frame_alloc();
	videoFrame->format = AV_PIX_FMT_YUV420P;
	videoFrame->width = cctx->width;
	videoFrame->height = cctx->height;
	if ((err = av_frame_get_buffer(videoFrame, 32)) < 0)
	{
		std::cout << "Failed to allocate picture" << err << std::endl;
		return -1;
	}

	swsCtx = sws_getContext(cctx->width, cctx->height, AV_PIX_FMT_BGR24, cctx->width, cctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, 0, 0, 0);

	start_time = getCurrentTime();
	for (int i = 0; i < 100; ++i)
	{
		capture >> frame;
		pushFrame(frame.data, getCurrentTime());
	}

	finish();
	free();
	return 0;
}

CMakeLists.txt

cpp 复制代码
cmake_minimum_required(VERSION 3.16)
 
project(recordStudy)
 
find_package(OpenCV REQUIRED)
 
include_directories(. ${OpenCV_INCLUDE_DIRS})
 
link_libraries(avformat)
link_libraries(avcodec)
link_libraries(avutil)
link_libraries(swscale)
link_libraries(avdevice)
 
add_executable(recordStudy main.cpp)
target_link_libraries(recordStudy ${OpenCV_LIBS})

牢骚

不知道Ubuntu24.04更新了啥,感觉启动都很卡,干啥都卡

相关推荐
uncle_ll7 分钟前
PyTorch图像预处理:计算均值和方差以实现标准化
图像处理·人工智能·pytorch·均值算法·标准化
宋138102797207 分钟前
Manus Xsens Metagloves虚拟现实手套
人工智能·机器人·vr·动作捕捉
SEVEN-YEARS11 分钟前
深入理解TensorFlow中的形状处理函数
人工智能·python·tensorflow
世优科技虚拟人15 分钟前
AI、VR与空间计算:教育和文旅领域的数字转型力量
人工智能·vr·空间计算
cloud studio AI应用21 分钟前
腾讯云 AI 代码助手:产品研发过程的思考和方法论
人工智能·云计算·腾讯云
禁默32 分钟前
第六届机器人、智能控制与人工智能国际学术会议(RICAI 2024)
人工智能·机器人·智能控制
Robot25140 分钟前
浅谈,华为切入具身智能赛道
人工智能
只怕自己不够好1 小时前
OpenCV 图像运算全解析:加法、位运算(与、异或)在图像处理中的奇妙应用
图像处理·人工智能·opencv
果冻人工智能2 小时前
2025 年将颠覆商业的 8 大 AI 应用场景
人工智能·ai员工
代码不行的搬运工2 小时前
神经网络12-Time-Series Transformer (TST)模型
人工智能·神经网络·transformer