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更新了啥,感觉启动都很卡,干啥都卡

相关推荐
feilieren5 分钟前
AI 视频:初识 Pika 2.0,基本使用攻略
人工智能·ai视频
开放知识图谱1 小时前
论文浅尝 | HippoRAG:神经生物学启发的大语言模型的长期记忆(Neurips2024)
人工智能·语言模型·自然语言处理
威化饼的一隅1 小时前
【多模态】swift-3框架使用
人工智能·深度学习·大模型·swift·多模态
人类群星闪耀时1 小时前
大模型技术优化负载均衡:AI驱动的智能化运维
运维·人工智能·负载均衡
编码小哥1 小时前
通过opencv加载、保存视频
人工智能·opencv
发呆小天才O.oᯅ1 小时前
YOLOv8目标检测——详细记录使用OpenCV的DNN模块进行推理部署C++实现
c++·图像处理·人工智能·opencv·yolo·目标检测·dnn
西猫雷婶2 小时前
python学opencv|读取图像(十六)修改HSV图像HSV值
开发语言·python·opencv
lovelin+v175030409662 小时前
智能电商:API接口如何驱动自动化与智能化转型
大数据·人工智能·爬虫·python
rpa_top2 小时前
RPA 助力电商:自动化商品信息上传,节省人力资源 —— 以影刀 RPA 为例【rpa.top】
大数据·前端·人工智能·自动化·rpa
视觉语言导航2 小时前
arXiv-2024 | STMR:语义拓扑度量表示引导的大模型推理无人机视觉语言导航
人工智能·具身智能