当前位置: 代码网 > 服务器>服务器>Linux > C++ ffmpeg RTMP 视频推流和摄像头推流实现, arm linux平台

C++ ffmpeg RTMP 视频推流和摄像头推流实现, arm linux平台

2024年08月06日 Linux 我要评论
FFmpeg版本:n4.2.2。

环境:
ffmpeg版本:n4.2.2

下载地址(下载编译后请确认版本正确):

https://ffmpeg.org//download.html
下面地址经过第三方git加速可能存在实效性:
https://hub.fgit.cf/ffmpeg/ffmpeg/tree/n4.4.2

实现代码:


#include <stdio.h>
 
#define __stdc_constant_macros
 
#ifdef _win32
//windows
extern "c"
{
#include "libavformat/avformat.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
};
#else
//linux...
#ifdef __cplusplus
extern "c"
{
#endif
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/time.h>
#ifdef __cplusplus
};
#endif
#endif
 
#define codec_flag_global_header  0x00400000

int main(int argc, char* argv[])
{
	avoutputformat *ofmt = null;
	//输入对应一个avformatcontext,输出对应一个avformatcontext
	//(input avformatcontext and output avformatcontext)
	avformatcontext *ifmt_ctx = null, *ofmt_ctx = null;
	avpacket pkt;
	const char *in_filename, *out_filename;
	int ret, i;
	int videoindex=-1;
	int frame_index=0;
	int64_t start_time=0;

	in_filename  = "test.mp4";//输入url(input file url)
	out_filename = "rtmp://192.168.110.79:1935/live/1";//输出 url(output url)[rtmp]

	av_register_all();
	//network
	avformat_network_init();
	//输入(input)
	if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
		printf( "could not open input file.");
		goto end;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
		printf( "failed to retrieve input stream information");
		goto end;
	}
 
	for(i=0; i<ifmt_ctx->nb_streams; i++) 
		if(ifmt_ctx->streams[i]->codec->codec_type==avmedia_type_video){
			videoindex=i;
			break;
		}
 
	av_dump_format(ifmt_ctx, 0, in_filename, 0);
 
	//输出(output)
	
	avformat_alloc_output_context2(&ofmt_ctx, null, "flv", out_filename); //rtmp
	//avformat_alloc_output_context2(&ofmt_ctx, null, "mpegts", out_filename);//udp
 
	if (!ofmt_ctx) {
		printf( "could not create output context\n");
		ret = averror_unknown;
		goto end;
	}
	ofmt = ofmt_ctx->oformat;
	for (i = 0; i < ifmt_ctx->nb_streams; i++) {
		//根据输入流创建输出流(create output avstream according to input avstream)
		avstream *in_stream = ifmt_ctx->streams[i];
		avstream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
		if (!out_stream) {
			printf( "failed allocating output stream\n");
			ret = averror_unknown;
			goto end;
		}
		//复制avcodeccontext的设置(copy the settings of avcodeccontext)
		ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
		if (ret < 0) {
			printf( "failed to copy context from input to output stream codec context\n");
			goto end;
		}
		out_stream->codec->codec_tag = 0;
		if (ofmt_ctx->oformat->flags & avfmt_globalheader)
			out_stream->codec->flags |= codec_flag_global_header;
	}
	//dump format------------------
	av_dump_format(ofmt_ctx, 0, out_filename, 1);
	//打开输出url(open output url)
	if (!(ofmt->flags & avfmt_nofile)) {
		ret = avio_open(&ofmt_ctx->pb, out_filename, avio_flag_write);
		if (ret < 0) {
			printf( "could not open output url '%s'", out_filename);
			goto end;
		}
	}
	//写文件头(write file header)
	ret = avformat_write_header(ofmt_ctx, null);
	if (ret < 0) {
		printf( "error occurred when opening output url\n");
		goto end;
	}
 
	start_time=av_gettime();
	while (1) {
		avstream *in_stream, *out_stream;
		//获取一个avpacket(get an avpacket)
		ret = av_read_frame(ifmt_ctx, &pkt);
		if (ret < 0)
			break;
		//fix:no pts (example: raw h.264)
		//simple write pts
		if(pkt.pts==av_nopts_value){
			//write pts
			avrational time_base1=ifmt_ctx->streams[videoindex]->time_base;
			//duration between 2 frames (us)
			int64_t calc_duration=(double)av_time_base/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
			//parameters
			pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*av_time_base);
			pkt.dts=pkt.pts;
			pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*av_time_base);
		}
		//important:delay
		if(pkt.stream_index==videoindex){
			avrational time_base=ifmt_ctx->streams[videoindex]->time_base;
			avrational time_base_q={1,av_time_base};
			int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
			int64_t now_time = av_gettime() - start_time;
			if (pts_time > now_time)
				av_usleep(pts_time - now_time);
 
		}
 
		in_stream  = ifmt_ctx->streams[pkt.stream_index];
		out_stream = ofmt_ctx->streams[pkt.stream_index];
		/* copy packet */
		//转换pts/dts(convert pts/dts)
		pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (avrounding)(av_round_near_inf|av_round_pass_minmax));
		pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (avrounding)(av_round_near_inf|av_round_pass_minmax));
		pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
		pkt.pos = -1;
		//print to screen
		if(pkt.stream_index==videoindex){
			printf("send %8d video frames to output url\n",frame_index);
			frame_index++;
		}
		//ret = av_write_frame(ofmt_ctx, &pkt);
		ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
 
		if (ret < 0) {
			printf( "error muxing packet\n");
			break;
		}
		
		av_free_packet(&pkt);
		
	}
	//写文件尾(write file trailer)
	av_write_trailer(ofmt_ctx);
end:
	avformat_close_input(&ifmt_ctx);
	/* close output */
	if (ofmt_ctx && !(ofmt->flags & avfmt_nofile))
		avio_close(ofmt_ctx->pb);
	avformat_free_context(ofmt_ctx);
	if (ret < 0 && ret != averror_eof) {
		printf( "error occurred.\n");
		return -1;
	}
	return 0;
}
 

cmake:

cmake_minimum_required(version 3.10.2)
project(w_test)


set(cmake_c_compiler /mnt/sda2/rk3588_sdk/other/rk3566_gcc_tool/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc)
set(cmake_cxx_compiler /mnt/sda2/rk3588_sdk/other/rk3566_gcc_tool/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++)
set(cmake_strip /mnt/sda2/rk3588_sdk/other/rk3566_gcc_tool/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-strip)


set(ffmpeg_libs_dir /home/rog/my_file/other_lib/myffmpeg/4.2.2/ffmpeg/my_lib_arm/lib)
set(ffmpeg_headers_dir /home/rog/my_file/other_lib/myffmpeg/4.2.2/ffmpeg/my_lib_arm/include)
 
add_library( avcodec shared imported)
add_library( avfilter shared imported )
add_library( swresample shared imported )
add_library( swscale shared imported )
add_library( avformat shared imported )
add_library( avutil shared imported )
 
set_target_properties( avcodec properties imported_location ${ffmpeg_libs_dir}/libavcodec.so )
set_target_properties( avfilter properties imported_location ${ffmpeg_libs_dir}/libavfilter.so )
set_target_properties( swresample properties imported_location ${ffmpeg_libs_dir}/libswresample.so )
set_target_properties( swscale properties imported_location ${ffmpeg_libs_dir}/libswscale.so )
set_target_properties( avformat properties imported_location ${ffmpeg_libs_dir}/libavformat.so )
set_target_properties( avutil properties imported_location ${ffmpeg_libs_dir}/libavutil.so )
 
include_directories( ${ffmpeg_headers_dir} )
link_directories(${ffmpeg_libs_dir} )
 
set(cmake_cxx_standard 14)
add_executable(w_test main.cpp)
target_link_libraries(${project_name} ${opencv_libs} avcodec avformat avutil swresample swscale swscale avfilter )


效果:
在这里插入图片描述
摄像头推流代码:

#include <iostream>
#include <csignal>
#include <opencv2/opencv.hpp>

bool is_running = true;

void onsignal(int)
{
    is_running = false;
}

int main()
{
    // 触发下面的信号就退出
    signal(sigint, onsignal);
    signal(sigquit, onsignal);
    signal(sigterm, onsignal);

    // 打开摄像头
    cv::videocapture capture("/dev/video21");
    if (!capture.isopened())
    {
        std::cerr << "failed to open camera." << std::endl;
        return exit_failure;
    }
    capture.set(cv::cap_prop_frame_width, 1280);
    capture.set(cv::cap_prop_frame_height, 720);

    std::string rtmp_server_url = "rtmp://192.168.110.79:1935/live/1";

    std::stringstream command;
    command << "ffmpeg ";

    // infile options
    command << "-y "               // 覆盖输出文件
            << "-an "              // 禁用音频
            << "-f rawvideo "      // 强制将格式转换为rawvideo
            << "-vcodec rawvideo " // 强制视频rawvideo(“复制”到复制流)
            << "-pix_fmt bgr24 "   // 将像素格式设置为bgr24
            << "-s 1280x720 "      // 设置帧大小(wxh或缩写)
            << "-r 10 ";           // 设置帧速率(hz值、分数或缩写)

    command
        << "-i - "; //

    // outfile options
    command                    //<< "-c:v libx264 "      // 超高速音频和视频编码器    按照需要选择是否打开注释,打开延时比较大
        << "-pix_fmt yuv420p " // 将像素格式设置为yuv420p
        //<< "-preset ultrafast " // 将libx264编码预设设置为超快  按照需要选择是否打开注释,打开延时比较大
        << "-f flv " // 将格式强制为flv
        << rtmp_server_url;

    std::cout << "------------command:" << command.str() << std::endl;

    cv::mat frame;

    // 在子进程中调用 ffmpeg 进行推流
    file *fp = nullptr;
    fp = popen(command.str().c_str(), "w");

    // 将 cv 读到的每一帧传入子进程
    if (fp != nullptr)
    {
        while (is_running)
        {
            capture >> frame;
            if (frame.empty())
            {
                continue;
            }

            fwrite(frame.data, sizeof(char), frame.total() * frame.elemsize(), fp);
        }
        pclose(fp);
        return exit_success;
    }
    else
    {
        return exit_failure;
    }
}

对应的cmake

cmake_minimum_required(version 3.10.2)
project(w_test)


set(cmake_install_rpath /userdata/obj/lib)
set(cmake_c_compiler /mnt/sda2/rk3588_sdk/other/rk3566_gcc_tool/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc)
set(cmake_cxx_compiler /mnt/sda2/rk3588_sdk/other/rk3566_gcc_tool/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++)
set(cmake_strip /mnt/sda2/rk3588_sdk/other/rk3566_gcc_tool/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-strip)
set(cmake_prefix_path /home/rog/my_file/other_lib/my_opencv/4.8.0_arm/opencv/my_lib_rk3588/lib/cmake/opencv4)
find_package( opencv 4.8.0 required )
include_directories(${opencv_include_dirs})

set(ffmpeg_libs_dir /home/rog/my_file/other_lib/myffmpeg/4.2.2/ffmpeg/my_lib_arm/lib)
set(ffmpeg_headers_dir /home/rog/my_file/other_lib/myffmpeg/4.2.2/ffmpeg/my_lib_arm/include)

set(x264_libs_dir /home/rog/my_file/other_lib/myffmpeg/x264-master/my_lib/lib)
set(x264_headers_dir /home/rog/my_file/other_lib/myffmpeg/x264-master/my_lib/include)
 
add_library( avcodec shared imported)
add_library( avfilter shared imported )
add_library( swresample shared imported )
add_library( swscale shared imported )
add_library( avformat shared imported )
add_library( avutil shared imported )
 
set_target_properties( avcodec properties imported_location ${ffmpeg_libs_dir}/libavcodec.so )
set_target_properties( avfilter properties imported_location ${ffmpeg_libs_dir}/libavfilter.so )
set_target_properties( swresample properties imported_location ${ffmpeg_libs_dir}/libswresample.so )
set_target_properties( swscale properties imported_location ${ffmpeg_libs_dir}/libswscale.so )
set_target_properties( avformat properties imported_location ${ffmpeg_libs_dir}/libavformat.so )
set_target_properties( avutil properties imported_location ${ffmpeg_libs_dir}/libavutil.so )


include_directories( ${ffmpeg_headers_dir} )
link_directories(${ffmpeg_libs_dir} )

include_directories( ${x264_headers_dir} )
link_directories(${x264_libs_dir} )
 
set(cmake_cxx_standard 14)
add_executable(w_test main.cpp)
target_link_libraries(${project_name} ${opencv_libs} avcodec avformat avutil swresample swscale swscale avfilter postproc x264 )

参考:
最简单的基于ffmpeg的推流器(以推送rtmp为例)

ffmpeg c++代码推流rtsp/rtmp(命令行推流)

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com