环境:Windows10,VS2022,ffmpeg-n8.0-latest-win64-lgpl-shared-8.0
#include <iostream>
#include <string>
extern "C" {
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
using namespace std;
int main() {
const char* device_name = "video=Integrated Camera";
const char* output_filename = "output.mp4";
avdevice_register_all();
// 1. 打开摄像头输入
AVFormatContext* input_ctx = nullptr;
const AVInputFormat* input_format = av_find_input_format("dshow");
if (!input_format) {
cerr << "Could not find dshow input format\n";
return -1;
}
if (avformat_open_input(&input_ctx, device_name, input_format, nullptr) < 0) {
cerr << "Could not open camera device\n";
return -1;
}
if (avformat_find_stream_info(input_ctx, nullptr) < 0) {
cerr << "Could not find stream info\n";
avformat_close_input(&input_ctx);
return -1;
}
cout << "Input format opened, found " << input_ctx->nb_streams << " streams.\n";
// 2. 查找视频流并设置解码器
int video_stream_index = -1;
AVCodecContext* decoder_ctx = nullptr;
for (unsigned int i = 0; i < input_ctx->nb_streams; i++) {
if (input_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_stream_index = i;
break;
}
}
if (video_stream_index == -1) {
cerr << "Could not find video stream\n";
avformat_close_input(&input_ctx);
return -1;
}
AVStream* video_stream = input_ctx->streams[video_stream_index];
AVCodecParameters* codecpar = video_stream->codecpar;
// 创建解码器上下文
const AVCodec* decoder = avcodec_find_decoder(codecpar->codec_id);
if (!decoder) {
cerr << "Decoder not found\n";
avformat_close_input(&input_ctx);
return -1;
}
decoder_ctx = avcodec_alloc_context3(decoder);
if (!decoder_ctx) {
cerr << "Could not allocate decoder context\n";
avformat_close_input(&input_ctx);
return -1;
}
if (avcodec_parameters_to_context(decoder_ctx, codecpar) < 0) {
cerr << "Could not copy codec parameters to decoder context\n";
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
if (avcodec_open2(decoder_ctx, decoder, nullptr) < 0) {
cerr << "Could not open decoder\n";
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
// 3. 查找H.264编码器
const AVCodec* encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!encoder) {
cerr << "H.264 encoder not found\n";
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
AVCodecContext* encoder_ctx = avcodec_alloc_context3(encoder);
if (!encoder_ctx) {
cerr << "Could not allocate encoder context\n";
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
// 编码器设置
encoder_ctx->codec_id = AV_CODEC_ID_H264;
encoder_ctx->bit_rate = 4000000;
encoder_ctx->width = decoder_ctx->width;
encoder_ctx->height = decoder_ctx->height;
encoder_ctx->time_base = av_inv_q(av_make_q(30, 1)); // 30 FPS
encoder_ctx->framerate = av_make_q(30, 1);
encoder_ctx->gop_size = 12;
encoder_ctx->max_b_frames = 2;
encoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
// H.264特定设置
av_opt_set(encoder_ctx->priv_data, "preset", "medium", 0);
av_opt_set(encoder_ctx->priv_data, "tune", "zerolatency", 0);
if (avcodec_open2(encoder_ctx, encoder, nullptr) < 0) {
cerr << "Could not open encoder\n";
avcodec_free_context(&encoder_ctx);
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
// 4. 创建输出文件上下文
AVFormatContext* output_ctx = nullptr;
if (avformat_alloc_output_context2(&output_ctx, nullptr, nullptr, output_filename) < 0) {
cerr << "Could not create output context\n";
avcodec_free_context(&encoder_ctx);
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
// 添加视频流
AVStream* out_stream = avformat_new_stream(output_ctx, nullptr);
if (!out_stream) {
cerr << "Could not create output stream\n";
avformat_free_context(output_ctx);
avcodec_free_context(&encoder_ctx);
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
if (avcodec_parameters_from_context(out_stream->codecpar, encoder_ctx) < 0) {
cerr << "Could not copy encoder parameters to output stream\n";
avformat_free_context(output_ctx);
avcodec_free_context(&encoder_ctx);
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
out_stream->time_base = encoder_ctx->time_base;
// 打开输出文件
if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {
if (avio_open(&output_ctx->pb, output_filename, AVIO_FLAG_WRITE) < 0) {
cerr << "Could not open output file\n";
avformat_free_context(output_ctx);
avcodec_free_context(&encoder_ctx);
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
}
if (avformat_write_header(output_ctx, nullptr) < 0) {
cerr << "Error occurred when writing header\n";
if (!(output_ctx->oformat->flags & AVFMT_NOFILE))
avio_closep(&output_ctx->pb);
avformat_free_context(output_ctx);
avcodec_free_context(&encoder_ctx);
avcodec_free_context(&decoder_ctx);
avformat_close_input(&input_ctx);
return -1;
}
// 5. 创建格式转换上下文
struct SwsContext* sws_ctx = nullptr;
if (decoder_ctx->pix_fmt != AV_PIX_FMT_YUV420P) {
sws_ctx = sws_getContext(
decoder_ctx->width, decoder_ctx->height, decoder_ctx->pix_fmt,
encoder_ctx->width, encoder_ctx->height, AV_PIX_FMT_YUV420P,
SWS_BICUBIC, nullptr, nullptr, nullptr
);
if (!sws_ctx) {
cerr << "Could not initialize the conversion context\n";
// 清理资源...
return -1;
}
}
// 6. 分配帧和包
AVFrame* input_frame = av_frame_alloc();
AVFrame* output_frame = av_frame_alloc();
AVPacket* input_pkt = av_packet_alloc();
AVPacket* output_pkt = av_packet_alloc();
if (!input_frame || !output_frame || !input_pkt || !output_pkt) {
cerr << "Could not allocate frame or packet\n";
return -1;
}
// 为输出帧分配缓冲区
output_frame->format = AV_PIX_FMT_YUV420P;
output_frame->width = encoder_ctx->width;
output_frame->height = encoder_ctx->height;
if (av_frame_get_buffer(output_frame, 0) < 0) {
cerr << "Could not allocate frame buffer\n";
return -1;
}
cout << "Start recording... Press Ctrl+C to stop.\n";
// 7. 主循环:读取、解码、转换、编码、写入
int64_t frame_pts = 0;
for (int frame_count = 0; frame_count < 300; frame_count++) {
if (av_read_frame(input_ctx, input_pkt) < 0) {
cerr << "Error reading frame\n";
break;
}
if (input_pkt->stream_index == video_stream_index) {
// 解码
if (avcodec_send_packet(decoder_ctx, input_pkt) >= 0) {
while (avcodec_receive_frame(decoder_ctx, input_frame) >= 0) {
// 格式转换(如果需要)
if (sws_ctx) {
sws_scale(sws_ctx,
(const uint8_t * const*)input_frame->data,
input_frame->linesize,
0, decoder_ctx->height,
output_frame->data, output_frame->linesize);
} else {
// 如果格式相同,直接复制
av_frame_copy(output_frame, input_frame);
}
// 设置时间戳
output_frame->pts = frame_pts++;
// 编码
if (avcodec_send_frame(encoder_ctx, output_frame) >= 0) {
while (avcodec_receive_packet(encoder_ctx, output_pkt) >= 0) {
// 调整时间戳
av_packet_rescale_ts(output_pkt, encoder_ctx->time_base, out_stream->time_base);
output_pkt->stream_index = out_stream->index;
// 写入文件
if (av_interleaved_write_frame(output_ctx, output_pkt) < 0) {
cerr << "Error writing frame\n";
}
av_packet_unref(output_pkt);
}
}
}
}
}
av_packet_unref(input_pkt);
}
// 8. 刷新编码器
avcodec_send_frame(encoder_ctx, nullptr);
while (avcodec_receive_packet(encoder_ctx, output_pkt) >= 0) {
av_packet_rescale_ts(output_pkt, encoder_ctx->time_base, out_stream->time_base);
output_pkt->stream_index = out_stream->index;
av_interleaved_write_frame(output_ctx, output_pkt);
av_packet_unref(output_pkt);
}
// 9. 写文件尾
av_write_trailer(output_ctx);
// 10. 清理资源
if (sws_ctx)
sws_freeContext(sws_ctx);
av_frame_free(&input_frame);
av_frame_free(&output_frame);
av_packet_free(&input_pkt);
av_packet_free(&output_pkt);
avcodec_free_context(&decoder_ctx);
avcodec_free_context(&encoder_ctx);
avformat_close_input(&input_ctx);
if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&output_ctx->pb);
}
avformat_free_context(output_ctx);
cout << "Recording finished, saved to " << output_filename << "\n";
return 0;
}

1万+

被折叠的 条评论
为什么被折叠?



