最簡單的基於FFMPEG的推流器附件:收流器,ffmpeg附件
=====================================================
最簡單的基於FFmpeg的推流器系列文章列表:
《最簡單的基於FFmpeg的推流器(以推送RTMP為例)》
《最簡單的基於FFMPEG的推流器附件:收流器》
=====================================================
出於對《最簡單的基於FFmpeg的推流器》的補充,本文記錄一個最簡單的基於FFmpeg的收流器。收流器和推流器的作用正好相反:推流器用於將本地檔案以流媒體的形式發送出去,而收流器用於將流媒體內容儲存為本地檔案。
本文記錄的推流器可以將RTMP流媒體儲存成為一個本地的FLV檔案。由於FFmpeg本身支援很多的流媒體協議和封裝格式,所以也支援其它的封裝格式和流媒體協議。
原始碼
/** * 最簡單的基於FFmpeg的收流器(接收RTMP) * Simplest FFmpeg Receiver (Receive RTMP) * * 雷霄驊 Lei Xiaohua * leixiaohua1020@126.com * 中國傳媒大學/數字電視技術 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本例子將流媒體資料(以RTMP為例)儲存成本地檔案。 * 是使用FFmpeg進行流媒體接收最簡單的教程。 * * This example saves streaming media data (Use RTMP as example) * as a local file. * It's the simplest FFmpeg stream receiver. * */#include <stdio.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32//Windowsextern "C"{#include "libavformat/avformat.h"#include "libavutil/mathematics.h"#include "libavutil/time.h"};#else//Linux...#ifdef __cplusplusextern "C"{#endif#include <libavformat/avformat.h>#include <libavutil/mathematics.h>#include <libavutil/time.h>#ifdef __cplusplus};#endif#endif//'1': Use H.264 Bitstream Filter #define USE_H264BSF 0int main(int argc, char* argv[]){AVOutputFormat *ofmt = NULL;//Input AVFormatContext and Output AVFormatContextAVFormatContext *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 = "rtmp://live.hkstv.hk.lxdns.com/live/hks";//in_filename = "rtp://233.233.233.233:6666";//out_filename = "receive.ts";//out_filename = "receive.mkv";out_filename = "receive.flv";av_register_all();//Networkavformat_network_init();//Inputif ((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);//Outputavformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename); //RTMPif (!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 AVStreamAVStream *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 AVCodecContextret = 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);//Open output URLif (!(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 headerret = avformat_write_header(ofmt_ctx, NULL);if (ret < 0) {printf( "Error occurred when opening output URL\n");goto end;}#if USE_H264BSFAVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb"); #endifwhile (1) {AVStream *in_stream, *out_stream;//Get an AVPacketret = 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/DTSpkt.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 Screenif(pkt.stream_index==videoindex){printf("Receive %8d video frames from input URL\n",frame_index);frame_index++;#if USE_H264BSFav_bitstream_filter_filter(h264bsfc, in_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);#endif}//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);}#if USE_H264BSFav_bitstream_filter_close(h264bsfc); #endif//Write file trailerav_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;}
運行結果程式運行之後,即可擷取流媒體資料並且在本地儲存成一個視頻檔案。
下載
simplest ffmpeg streamer
項目首頁
SourceForge:https://sourceforge.net/projects/simplestffmpegstreamer/
Github:https://github.com/leixiaohua1020/simplest_ffmpeg_streamer
開源中國:http://git.oschina.net/leixiaohua1020/simplest_ffmpeg_streamer
方案套件含2個項目:
simplest_ffmpeg_streamer: 將本地視頻檔案推送至流媒體伺服器。
simplest_ffmpeg_receiver: 將流媒體資料儲存成本地檔案。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。