C++使用FFmpeg读取UVC摄像头推流,流媒体服务器用Go实现,摄像头是USB免驱摄像头
关于Go流媒体服务器的实现请看:FFmpe读取UVC摄像头推流
FFmpeg版本:n7.1
注意链接库的顺序:
avformat avcodec avdevice avfilter avutil swresample swscale
C++
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include "libavdevice/avdevice.h"
}
int main() {
// 初始化FFmpeg库
avdevice_register_all();
avformat_network_init();
// 打开视频设备
AVFormatContext *input_ctx = nullptr;
AVDictionary *options = nullptr;
av_dict_set(&options, "input_format", "mjpeg", 0);
if (avformat_open_input(&input_ctx, "/dev/video0", nullptr, &options) < 0) {
fprintf(stderr, "无法打开视频输入设备。\n");
return -1;
}
// 查找视频流
if (avformat_find_stream_info(input_ctx, nullptr) < 0) {
fprintf(stderr, "无法获取视频流信息。\n");
return -1;
}
// 创建一个用于接收解码器的指针变量
const AVCodec *decoder = nullptr;
// 使用二级指针传递 decoder 变量的地址
int video_stream_index = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
if (video_stream_index < 0) {
fprintf(stderr, "无法找到视频流。\n");
return -1;
}
// 打开解码器
AVCodecContext *decoder_ctx = avcodec_alloc_context3(decoder);
avcodec_parameters_to_context(decoder_ctx, input_ctx->streams[video_stream_index]->codecpar);
if (avcodec_open2(decoder_ctx, decoder, nullptr) < 0) {
fprintf(stderr, "无法打开解码器。\n");
return -1;
}
// 创建输出格式上下文
AVFormatContext *output_ctx = nullptr;
avformat_alloc_output_context2(&output_ctx, nullptr, "rtsp", "rtsp://192.168.1.31:8554/live.sdp");
if (!output_ctx) {
fprintf(stderr, "无法创建输出上下文。\n");
return -1;
}
// 设置编码器
const AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
AVCodecContext *encoder_ctx = avcodec_alloc_context3(encoder);
encoder_ctx->bit_rate = 400000;
encoder_ctx->width = decoder_ctx->width;
encoder_ctx->height = decoder_ctx->height;
encoder_ctx->time_base = {1, 25};
encoder_ctx->framerate = {25, 1};
encoder_ctx->gop_size = 10;
encoder_ctx->max_b_frames = 1;
encoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set(encoder_ctx->priv_data, "preset", "ultrafast", 0);
av_opt_set(encoder_ctx->priv_data, "tune", "zerolatency", 0);
if (avcodec_open2(encoder_ctx, encoder, nullptr) < 0) {
fprintf(stderr, "无法打开编码器。\n");
return -1;
}
// 创建并添加输出流
AVStream *out_stream = avformat_new_stream(output_ctx, nullptr);
avcodec_parameters_from_context(out_stream->codecpar, encoder_ctx);
// 打开输出URL
if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {
if (avio_open(&output_ctx->pb, "rtsp://192.168.129.191:8554/live.sdp", AVIO_FLAG_WRITE) < 0) {
fprintf(stderr, "无法打开输出文件。\n");
return -1;
}
}
// 写文件头
avformat_write_header(output_ctx, nullptr);
// 初始化帧数据和转换器
AVFrame *frame = av_frame_alloc();
AVPacket *packet = av_packet_alloc();
AVFrame *yuv_frame = av_frame_alloc();
yuv_frame->format = encoder_ctx->pix_fmt;
yuv_frame->width = encoder_ctx->width;
yuv_frame->height = encoder_ctx->height;
av_frame_get_buffer(yuv_frame, 0);
struct SwsContext *sws_ctx =
sws_getContext(decoder_ctx->width, decoder_ctx->height, decoder_ctx->pix_fmt, encoder_ctx->width,
encoder_ctx->height, encoder_ctx->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr);
// 循环读取并编码帧
while (av_read_frame(input_ctx, packet) >= 0) {
if (packet->stream_index == video_stream_index) {
if (avcodec_send_packet(decoder_ctx, packet) == 0) {
while (avcodec_receive_frame(decoder_ctx, frame) == 0) {
// 转换帧格式
sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, yuv_frame->data,
yuv_frame->linesize);
// 发送帧到编码器
if (avcodec_send_frame(encoder_ctx, yuv_frame) == 0) {
AVPacket encoded_pkt;
av_init_packet(&encoded_pkt);
encoded_pkt.data = nullptr;
encoded_pkt.size = 0;
while (avcodec_receive_packet(encoder_ctx, &encoded_pkt) == 0) {
// 推送编码后的数据
encoded_pkt.stream_index = out_stream->index;
av_interleaved_write_frame(output_ctx, &encoded_pkt);
av_packet_unref(&encoded_pkt);
}
}
}
}
}
av_packet_unref(packet);
}
// 写文件尾并清理资源
av_write_trailer(output_ctx);
av_frame_free(&frame);
av_frame_free(&yuv_frame);
av_packet_free(&packet);
avcodec_free_context(&decoder_ctx);
avcodec_free_context(&encoder_ctx);
avformat_close_input(&input_ctx);
avio_closep(&output_ctx->pb);
avformat_free_context(output_ctx);
sws_freeContext(sws_ctx);
avformat_network_deinit();
return 0;
}
本文作者:phae
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!