记录RTSP通过FFmpeg+nginx发布成rtmp和http-flv

背景

因为项目需要,最近在Windows上实现了rtsp通过FFmpeg+nginx(nginx-http-flv-module)发布成rtmp和http-flv给VLC或h5 video(flv.js)播放的功能。

编译nginx

编译nginx需要加入nginx-http-flv-module模块(参考:windows下编译nginx),执行configure的时候要加入–add-module=path/lib/nginx-http-flv-module选项。
nginx的具体编译过程不详介绍。
重新编译nginx是为了集成nginx-http-flv-module这个模块,在发布rtmp的时候可以转化成支持HTTP方式的FLV。

配置nginx.conf

配置好nginx.conf,在cmd下启动nginx服务

rtmp {
	......
    server {
        listen 1985;
        application live {  
            live on;
        }
	}
	......
}

http {
    ......
    server {
        listen       8080;
        server_name	 localhost;
		
		location /live {
			flv_live on;
            chunked_transfer_encoding  on; #open 'Transfer-Encoding: chunked' response
			add_header 'Access-Control-Allow-Credentials' 'true'; #add additional HTTP header
			add_header 'Access-Control-Allow-Origin' '*'; #add additional HTTP header
			add_header Access-Control-Allow-Headers X-Requested-With;
			add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
			add_header 'Cache-Control' 'no-cache';
        }
	}
	......
}

FFmpeg命令行rtmp推流

Windows下FFmpeg.exe可直接FFmpeg官网下载,配置下环境变量即可。

大华IPC rtsp地址:
rtsp://admin:[email protected]:554/cam/realmonitor?channel=1&subtype=0

海康IPC rtsp地址:
rtsp://admin:[email protected]:554/h264/ch1/main/av_stream?videoCodecType=H.264

命令行推流:
ffmpeg -i rtsp://admin:[email protected]:554/cam/realmonitor?channel=1&subtype=0 -vcodec copy -acodec copy -f flv rtmp://localhost:1985/live/mystream (注意:大华IPC摄像头命令行推流会出错,用FFmpeg API推流不会出错,此问题暂不排查)

ffmpeg -i rtsp://admin:[email protected]:554/h264/ch1/main/av_stream?videoCodecType=H.264 -vcodec copy -acodec copy -f flv rtmp://localhost:1985/live/mystream

VLC和flv.js可以访问的地址:
http://localhost:8080/live?port=1985&app=live&stream=mystream
注意:如果使用flv.js播放流,那么请保证发布的流被正确编码,因为flv.js只支持H.264编码的视频和AAC/MP3编码的音频。

FFmpeg API方式推流

	AVOutputFormat *ofmt = NULL;
	//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;

	in_filename = "rtsp://admin:[email protected]:554/cam/realmonitor?channel=1&subtype=0";
	out_filename = "rtmp://localhost:1985/live/mystream";

	//Input  
	if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
		printf("Could not open input file. in_filename(%s)\n", in_filename);
		goto end;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
		printf("Failed to retrieve input stream information");
		goto end;
	}

	for (i = 0; inb_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  
	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;
		}
		//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 |= AV_CODEC_FLAG_GLOBAL_HEADER;
	}
	//Dump Format------------------  
	av_dump_format(ofmt_ctx, 0, out_filename, 1);
	//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;
	}

#if USE_H264BSF  
	AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
#endif  

	while (1) {
		AVStream *in_stream, *out_stream;
		//Get an AVPacket  
		ret = av_read_frame(ifmt_ctx, &pkt);
		if (ret < 0)
			break;

		in_stream = ifmt_ctx->streams[pkt.stream_index];
		out_stream = ofmt_ctx->streams[pkt.stream_index];
		/* copy packet */
		//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("Receive %8d video frames from input URL\n", frame_index);
			frame_index++;
#if USE_H264BSF  
			av_bitstream_filter_filter(h264bsfc, in_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif  
		}
		
		ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
		if (ret < 0 && frame_index > 3) {
			printf("Error muxing packet\n");
			break;
		}

		av_free_packet(&pkt);
	}

#if USE_H264BSF  
	av_bitstream_filter_close(h264bsfc);
#endif  
	//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);
	printf("-----finish------\n");
	if (ret < 0 && ret != AVERROR_EOF) {
		printf("Error occurred.\n");
		return -1;
	}
#endif
	return 0;

播放http-flv

vlc播放器播放:
记录RTSP通过FFmpeg+nginx发布成rtmp和http-flv_第1张图片
flv.js播放:
记录RTSP通过FFmpeg+nginx发布成rtmp和http-flv_第2张图片

致谢

未央千城的博客:介绍了rtmp-http-flv-module和http-flv的知识
雷神的博客:介绍了基于FFmpeg的推流器(rtmp)

下载

CSND下载地址:https://download.csdn.net/download/qq_22633333/12572493

你可能感兴趣的:(ffmpeg)