广州做护肤品的网站,免备案cdn,wap手机网站代码,天津非常好的网站建设使用FFmpeg库实现从USB摄像头获取流并通过RTMP推流#xff0c;FFmpeg版本为4.4.2-0。RTMP服务器使用的是SRS#xff0c;拉流端使用VLC。
在Linux上查看摄像头信息可使用 v4l2-ctl 工具#xff0c;查看命令如下#xff1a;
v4l2-ctl --device/dev/video0 --list-formats-e…使用FFmpeg库实现从USB摄像头获取流并通过RTMP推流FFmpeg版本为4.4.2-0。RTMP服务器使用的是SRS拉流端使用VLC。
在Linux上查看摄像头信息可使用 v4l2-ctl 工具查看命令如下
v4l2-ctl --device/dev/video0 --list-formats-ext 代码如下
#include libavdevice/avdevice.h
#include libswscale/swscale.h
#include libavutil/imgutils.hint main(void)
{const char *input_format_name video4linux2; // 输入格式名称Linux下为video4linux2或v4l2const char *device_name /dev/video0; // 摄像头设备名称const char *camera_resolution 640x480; // 摄像头分辨率enum AVPixelFormat camera_pix_fmt AV_PIX_FMT_YUYV422; // 摄像头像素格式const char *url rtmp://192.168.3.230/live/livestream; // rtmp地址int frame_rate 25; // 帧率int ret -1, retVal -1;int video_streamid -1;int64_t frame_index 0;AVDictionary *options NULL;AVInputFormat *fmt NULL;AVFormatContext *in_context NULL, *out_context NULL;struct SwsContext *sws_ctx NULL;AVCodecContext *codec_context NULL;AVStream *out_stream NULL;AVCodec *codec NULL;AVPacket *packet NULL;// 注册所有设备avdevice_register_all();// 查找输入格式fmt av_find_input_format(input_format_name);if (!fmt){fprintf(stderr, av_find_input_format error);return -1;}// 设置分辨率av_dict_set(options, video_size, camera_resolution, 0);// 打开输入流并初始化格式上下文retVal avformat_open_input(in_context, device_name, fmt, options);if (retVal ! 0){// 打印错误信息fprintf(stderr, avformat_open_input error);return -1;}// 查找视频流索引video_streamid av_find_best_stream(in_context, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if (video_streamid 0){fprintf(stderr, cannot find video stream);goto end;}AVStream *video_stream in_context-streams[video_streamid];printf(input stream, width: %d, height: %d, format: %s\n,video_stream-codecpar-width, video_stream-codecpar-height,av_get_pix_fmt_name((enum AVPixelFormat)video_stream-codecpar-format));// 检查实际获取到的格式是否为设置的摄像头像素格式if (video_stream-codecpar-format ! camera_pix_fmt){fprintf(stderr, pixel format error);goto end;}// 初始化转换上下文sws_ctx sws_getContext(video_stream-codecpar-width, video_stream-codecpar-height, camera_pix_fmt,video_stream-codecpar-width, video_stream-codecpar-height, AV_PIX_FMT_YUV420P,SWS_BILINEAR, NULL, NULL, NULL);if (!sws_ctx){fprintf(stderr, sws_getContext error\n);goto end;}// 创建帧AVFrame *input_frame av_frame_alloc();AVFrame *frame_yuv420p av_frame_alloc();if (!input_frame || !frame_yuv420p){fprintf(stderr, av_frame_alloc error\n);goto end;}// 设置帧格式input_frame-format camera_pix_fmt;input_frame-width video_stream-codecpar-width;input_frame-height video_stream-codecpar-height;frame_yuv420p-format AV_PIX_FMT_YUV420P;frame_yuv420p-width video_stream-codecpar-width;frame_yuv420p-height video_stream-codecpar-height;// 分配帧内存retVal av_frame_get_buffer(frame_yuv420p, 0);if (retVal 0){fprintf(stderr, av_frame_get_buffer error\n);goto end;}// 分配输出格式上下文avformat_alloc_output_context2(out_context, NULL, flv, NULL);if (!out_context){fprintf(stderr, avformat_alloc_output_context2 failed\n);goto end;}// 查找编码器codec avcodec_find_encoder(AV_CODEC_ID_H264);if (!codec){fprintf(stderr, Codec not found\n);goto end;}printf(codec name: %s\n, codec-name);// 创建新的视频流out_stream avformat_new_stream(out_context, NULL);if (!out_stream){fprintf(stderr, avformat_new_stream failed\n);goto end;}// 分配编码器上下文codec_context avcodec_alloc_context3(codec);if (!codec_context){fprintf(stderr, avcodec_alloc_context3 failed\n);goto end;}// 设置编码器参数codec_context-codec_id AV_CODEC_ID_H264;codec_context-codec_type AVMEDIA_TYPE_VIDEO;codec_context-pix_fmt AV_PIX_FMT_YUV420P;codec_context-width frame_yuv420p-width;codec_context-height frame_yuv420p-height;codec_context-time_base (AVRational){1, frame_rate}; // 设置时间基codec_context-framerate (AVRational){frame_rate, 1}; // 设置帧率codec_context-bit_rate 750 * 1000; // 设置比特率codec_context-gop_size frame_rate; // 设置GOP大小codec_context-max_b_frames 0; // 设置最大B帧数不需要B帧时设置为0av_opt_set(codec_context-priv_data, profile, baseline, 0); // 设置h264画质级别av_opt_set(codec_context-priv_data, tune, zerolatency, 0); // 设置h264编码优化参数// 检测输出上下文的封装格式判断是否设置 AV_CODEC_FLAG_GLOBAL_HEADER// AV_CODEC_FLAG_GLOBAL_HEADER由原来编码时在每个关键帧前加入pps和sps改变为在extradate这个字节区加入pps和spsif (out_context-oformat-flags AVFMT_GLOBALHEADER){printf(set AV_CODEC_FLAG_GLOBAL_HEADER\n);codec_context-flags | AV_CODEC_FLAG_GLOBAL_HEADER;}// 打开编码器if (avcodec_open2(codec_context, codec, NULL) 0){fprintf(stderr, avcodec_open2 failed\n);goto end;}// 将编码器参数复制到流int result avcodec_parameters_from_context(out_stream-codecpar, codec_context);if (result 0){fprintf(stderr, avcodec_parameters_from_context failed\n);goto end;}// 打开urlif (!(out_context-oformat-flags AVFMT_NOFILE)){result avio_open(out_context-pb, url, AVIO_FLAG_WRITE);if (result 0){fprintf(stderr, avio_open error (errmsg %s)\n, av_err2str(result));goto end;}}// 写文件头result avformat_write_header(out_context, NULL);if (result 0){fprintf(stderr, avformat_write_header failed\n);goto end;}packet av_packet_alloc();if (!packet){fprintf(stderr, av_packet_alloc failed\n);goto end;}// 读取帧并进行转换AVPacket pkt;while (av_read_frame(in_context, pkt) 0){if (pkt.stream_index video_streamid){// 把读取的帧数据(AVPacket)拷贝到输入帧(AVFrame)中retVal av_image_fill_arrays(input_frame-data, input_frame-linesize, pkt.data, camera_pix_fmt,video_stream-codecpar-width, video_stream-codecpar-height, 1);if (retVal 0){av_packet_unref(pkt);fprintf(stderr, av_image_fill_arrays error\n);break;}// 转换为 YUV420Psws_scale(sws_ctx, (const uint8_t *const *)input_frame-data, input_frame-linesize, 0,input_frame-height, frame_yuv420p-data, frame_yuv420p-linesize);frame_yuv420p-pts frame_index;frame_index;// 发送帧到编码器result avcodec_send_frame(codec_context, frame_yuv420p);if (result 0){fprintf(stderr, avcodec_send_frame error (errmsg %s)\n, av_err2str(result));break;}// 接收编码后的数据包while (result 0){result avcodec_receive_packet(codec_context, packet);if (result AVERROR(EAGAIN) || result AVERROR_EOF){break;}else if (result 0){fprintf(stderr, avcodec_receive_packet error (errmsg %s)\n, av_err2str(result));goto end;}packet-stream_index out_stream-index;// 将时间戳从编码器时间基转换到流时间基av_packet_rescale_ts(packet, codec_context-time_base, out_stream-time_base);packet-pos -1;// 推送到RTMP服务器result av_interleaved_write_frame(out_context, packet);if (result 0){fprintf(stderr, av_interleaved_write_frame error (errmsg %d)\n, result);av_packet_unref(packet);goto end;}av_packet_unref(packet);}}av_packet_unref(pkt);}end:// 释放资源if (input_frame)av_frame_free(input_frame);if (frame_yuv420p)av_frame_free(frame_yuv420p);if (sws_ctx)sws_freeContext(sws_ctx);if (in_context)avformat_close_input(in_context);if (packet)av_packet_free(packet);if (codec_context)avcodec_free_context(codec_context);if (out_context)avformat_free_context(out_context);return ret;
}