天天看点

基于FFmpeg的推流器 (simplest ffmpeg streamer)

基于FFmpeg的推流器 (simplest ffmpeg streamer)。推流器的作用就是将本地的视频数据推送至流媒体服务器。本文记录的推流器,可以将本地的 MOV / AVI / MKV / MP4 / FLV 等格式的媒体文件,通过流媒体协议(例如RTMP,HTTP,UDP,TCP,RTP等等)以直播流的形式推送出去。目前主要的推流器如下:TMP流媒体服务器(例如 Flash Media Server,Red5,Wowza等等),本文实现推流器后用相应的flash media server实现直播流播放

推流器代码如下:

  1. #include <stdio.h>

  2. #define __STDC_CONSTANT_MACROS

  3. #ifdef _WIN32

  4. //Windows

  5. extern "C"

  6. {

  7. #include "libavformat/avformat.h"

  8. #include "libavutil/mathematics.h"

  9. #include "libavutil/time.h"

  10. };

  11. #else

  12. //Linux...

  13. #ifdef __cplusplus

  14. extern "C"

  15. {

  16. #endif

  17. #include <libavformat/avformat.h>

  18. #include <libavutil/mathematics.h>

  19. #include <libavutil/time.h>

  20. #ifdef __cplusplus

  21. };

  22. #endif

  23. #endif

  24. int main(int argc, char* argv[])

  25. {

  26. AVOutputFormat *ofmt = NULL;

  27. //Input AVFormatContext and Output AVFormatContext

  28. AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;

  29. AVPacket pkt;

  30. const char *in_filename, *out_filename;

  31. int ret, i;

  32. int videoindex=-1;

  33. int frame_index=0;

  34. int64_t start_time=0;

  35. in_filename = "dakongtou.mp4";//输入URL(Input file URL)

  36. //in_filename = "shanghai03_p.h264";

  37. out_filename = "rtmp://localhost/test/livestream";//输出 URL(Output URL)[RTMP]

  38. av_register_all();

  39. //Network

  40. avformat_network_init();

  41. //Input

  42. if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {

  43. printf( "Could not open input file.");

  44. goto end;

  45. }

  46. if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {

  47. printf( "Failed to retrieve input stream information");

  48. goto end;

  49. }

  50. for(i=0; i<ifmt_ctx->nb_streams; i++)

  51. if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){

  52. videoindex=i;

  53. break;

  54. }

  55. av_dump_format(ifmt_ctx, 0, in_filename, 0);

  56. //Output

  57. avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_filename); //RTMP

  58. //avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);//UDP

  59. if (!ofmt_ctx) {

  60. printf( "Could not create output context\n");

  61. ret = AVERROR_UNKNOWN;

  62. goto end;

  63. }

  64. ofmt = ofmt_ctx->oformat;

  65. for (i = 0; i < ifmt_ctx->nb_streams; i++) {

  66. //Create output AVStream according to input AVStream

  67. AVStream *in_stream = ifmt_ctx->streams[i];

  68. AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);

  69. if (!out_stream) {

  70. printf( "Failed allocating output stream\n");

  71. ret = AVERROR_UNKNOWN;

  72. goto end;

  73. }

  74. //Copy the settings of AVCodecContext

  75. ret = avcodec_copy_context(out_stream->codec, in_stream->codec);

  76. if (ret < 0) {

  77. printf( "Failed to copy context from input to output stream codec context\n");

  78. goto end;

  79. }

  80. out_stream->codec->codec_tag = 0;

  81. if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)

  82. out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;

  83. }

  84. //Dump Format------------------

  85. av_dump_format(ofmt_ctx, 0, out_filename, 1);

  86. //Open output URL

  87. if (!(ofmt->flags & AVFMT_NOFILE)) {

  88. ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);

  89. if (ret < 0) {

  90. printf( "Could not open output URL '%s'", out_filename);

  91. goto end;

  92. }

  93. }

  94. //Write file header

  95. ret = avformat_write_header(ofmt_ctx, NULL);

  96. if (ret < 0) {

  97. printf( "Error occurred when opening output URL\n");

  98. goto end;

  99. }

  100. start_time=av_gettime();

  101. while (1) {

  102. AVStream *in_stream, *out_stream;

  103. //Get an AVPacket

  104. ret = av_read_frame(ifmt_ctx, &pkt);

  105. if (ret < 0)

  106. break;

  107. //FIX:No PTS (Example: Raw H.264)

  108. //Simple Write PTS

  109. if(pkt.pts==AV_NOPTS_VALUE){

  110. //Write PTS

  111. AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;

  112. //Duration between 2 frames (us)

  113. int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);

  114. //Parameters

  115. pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);

  116. pkt.dts=pkt.pts;

  117. pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);

  118. }

  119. //Important:Delay

  120. if(pkt.stream_index==videoindex){

  121. AVRational time_base=ifmt_ctx->streams[videoindex]->time_base;

  122. AVRational time_base_q={1,AV_TIME_BASE};

  123. int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);

  124. int64_t now_time = av_gettime() - start_time;

  125. if (pts_time > now_time)

  126. av_usleep(pts_time - now_time);

  127. }

  128. in_stream = ifmt_ctx->streams[pkt.stream_index];

  129. out_stream = ofmt_ctx->streams[pkt.stream_index];

  130. //Convert PTS/DTS

  131. 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));

  132. 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));

  133. pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);

  134. pkt.pos = -1;

  135. //Print to Screen

  136. if(pkt.stream_index==videoindex){

  137. printf("Send %8d video frames to output URL\n",frame_index);

  138. frame_index++;

  139. }

  140. //ret = av_write_frame(ofmt_ctx, &pkt);

  141. ret = av_interleaved_write_frame(ofmt_ctx, &pkt);

  142. if (ret < 0) {

  143. printf( "Error muxing packet\n");

  144. break;

  145. }

  146. av_free_packet(&pkt);

  147. }

  148. //Write file trailer

  149. av_write_trailer(ofmt_ctx);

  150. end:

  151. avformat_close_input(&ifmt_ctx);

  152. if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))

  153. avio_close(ofmt_ctx->pb);

  154. avformat_free_context(ofmt_ctx);

  155. if (ret < 0 && ret != AVERROR_EOF) {

  156. printf( "Error occurred.\n");

  157. return -1;

  158. }

  159. return 0;

  160. }

由代码中可以看出,视频源为dakongtou.mp4,推送出的url地址为rtmp://localhost/test/livestream

继续阅读