因为项目需要,最近在Windows上实现了rtsp通过FFmpeg+nginx(nginx-http-flv-module)发布成rtmp和http-flv给VLC或h5 video(flv.js)播放的功能。
编译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,在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';
}
}
......
}
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编码的音频。
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;
未央千城的博客:介绍了rtmp-http-flv-module和http-flv的知识
雷神的博客:介绍了基于FFmpeg的推流器(rtmp)
CSND下载地址:https://download.csdn.net/download/qq_22633333/12572493