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

发布时间 2023-05-23 11:11:23作者: 阿风小子
背景
因为项目需要,最近在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:admin123@172.16.28.254:554/cam/realmonitor?channel=1&subtype=0
 
海康IPC rtsp地址:
rtsp://admin:admin123@172.16.28.253:554/h264/ch1/main/av_stream?videoCodecType=H.264
 
命令行推流:
ffmpeg -i rtsp://admin:admin123@172.16.28.254: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:admin123@172.16.28.253: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:admin123@172.16.28.254: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; 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  
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播放器播放:
flv.js播放: