c++ 使用ffmpeg录制视频
时间: 2023-12-17 15:28:26 浏览: 231
使用FFmpeg库可以在C++中实现录制视频的功能。下面是一个简单的示例代码,可以录制桌面并将其保存为MP4格式的视频文件:
```c++
#include <iostream>
#include <string>
#include <sstream>
#include <ctime>
#include <chrono>
#include <thread>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
}
using namespace std;
int main(int argc, char* argv[])
{
// 初始化FFmpeg库
av_register_all();
avformat_network_init();
// 获取当前时间作为文件名
time_t now = time(nullptr);
tm* localtm = localtime(&now);
stringstream ss;
ss << "output_" << localtm->tm_year + 1900 << "-" << localtm->tm_mon + 1 << "-" << localtm->tm_mday << "_" << localtm->tm_hour << "-" << localtm->tm_min << "-" << localtm->tm_sec << ".mp4";
string filename = ss.str();
// 打开视频输出文件
AVFormatContext* outctx = nullptr;
int ret = avformat_alloc_output_context2(&outctx, nullptr, nullptr, filename.c_str());
if (ret < 0) {
cerr << "Could not create output context: " << av_err2str(ret) << endl;
return 1;
}
// 查找视频编码器
AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec) {
cerr << "Could not find encoder" << endl;
return 1;
}
// 创建视频流
AVStream* stream = avformat_new_stream(outctx, codec);
if (!stream) {
cerr << "Could not create stream" << endl;
return 1;
}
// 配置视频编码器参数
AVCodecContext* codecctx = avcodec_alloc_context3(codec);
if (!codecctx) {
cerr << "Could not allocate codec context" << endl;
return 1;
}
codecctx->width = 640;
codecctx->height = 480;
codecctx->time_base = { 1, 25 };
codecctx->framerate = { 25, 1 };
codecctx->pix_fmt = AV_PIX_FMT_YUV420P;
codecctx->gop_size = 10;
codecctx->max_b_frames = 1;
av_opt_set(codecctx->priv_data, "preset", "ultrafast", 0);
av_opt_set(codecctx->priv_data, "tune", "zerolatency", 0);
ret = avcodec_open2(codecctx, codec, nullptr);
if (ret < 0) {
cerr << "Could not open codec: " << av_err2str(ret) << endl;
return 1;
}
// 将视频流的编码器参数复制到输出文件的视频流中
ret = avcodec_parameters_from_context(stream->codecpar, codecctx);
if (ret < 0) {
cerr << "Could not copy codec parameters: " << av_err2str(ret) << endl;
return 1;
}
// 打开视频输出文件
ret = avio_open(&outctx->pb, filename.c_str(), AVIO_FLAG_WRITE);
if (ret < 0) {
cerr << "Could not open output file: " << av_err2str(ret) << endl;
return 1;
}
// 写入输出文件的头部信息
ret = avformat_write_header(outctx, nullptr);
if (ret < 0) {
cerr << "Could not write header: " << av_err2str(ret) << endl;
return 1;
}
// 初始化屏幕捕获
AVFormatContext* inctx = nullptr;
AVDictionary* options = nullptr;
av_dict_set(&options, "framerate", "5", 0);
av_dict_set(&options, "offset_x", "10", 0);
av_dict_set(&options, "offset_y", "20", 0);
av_dict_set(&options, "video_size", "640x480", 0);
ret = avformat_open_input(&inctx, "desktop", nullptr, &options);
if (ret < 0) {
cerr << "Could not open input: " << av_err2str(ret) << endl;
return 1;
}
ret = avformat_find_stream_info(inctx, nullptr);
if (ret < 0) {
cerr << "Could not find stream info: " << av_err2str(ret) << endl;
return 1;
}
AVCodec* incodec = avcodec_find_decoder(inctx->streams[0]->codecpar->codec_id);
if (!incodec) {
cerr << "Could not find decoder" << endl;
return 1;
}
AVCodecContext* incodecctx = avcodec_alloc_context3(incodec);
if (!incodecctx) {
cerr << "Could not allocate codec context" << endl;
return 1;
}
ret = avcodec_parameters_to_context(incodecctx, inctx->streams[0]->codecpar);
if (ret < 0) {
cerr << "Could not copy codec parameters: " << av_err2str(ret) << endl;
return 1;
}
ret = avcodec_open2(incodecctx, incodec, nullptr);
if (ret < 0) {
cerr << "Could not open codec: " << av_err2str(ret) << endl;
return 1;
}
AVFrame* inframe = av_frame_alloc();
if (!inframe) {
cerr << "Could not allocate frame" << endl;
return 1;
}
AVPacket* inpacket = av_packet_alloc();
if (!inpacket) {
cerr << "Could not allocate packet" << endl;
return 1;
}
// 初始化视频帧转换器
SwsContext* swsctx = sws_getContext(incodecctx->width, incodecctx->height, incodecctx->pix_fmt, codecctx->width, codecctx->height, codecctx->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr);
if (!swsctx) {
cerr << "Could not create sws context" << endl;
return 1;
}
AVFrame* frame = av_frame_alloc();
if (!frame) {
cerr << "Could not allocate frame" << endl;
return 1;
}
frame->format = codecctx->pix_fmt;
frame->width = codecctx->width;
frame->height = codecctx->height;
ret = av_frame_get_buffer(frame, 32);
if (ret < 0) {
cerr << "Could not allocate frame data: " << av_err2str(ret) << endl;
return 1;
}
// 录制视频
int64_t pts = 0;
while (true) {
// 从屏幕捕获中读取一帧视频
ret = av_read_frame(inctx, inpacket);
if (ret < 0) {
cerr << "Could not read frame: " << av_err2str(ret) << endl;
break;
}
if (inpacket->stream_index != 0) {
av_packet_unref(inpacket);
continue;
}
ret = avcodec_send_packet(incodecctx, inpacket);
if (ret < 0) {
cerr << "Could not send packet: " << av_err2str(ret) << endl;
break;
}
while (ret >= 0) {
ret = avcodec_receive_frame(incodecctx, inframe);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if (ret < 0) {
cerr << "Could not receive frame: " << av_err2str(ret) << endl;
break;
}
// 将视频帧转换为输出文件的格式
sws_scale(swsctx, inframe->data, inframe->linesize, 0, incodecctx->height, frame->data, frame->linesize);
// 编码视频帧并写入输出文件
frame->pts = pts++;
ret = avcodec_send_frame(codecctx, frame);
if (ret < 0) {
cerr << "Could not send frame: " << av_err2str(ret) << endl;
break;
}
while (ret >= 0) {
AVPacket pkt = { 0 };
ret = avcodec_receive_packet(codecctx, &pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if (ret < 0) {
cerr << "Could not receive packet: " << av_err2str(ret) << endl;
break;
}
av_packet_rescale_ts(&pkt, codecctx->time_base, stream->time_base);
pkt.stream_index = stream->index;
ret = av_interleaved_write_frame(outctx, &pkt);
if (ret < 0) {
cerr << "Could not write frame: " << av_err2str(ret) << endl;
break;
}
av_packet_unref(&pkt);
}
}
av_packet_unref(inpacket);
// 暂停一段时间,以控制录制视频的帧率
this_thread::sleep_for(chrono::milliseconds(200));
}
// 写入输出文件的尾部信息
ret = av_write_trailer(outctx);
if (ret < 0) {
cerr << "Could not write trailer: " << av_err2str(ret) << endl;
return 1;
}
// 释放资源
av_frame_free(&frame);
sws_freeContext(swsctx);
av_packet_free(&inpacket);
av_frame_free(&inframe);
avcodec_free_context(&incodecctx);
avformat_close_input(&inctx);
avio_close(outctx->pb);
avformat_free_context(outctx);
return 0;
}
```
该示例代码使用了FFmpeg库中的AVFormatContext、AVCodecContext、AVStream、AVFrame、AVPacket等结构体和函数,实现了从屏幕捕获视频帧、转换视频帧格式、编码视频帧、写入输出文件等功能。你可以根据自己的需求修改代码中的参数和细节。
阅读全文