Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1655910
  • 博文数量: 1493
  • 博客积分: 38
  • 博客等级: 民兵
  • 技术积分: 5834
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-19 17:28
文章分类

全部博文(1493)

文章存档

2016年(11)

2015年(38)

2014年(137)

2013年(253)

2012年(1054)

2011年(1)

分类: C/C++

2013-07-19 22:00:57

本环境的搭建旨在测试IOS  http streaming的测试,进一步探求通过此方式支持实时流的可能性。
一、 ffmpeg的编译和移植:
./configure --enable-gpl --enable-nonfree --enable-pthreads --enable-libfaac --enable-libmp3lame --enable-libx264
make && make install


这里编译的过程需要安装libfaac, libmp3lame, libx264这三个第三方开源编码库。
二、 安装好ffmpeg后,需要准备符合IOS规定的码流,将码流转码成音频为mp3,视频为h264的编码方式,这里需要说明的是,IOS支持1920x1080p,audio和video解码器都是IOS硬件加速当中提供的,因而不需要你对bitrate、schannel、bitrate等进行转换。
ffmpeg -i ~/Videos/1.ts  -f mpegts -acodec libmp3lame -ar 48000 -ab 64k -s 1080x720 -vcodec libx264 sample.ts
获取到符合的码流以后,需要对码流进行segment。
三、 Segment
Ios规定的支持http streaming的方式是由一个索引文件(后缀为.m3u8)和相关分段文件决定的,其中m3u8当中的内容如下:
#EXTM3U
#EXT-X-TARGETDURATION:3
#EXTINF:9,

#EXTINF:7,

#EXTINF:7,

#EXTINF:4,

#EXT-X-ENDLIST
其实就是每个index的url的集合:
Segment.c和Makefile在我的附件当中,因为segment.c是基于ffmpeg去开发的,所以需要你把ffmpeg和segment放在平行目录当中(具体看Makefile调用关系)
./live_segmenter –i -o -d -x -p
分配好以后发现segment时间长度不一致,这是为什么呢,因为H264解码的关系,为了保证每一个segment文件都可以正常播放,因而segment文件的第一个视频帧肯定是key_frame(这样的做法有待商榷)。

点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <sys/types.h>
  5. #include <unistd.h>
  6. #include <assert.h>
  7. #include <float.h>
  8. #include <math.h>

  9. #include "libavformat/avformat.h"
  10. #include "libavutil/opt.h"
  11. #define MAX_LENGTH 1024
  12. typedef struct iputargs_t{
  13.     char input[MAX_LENGTH];
  14.     char output_prefix[MAX_LENGTH];
  15.     char output[MAX_LENGTH];

  16. }iputargs_t;

  17. static iputargs_t input_args;

  18. //----------------------------------------------------------------
  19. // fopen_utf8
  20. //
  21. static FILE *
  22. fopen_utf8(const char * filename, const char * mode)
  23. {
  24.     FILE * file = NULL;
  25.     

  26.     file = fopen(filename, mode);
  27.     
  28.     return file;
  29. }


  30. static AVStream *add_output_stream(AVFormatContext *output_format_context, AVStream *input_stream) {
  31.     AVCodecContext *input_codec_context;
  32.     AVCodecContext *output_codec_context;
  33.     AVStream *output_stream;

  34.     output_stream = avformat_new_stream(output_format_context, NULL);
  35.     if (!output_stream) {
  36.         fprintf(stderr, "Could not allocate stream\n");
  37.         exit(1);
  38.     }
  39.     output_stream->id = 0;

  40.     input_codec_context = input_stream->codec;
  41.     output_codec_context = output_stream->codec;

  42.     output_codec_context->codec_id = input_codec_context->codec_id;
  43.     output_codec_context->codec_type = input_codec_context->codec_type;
  44.     output_codec_context->codec_tag = input_codec_context->codec_tag;
  45.     output_codec_context->bit_rate = input_codec_context->bit_rate;
  46.     output_codec_context->extradata = input_codec_context->extradata;
  47.     output_codec_context->extradata_size = input_codec_context->extradata_size;

  48.     if(av_q2d(input_codec_context->time_base) * input_codec_context->ticks_per_frame > av_q2d(input_stream->time_base) && av_q2d(input_stream->time_base) < 1.0/1000) {
  49.         output_codec_context->time_base = input_codec_context->time_base;
  50.         output_codec_context->time_base.num *= input_codec_context->ticks_per_frame;
  51.     }
  52.     else {
  53.         output_codec_context->time_base = input_stream->time_base;
  54.     }

  55.     switch (input_codec_context->codec_type) {
  56.         case AVMEDIA_TYPE_AUDIO:
  57.             output_codec_context->channel_layout = input_codec_context->channel_layout;
  58.             output_codec_context->sample_rate = input_codec_context->sample_rate;
  59.             output_codec_context->channels = input_codec_context->channels;
  60.             output_codec_context->frame_size = input_codec_context->frame_size;
  61.             if ((input_codec_context->block_align == 1 && input_codec_context->codec_id == CODEC_ID_MP3) || input_codec_context->codec_id == CODEC_ID_AC3) {
  62.                 output_codec_context->block_align = 0;
  63.             }
  64.             else {
  65.                 output_codec_context->block_align = input_codec_context->block_align;
  66.             }
  67.             break;
  68.         case AVMEDIA_TYPE_VIDEO:
  69.             output_codec_context->pix_fmt = input_codec_context->pix_fmt;
  70.             output_codec_context->width = input_codec_context->width;
  71.             output_codec_context->height = input_codec_context->height;
  72.             output_codec_context->has_b_frames = input_codec_context->has_b_frames;

  73.             if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
  74.                 output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
  75.             }
  76.             break;
  77.     default:
  78.         break;
  79.     }

  80.     return output_stream;
  81. }

  82. typedef struct SMSegmentInfo
  83. {
  84.     unsigned int index;
  85.     double duration;
  86.     char * filename;
  87.   
  88. } TSMSegmentInfo;

  89. typedef struct SMPlaylist
  90. {
  91.     /* a ring buffer of segments */
  92.     TSMSegmentInfo * buffer;

  93.     /* maximum number of segments that can be stored in the ring buffer */
  94.     unsigned int bufferCapacity;

  95.     /* index of the first segment on the ring buffer */
  96.     unsigned int first;
  97.     
  98.     /* how many segments are currently in the ring buffer */
  99.     unsigned int count;

  100.     /* shortcuts */
  101.     unsigned int targetDuration;
  102.     char * httpPrefix;

  103.     /* playlist file used for non-live streaming */
  104.     FILE * file;
  105.   
  106. } TSMPlaylist;

  107. static char *
  108. duplicateString(const char * str)
  109. {
  110.     /* unfortunately strdup isn't always available */
  111.     size_t strSize = strlen(str) + 1;
  112.     char * copy = (char *) malloc(strSize);
  113.     memcpy(copy, str, strSize);
  114.     return copy;
  115. }

  116. static TSMPlaylist *
  117. createPlaylist(const unsigned int max_segments,
  118.                const unsigned int target_segment_duration,
  119.                const char * http_prefix)
  120. {
  121.     TSMPlaylist * playlist = (TSMPlaylist *) malloc(sizeof(TSMPlaylist));
  122.     memset(playlist, 0, sizeof(TSMPlaylist));

  123.     if (max_segments)
  124.     {
  125.         playlist->buffer = (TSMSegmentInfo *) malloc(sizeof(TSMSegmentInfo) *
  126.                                                      max_segments);
  127.     }
  128.     
  129.     playlist->bufferCapacity = max_segments;
  130.     playlist->targetDuration = target_segment_duration;
  131.     playlist->httpPrefix = duplicateString(http_prefix);
  132.     
  133.     return playlist;
  134. }

  135. static void
  136. updateLivePlaylist(TSMPlaylist * playlist,
  137.                    const char * playlistFileName,
  138.                    const char * outputFileName,
  139.                    const unsigned int segmentIndex,
  140.                    const double segmentDuration)
  141. {
  142.     unsigned int bufferIndex = 0;
  143.     TSMSegmentInfo * nextSegment = NULL;
  144.     TSMSegmentInfo removeMe;
  145.     memset(&removeMe, 0, sizeof(removeMe));
  146.     assert(!playlist->file);
  147.     
  148.     if (playlist->count == playlist->bufferCapacity)
  149.     {
  150.         /* keep track of the segment that should be removed */
  151.         removeMe = playlist->buffer[playlist->first];
  152.         
  153.         /* make room for the new segment */
  154.         playlist->first++;
  155.         playlist->first %= playlist->bufferCapacity;
  156.     }
  157.     else
  158.     {
  159.         playlist->count++;
  160.     }

  161.     /* store the new segment info */
  162.     bufferIndex = ((playlist->first + playlist->count - 1) %
  163.                    playlist->bufferCapacity);
  164.     nextSegment = &playlist->buffer[bufferIndex];
  165.     nextSegment->filename = duplicateString(outputFileName);
  166.     nextSegment->duration = segmentDuration;
  167.     nextSegment->index = segmentIndex;
  168.     
  169.     /* live streaming -- write full playlist from scratch */
  170.     playlist->file = fopen_utf8(playlistFileName, "w+b");
  171.     
  172.     if (playlist->file)
  173.     {
  174.         unsigned int i, j;
  175.         const TSMSegmentInfo * first = &playlist->buffer[playlist->first];
  176.         
  177.         char tmp[1024] = { 0 };
  178.         snprintf(tmp,
  179.                  sizeof(tmp),
  180.                  "#EXTM3U\n"
  181.                  "#EXT-X-TARGETDURATION:%u\n"
  182.                  "#EXT-X-MEDIA-SEQUENCE:%u\n",
  183.                  playlist->targetDuration,
  184.                  first->index);
  185.         fwrite(tmp, strlen(tmp), 1, playlist->file);
  186.         
  187.         for ( i = 0; i < playlist->count; i++)
  188.         {
  189.            j = ((playlist->first + i) %
  190.                               playlist->bufferCapacity);
  191.             
  192.             const TSMSegmentInfo * segment = &playlist->buffer[j];
  193.             snprintf(tmp,
  194.                      sizeof(tmp),
  195.                      "#EXTINF:%u,\n%s%s\n",
  196.                      (int)(segment->duration + 0.5),
  197.                      playlist->httpPrefix,
  198.                      segment->filename);
  199.             fwrite(tmp, strlen(tmp), 1, playlist->file);
  200.         }
  201.         
  202.         // snprintf(tmp, sizeof(tmp), "#EXT-X-ENDLIST\n");
  203.         // fwrite(tmp, strlen(tmp), 1, playlist->file);
  204.         
  205.         fclose(playlist->file);
  206.         playlist->file = NULL;
  207.     }
  208.     else
  209.     {
  210.         fprintf(stderr,
  211.                 "Could not open m3u8 index file (%s), "
  212.                 "no index file will be created\n",
  213.                 playlistFileName);
  214.     }
  215.     
  216.     if (removeMe.filename)
  217.     {
  218.         /* remove the oldest segment file */
  219.         remove(removeMe.filename);
  220.         free(removeMe.filename);
  221.     }
  222. }

  223. static void
  224. updatePlaylist(TSMPlaylist * playlist,
  225.                const char * playlistFileName,
  226.                const char * segmentFileName,
  227.                const unsigned int segmentIndex,
  228.                const int segmentDuration)
  229. {
  230.     if (playlist->bufferCapacity > 0)
  231.     {
  232.         /* create a live streaming playlist */
  233.         updateLivePlaylist(playlist,
  234.                            playlistFileName,
  235.                            segmentFileName,
  236.                            segmentIndex,
  237.                            segmentDuration);
  238.     }
  239.     else
  240.     {
  241.         /* append to the existing playlist */
  242.         char tmp[1024] = { 0 };

  243.         if (!playlist->file)
  244.         {
  245.             playlist->file = fopen_utf8(playlistFileName, "w+b");
  246.             snprintf(tmp,
  247.                      sizeof(tmp),
  248.                      "#EXTM3U\n"
  249.                      "#EXT-X-TARGETDURATION:%u\n",
  250.                      playlist->targetDuration);
  251.             fwrite(tmp, strlen(tmp), 1, playlist->file);
  252.         }
  253.         
  254.         if (!playlist->file)
  255.         {
  256.             fprintf(stderr,
  257.                     "Could not open m3u8 index file (%s), "
  258.                     "no index file will be created\n",
  259.                     playlistFileName);
  260.         }
  261.         
  262.         snprintf(tmp,
  263.                  sizeof(tmp),
  264.                  "#EXTINF:%u,\n%s%s\n",
  265.                  segmentDuration,
  266.                  playlist->httpPrefix,
  267.                  segmentFileName);
  268.         fwrite(tmp, strlen(tmp), 1, playlist->file);
  269.         fflush(playlist->file);
  270.     }
  271. }

  272. static void
  273. closePlaylist(TSMPlaylist * playlist)
  274. {
  275.     if (playlist->file)
  276.     {
  277.         /* append to the existing playlist */
  278.         char tmp[1024] = { 0 };
  279.         
  280.         snprintf(tmp, sizeof(tmp), "#EXT-X-ENDLIST\n");
  281.         fwrite(tmp, strlen(tmp), 1, playlist->file);
  282.         
  283.         fclose(playlist->file);
  284.         playlist->file = NULL;
  285.     }
  286. }

  287. static void
  288. releasePlaylist(TSMPlaylist ** playlistRef)
  289. {
  290.     TSMPlaylist * playlist = *playlistRef;
  291.     closePlaylist(playlist);
  292.     unsigned int i ;
  293.     
  294.     for (i = 0; i < playlist->bufferCapacity; i++)
  295.     {
  296.         TSMSegmentInfo * segmentInfo = &playlist->buffer[i];
  297.         if (segmentInfo->filename)
  298.         {
  299.             free(segmentInfo->filename);
  300.         }
  301.     }
  302.     
  303.     free(playlist->buffer);
  304.     free(playlist->httpPrefix);
  305.     free(playlist);
  306.     *playlistRef = NULL;
  307. }
  308.     

  309. typedef struct SMPacketLink
  310. {
  311.     /* packet start time in seconds */
  312.     double timeStamp;

  313.     /* the packet */
  314.     AVPacket packet;

  315.     /* a link to the next packet */
  316.     struct SMPacketLink * next;
  317.     
  318. } TSMPacketLink;

  319. typedef struct SMPacketList
  320. {
  321.     TSMPacketLink * head;
  322.     TSMPacketLink * tail;
  323.     unsigned int size;
  324. } TSMPacketList;

  325. typedef struct SMStreamLace
  326. {
  327.     TSMPacketList ** streams;
  328.     unsigned int numStreams;
  329. } TSMStreamLace;

  330. static TSMPacketLink *
  331. createLink(const AVPacket * packet, double timeStamp)
  332. {
  333.     TSMPacketLink * link = (TSMPacketLink *) malloc(sizeof(TSMPacketLink));
  334.     link->timeStamp = timeStamp;
  335.     link->next = NULL;
  336.     memcpy(&link->packet, packet, sizeof(AVPacket));
  337.     return link;
  338. }

  339. static void
  340. fifoPush(TSMPacketList * packets, const AVPacket * packet, double timeStamp)
  341. {
  342.     TSMPacketLink * link = createLink(packet, timeStamp);
  343.     if (!packets->head)
  344.     {
  345.         assert(!packets->tail);
  346.         assert(!packets->size);
  347.         packets->head = link;
  348.         packets->tail = link;
  349.         packets->size = 1;
  350.     }
  351.     else
  352.     {
  353.         /* attach at the tail */
  354.         assert(packets->size > 0);
  355.         
  356.         packets->tail->next = link;
  357.         packets->tail = link;
  358.         packets->size++;
  359.     }
  360. }

  361. static int
  362. fifoPop(TSMPacketList * packets, AVPacket * packet)
  363. {
  364.     TSMPacketLink * link = packets->head;
  365.     if (!link)
  366.     {
  367.         return 0;
  368.     }
  369.     
  370.     memcpy(packet, &link->packet, sizeof(AVPacket));
  371.     packets->head = link->next;
  372.     packets->size--;
  373.     
  374.     if (!packets->head)
  375.     {
  376.         packets->tail = NULL;
  377.     }
  378.     
  379.     free(link);
  380.     return 1;
  381. }

  382. static TSMPacketList *
  383. createPacketList()
  384. {
  385.     TSMPacketList * packets = (TSMPacketList *)malloc(sizeof(TSMPacketList));
  386.     memset(packets, 0, sizeof(TSMPacketList));
  387.     return packets;
  388. }

  389. static TSMStreamLace *
  390. createStreamLace(unsigned int numStreams)
  391. {
  392.     unsigned int i;
  393.     TSMStreamLace * lace = (TSMStreamLace *)malloc(sizeof(TSMStreamLace));
  394.     lace->streams = (TSMPacketList **)malloc(sizeof(TSMPacketList *) * numStreams);
  395.     
  396.     for ( i = 0; i < numStreams; i++)
  397.     {
  398.         lace->streams[i] = createPacketList();
  399.     }

  400.     lace->numStreams = numStreams;
  401.     return lace;
  402. }

  403. static void
  404. insertPacket(TSMStreamLace * lace, const AVPacket * packet, double timeStamp)
  405. {
  406.     fifoPush(lace->streams[packet->stream_index], packet, timeStamp);
  407. }

  408. static TSMPacketList *
  409. chooseNextStream(TSMStreamLace * lace)
  410. {
  411.     /* improve lacing so that that audio/video packets that should be
  412.        together do not get stuck into separate segments. */
  413.     
  414.     TSMPacketList * nextStream = NULL;
  415.     double earliestTimeStamp = DBL_MAX;
  416.     unsigned int i;
  417.     for (i = 0; i < lace->numStreams; i++)
  418.     {
  419.         TSMPacketList * stream = lace->streams[i];
  420.         if (stream->size && stream->head->timeStamp < earliestTimeStamp)
  421.         {
  422.             nextStream = stream;
  423.             earliestTimeStamp = stream->head->timeStamp;
  424.         }
  425.     }
  426.     
  427.     return nextStream;
  428. }

  429. static int
  430. removePacket(TSMStreamLace * lace, AVPacket * packet)
  431. {
  432.     TSMPacketList * stream = chooseNextStream(lace);
  433.     if (!stream)
  434.     {
  435.         return 0;
  436.     }
  437.     
  438.     return fifoPop(stream, packet);
  439. }

  440. static unsigned int
  441. countPackets(const TSMStreamLace * lace)
  442. {
  443.     unsigned int numPackets = 0;
  444.     unsigned int i;
  445.     for ( i = 0; i < lace->numStreams; i++)
  446.     {
  447.         const TSMPacketList * stream = lace->streams[i];
  448.         numPackets += stream->size;
  449.     }

  450.     return numPackets;
  451. }

  452. static void
  453. removeAllPackets(TSMStreamLace * lace)
  454. {
  455.     unsigned int i;
  456.     AVPacket packet;
  457.     for ( i = 0; i < lace->numStreams; i++)
  458.     {
  459.         TSMPacketList * stream = lace->streams[i];
  460.         while (stream->size)
  461.         {
  462.             fifoPop(stream, &packet);
  463.             av_free_packet(&packet);
  464.         }
  465.     }
  466. }

  467. static int
  468. loglevel(const char* arg)
  469. {
  470.     const struct { const char *name; int level; } log_levels[] = {
  471.         { "quiet" , AV_LOG_QUIET },
  472.         { "panic" , AV_LOG_PANIC },
  473.         { "fatal" , AV_LOG_FATAL },
  474.         { "error" , AV_LOG_ERROR },
  475.         { "warning", AV_LOG_WARNING },
  476.         { "info" , AV_LOG_INFO },
  477.         { "verbose", AV_LOG_VERBOSE },
  478.         { "debug" , AV_LOG_DEBUG },
  479.     };
  480.     int i;
  481.     
  482.     for (i = 0; i < FF_ARRAY_ELEMS(log_levels); i++) {
  483.         if (!strcmp(log_levels[i].name, arg)) {
  484.             av_log_set_level(log_levels[i].level);
  485.             return 0;
  486.         }
  487.     }
  488.     
  489.     return 1;
  490. }

  491. //----------------------------------------------------------------
  492. // usage3
  493. //
  494. static void usage3(char ** argv, const char * message, const char * details)
  495. {
  496.     if (message)
  497.     {
  498.         fprintf(stderr, "ERROR: %s%s\n\n", message, details);
  499.     }
  500.     
  501.     fprintf(stderr,
  502.             "USAGE: %s "
  503.             "-i input-MPEG-TS-file "
  504.             "-d seconds-per-segment "
  505.             "[-o segment-file-prefix] "
  506.             "-x output-playlist-m3u8 "
  507.             "[-p http-prefix] "
  508.             "[-w max-live-segments] "
  509.             "[-P pid-file] "
  510.             "[--watch-for-kill-file] "
  511.             "[--strict-segment-duration] "
  512.             "[--avformat-option opt value] "
  513.             "[--loglevel level] "
  514.             "\n\n",
  515.             argv[0]);
  516.     
  517.     fprintf(stderr,
  518.             "Compiled by Daniel Espendiller - \n"
  519.             "build on %s %s with %s\n\n"
  520.             "Took some code from:\n"
  521.             " - source:\n"
  522.             " - iStreamdev:http://projects.vdr-developer.org/git/?p=istreamdev.git;a=tree;f=segmenter;hb=HEAD\n"
  523.             " - live_segmenter:\n",
  524.             __DATE__,
  525.             __TIME__,
  526.             __VERSION__);
  527.     
  528.     exit(1);
  529. }

  530. //----------------------------------------------------------------
  531. // usage
  532. //
  533. static void usage(char ** argv, const char * message)
  534. { usage3(argv, message, ""); }

  535. //----------------------------------------------------------------
  536. // main_utf8
  537. //
  538. int main_utf8(int argc, char **argv)
  539. {
  540.     double target_segment_duration = 0.0;
  541.     char *segment_duration_check = NULL;
  542.     const char *playlist_filename = NULL;
  543.     const char *http_prefix = "";
  544.     long max_tsfiles = 0;
  545.     char *max_tsfiles_check = NULL;
  546.     double prev_segment_time = 0.0;
  547.     double segment_duration = 0.0;
  548.     unsigned int output_index = 0;
  549.     const AVClass *fc = avformat_get_class();
  550.     AVDictionary *format_opts = NULL;
  551.     AVOutputFormat *ofmt = NULL;
  552.     AVFormatContext *ic = NULL;
  553.     AVFormatContext *oc = NULL;
  554.     AVStream *video_st = NULL;
  555.     AVStream *audio_st = NULL;
  556.     AVCodec *codec = NULL;
  557.     char *pid_filename = NULL;
  558.     int video_index = -1;
  559.     int audio_index = -1;
  560.     int kill_file = 0;
  561.     int decode_done = 0;
  562.     int ret = 0;
  563.     int i = 0;
  564.     TSMStreamLace * streamLace = NULL;
  565.     TSMPlaylist * playlist = NULL;
  566.     const double segment_duration_error_tolerance = 0.05;
  567.     double extra_duration_needed = 0;
  568.     int strict_segment_duration = 0;
  569.     
  570.     av_log_set_level(AV_LOG_INFO);
  571.     
  572.     for (i = 1; i < argc; i++)
  573.     {
  574.         if (strcmp(argv[i], "-i") == 0)
  575.         {
  576.             if ((argc - i) <= 1)
  577.                 usage(argv, "could not parse -i parameter");
  578.             i++;
  579.             strcpy(input_args.input, argv[i]);
  580.         }
  581.         else if (strcmp(argv[i], "-o") == 0)
  582.         {
  583.             if ((argc - i) <= 1)
  584.                 usage(argv, "could not parse -i parameter");
  585.             i++;
  586.             strcpy(input_args.output_prefix, argv[i]);
  587.         }
  588.         else if (strcmp(argv[i], "-d") == 0)
  589.         {
  590.             if ((argc - i) <= 1) usage(argv, "could not parse -d parameter");
  591.                 i++;
  592.             
  593.             target_segment_duration = strtod(argv[i], &segment_duration_check);
  594.             if (segment_duration_check == argv[i] ||
  595.             target_segment_duration == HUGE_VAL ||
  596.             target_segment_duration == -HUGE_VAL)
  597.             {
  598.                 usage3(argv, "invalid segment duration: ", argv[i]);
  599.             }
  600.         }
  601.         else if (strcmp(argv[i], "-x") == 0)
  602.         {
  603.             if ((argc - i) <= 1) usage(argv, "could not parse -x parameter");
  604.             i++;
  605.             playlist_filename = argv[i];
  606.         }
  607.         else if (strcmp(argv[i], "-p") == 0)
  608.         {
  609.             if ((argc - i) <= 1) usage(argv, "could not parse -p parameter");
  610.                 i++;
  611.             http_prefix = argv[i];
  612.         }
  613.         else if (strcmp(argv[i], "-w") == 0)
  614.         {
  615.             if ((argc - i) <= 1) usage(argv, "could not parse -w parameter");
  616.                 i++;

  617.             max_tsfiles = strtol(argv[i], &max_tsfiles_check, 10);
  618.             if (max_tsfiles_check == argv[i] ||
  619.                 max_tsfiles < 0 ||
  620.                 max_tsfiles >= INT_MAX)
  621.             {
  622.                 usage3(argv, "invalid live stream max window size: ", argv[i]);
  623.             }
  624.         }
  625.     }
  626.     
  627.     if (!input_args.input)
  628.     {
  629.         usage(argv, "-i input file parameter must be specified");
  630.     }
  631.     printf("+++input_file: %s\n", input_args.input);
  632.     
  633.     if (!playlist_filename)
  634.     {
  635.         usage(argv, "-x m3u8 playlist file parameter must be specified");
  636.     }
  637.     printf("+++playlist_filename: %s\n", playlist_filename);
  638.     
  639.     if (target_segment_duration == 0.0)
  640.     {
  641.         usage(argv, "-d segment duration parameter must be specified");
  642.     }
  643.     printf("+++target_segment_duration: %f\n", target_segment_duration);

  644.     av_register_all();
  645.     avformat_network_init();

  646.     if (!strcmp(input_args.input, "-")) {
  647.         strcpy(input_args.input, "pipe:");
  648.     }

  649.     playlist = createPlaylist(max_tsfiles,
  650.                               target_segment_duration,
  651.                               http_prefix);

  652.     if (!playlist)
  653.     {
  654.         fprintf(stderr, "Could not allocate space for m3u8 playlist structure\n");
  655.         goto error;
  656.     }
  657.     
  658.     ic = avformat_alloc_context();
  659.     ret = avformat_open_input(&ic, input_args.input, NULL, NULL);
  660.     if (ret != 0) {
  661.         fprintf(stderr, "Could not open input file, make sure it is an mpegts or mp4 file: %d\n", ret);
  662.         goto error;
  663.     }
  664.     
  665.     if (avformat_find_stream_info(ic, NULL) < 0) {
  666.         fprintf(stderr, "Could not read stream information\n");
  667.         goto error;
  668.     }
  669.     av_dump_format(ic, 0, input_args.input, 0);

  670.     ofmt = av_guess_format("mpegts", NULL, NULL);
  671.     if (!ofmt) {
  672.         fprintf(stderr, "Could not find MPEG-TS muxer\n");
  673.         goto error;
  674.     }

  675.     oc = avformat_alloc_context();
  676.     if (!oc) {
  677.         fprintf(stderr, "Could not allocated output context\n");
  678.         goto error;
  679.     }
  680.     oc->oformat = ofmt;
  681.     
  682.     video_index = -1;
  683.     audio_index = -1;
  684.     for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) {
  685.         switch (ic->streams[i]->codec->codec_type) {
  686.         case AVMEDIA_TYPE_VIDEO:
  687.             video_index = i;
  688.             ic->streams[i]->discard = AVDISCARD_NONE;
  689.             video_st = add_output_stream(oc, ic->streams[i]);
  690.         break;
  691.         case AVMEDIA_TYPE_AUDIO:
  692.             audio_index = i;
  693.             ic->streams[i]->discard = AVDISCARD_NONE;
  694.             audio_st = add_output_stream(oc, ic->streams[i]);
  695.         break;
  696.         default:
  697.             ic->streams[i]->discard = AVDISCARD_ALL;
  698.         break;
  699.         }
  700.     }

  701.     av_dump_format(oc, 0, input_args.output_prefix, 1);
  702.     if (video_index >=0) {
  703.         codec = avcodec_find_decoder(video_st->codec->codec_id);
  704.         if (!codec) {
  705.             fprintf(stderr, "Could not find video decoder, key frames will not be honored\n");
  706.         }

  707.         if (avcodec_open2(video_st->codec, codec, NULL) < 0) {
  708.             fprintf(stderr, "Could not open video decoder, key frames will not be honored\n");
  709.         }
  710.     }
  711.     sprintf(input_args.output, "%s-%u.ts", input_args.output_prefix, ++output_index);
  712.     if (avio_open(&oc->pb, input_args.output, AVIO_FLAG_WRITE) < 0) {
  713.         fprintf(stderr, "Could not open '%s'\n", input_args.output);
  714.         goto error;
  715.     }

  716.     if (avformat_write_header(oc, NULL)) {
  717.         fprintf(stderr, "Could not write mpegts header to first output file\n");
  718.         goto error;
  719.     }

  720.     prev_segment_time = (double)(ic->start_time) / (double)(AV_TIME_BASE);

  721.     streamLace = createStreamLace(ic->nb_streams);
  722.     
  723.     do {
  724.         double segment_time = 0.0;
  725.         AVPacket packet;
  726.         double packetStartTime = 0.0;
  727.         double packetDuration = 0.0;
  728.         
  729.         if (!decode_done)
  730.         {
  731.             decode_done = av_read_frame(ic, &packet);
  732.             if (!decode_done)
  733.             {
  734.                 if (packet.stream_index != video_index &&
  735.                     packet.stream_index != audio_index)
  736.                 {
  737.                     av_free_packet(&packet);
  738.                     continue;
  739.                 }
  740.                 
  741.                 double timeStamp =
  742.                     (double)(packet.pts) *
  743.                     (double)(ic->streams[packet.stream_index]->time_base.num) /
  744.                     (double)(ic->streams[packet.stream_index]->time_base.den);
  745.                 
  746.                 if (av_dup_packet(&packet) < 0)
  747.                 {
  748.                     fprintf(stderr, "Could not duplicate packet\n");
  749.                     av_free_packet(&packet);
  750.                     break;
  751.                 }
  752.                 
  753.                 insertPacket(streamLace, &packet, timeStamp);
  754.             }
  755.         }
  756.         
  757.         if (countPackets(streamLace) < 50 && !decode_done)
  758.         {
  759.             /* allow the queue to fill up so that the packets can be sorted properly */
  760.             continue;
  761.         }
  762.         
  763.         if (!removePacket(streamLace, &packet))
  764.         {
  765.             if (decode_done)
  766.             {
  767.                 /* the queue is empty, we are done */
  768.                 break;
  769.             }
  770.             
  771.             assert(decode_done);
  772.             continue;
  773.         }
  774.         
  775.         packetStartTime =
  776.             (double)(packet.pts) *
  777.             (double)(ic->streams[packet.stream_index]->time_base.num) /
  778.             (double)(ic->streams[packet.stream_index]->time_base.den);
  779.         
  780.         packetDuration =
  781.             (double)(packet.duration) *
  782.             (double)(ic->streams[packet.stream_index]->time_base.num) /
  783.             (double)(ic->streams[packet.stream_index]->time_base.den);
  784.         
  785. #if !defined(NDEBUG) && (defined(DEBUG) || defined(_DEBUG))
  786.         if (av_log_get_level() >= AV_LOG_VERBOSE)
  787.             fprintf(stderr,
  788.                     "stream %i, packet [%f, %f)\n",
  789.                     packet.stream_index,
  790.                     packetStartTime,
  791.                     packetStartTime + packetDuration);
  792. #endif

  793.         segment_duration = packetStartTime + packetDuration - prev_segment_time;

  794.         // NOTE: segments are supposed to start on a keyframe.
  795.         // If the keyframe interval and segment duration do not match
  796.         // forcing the segment creation for "better seeking behavior"
  797.         // will result in decoding artifacts after seeking or stream switching.
  798.         if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY || strict_segment_duration)) {
  799.             segment_time = packetStartTime;
  800.         }
  801.         else if (video_index < 0) {
  802.             segment_time = packetStartTime;
  803.         }
  804.         else {
  805.             segment_time = prev_segment_time;
  806.         }

  807.     printf("segment time: %f\n", segment_time);
  808.         if (segment_time - prev_segment_time + segment_duration_error_tolerance >
  809.             target_segment_duration + extra_duration_needed)
  810.         {
  811.             avio_flush(oc->pb);
  812.             avio_close(oc->pb);

  813.             // Keep track of accumulated rounding error to account for it in later chunks.
  814.             double segment_duration = segment_time - prev_segment_time;
  815.             int rounded_segment_duration = (int)(segment_duration + 0.5);
  816.             extra_duration_needed += (double)rounded_segment_duration - segment_duration;

  817.             updatePlaylist(playlist,
  818.                            playlist_filename,
  819.                            input_args.output,
  820.                            output_index,
  821.                            rounded_segment_duration);
  822.             
  823.             sprintf(input_args.output, "%s-%u.ts", input_args.output_prefix, ++output_index);
  824.             if (avio_open(&oc->pb, input_args.output, AVIO_FLAG_WRITE) < 0) {
  825.                 fprintf(stderr, "Could not open '%s'\n", input_args.output);
  826.                 break;
  827.             }

  828.             // close when we find the 'kill' file
  829.             if (kill_file) {
  830.                 FILE* fp = fopen("kill", "rb");
  831.                 if (fp) {
  832.                     fprintf(stderr, "user abort: found kill file\n");
  833.                     fclose(fp);
  834.                     remove("kill");
  835.                     decode_done = 1;
  836.                     removeAllPackets(streamLace);
  837.                 }
  838.             }
  839.             prev_segment_time = segment_time;
  840.         }

  841.         ret = av_interleaved_write_frame(oc, &packet);
  842.         if (ret < 0) {
  843.             fprintf(stderr, "Warning: Could not write frame of stream\n");
  844.         }
  845.         else if (ret > 0) {
  846.             fprintf(stderr, "End of stream requested\n");
  847.             av_free_packet(&packet);
  848.             break;
  849.         }

  850.         av_free_packet(&packet);
  851.     } while (!decode_done || countPackets(streamLace) > 0);

  852.     av_write_trailer(oc);

  853.     if (video_index >= 0) {
  854.       avcodec_close(video_st->codec);
  855.     }

  856.     for(i = 0; i < oc->nb_streams; i++) {
  857.         av_freep(&oc->streams[i]->codec);
  858.         av_freep(&oc->streams[i]);
  859.     }

  860.     avio_close(oc->pb);
  861.     av_free(oc);

  862.     updatePlaylist(playlist,
  863.                    playlist_filename,
  864.                    input_args.output,
  865.                    output_index,
  866.                    segment_duration);
  867.     closePlaylist(playlist);
  868.     releasePlaylist(&playlist);
  869.     
  870.     if (pid_filename)
  871.     {
  872.         remove(pid_filename);
  873.     }
  874.     return 0;

  875. error:
  876.     if (pid_filename)
  877.     {
  878.         remove(pid_filename);
  879.     }

  880.     return 1;

  881. }

  882. int main(int argc, char ** argv)
  883. {
  884.     return main_utf8(argc, argv);
  885. }


四、 准备webserver, 建议用lighttpd。
下载最新的lighttpd-1.4.32,
./configure –prefix=dir
Make && make install
下面就是配置了:


server.modules必须启用:
mod_access
在mimetype.assign当中加入:
".m3u8"         =>      "application/x-mpegURL", 
  ".ts"           =>      "video/MP2T",
server.document-root 设置为你的目录,记住设置下面:
index-file.names            = ( "index.php", "index.html",
                                "index.htm", "default.htm",
                                "index.lighttpd.html")
打开时发现出现404 not foud,这说明你的server已经成功启动,你要做的就是把/var/www/ index.lighttpd.html 拷贝到你的目录下,你就可以打开这个html了。
五、 设置好网络以后,将index.m3u8和相关文件按照自定义顺序拷贝到server的目录下,然后就可以开始试验了。
六、 打开IOS,在movie player当中输入,播放即可,非常流畅。


阅读(1122) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~