Guest User

Libav mp4 to mpeg-ts remuxing

a guest
Jun 13th, 2013
278
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.36 KB | None | 0 0
  1. /*
  2.  *
  3.  * SAMPLE CODE FOR MP4-to-MPEG-TS CONTAINER CONVERSION
  4.  *
  5.  * This is the basis for a python plugin for doing on-the-fly segmentation of
  6.  *
  7.  *
  8.  */
  9.  
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13.  
  14. #include <libavformat/avformat.h>
  15. #include <libavutil/mathematics.h>
  16.  
  17. typedef struct config_info
  18. {
  19.     const char *input_filename;
  20.     const char *output_playlist;
  21.     const char *output_prefix;
  22.     int segment_length;
  23. } config_info;
  24.  
  25. static AVStream *add_output_stream(AVFormatContext *output_format_context, AVStream *input_stream)
  26. {
  27.     AVCodecContext *input_codec_context;
  28.     AVCodecContext *output_codec_context;
  29.     AVStream *output_stream;
  30.  
  31.     output_stream = avformat_new_stream(output_format_context, NULL);
  32.     if (!output_stream) {
  33.         fprintf(stderr, "Could not allocate stream\n");
  34.         exit(1);
  35.     }
  36.  
  37.     input_codec_context = input_stream->codec;
  38.     output_codec_context = output_stream->codec;
  39.  
  40.     output_codec_context->codec_id = input_codec_context->codec_id;
  41.     output_codec_context->codec_type = input_codec_context->codec_type;
  42.     output_codec_context->codec_tag = input_codec_context->codec_tag;
  43.     output_codec_context->bit_rate = input_codec_context->bit_rate;
  44.     output_codec_context->extradata = input_codec_context->extradata;
  45.     output_codec_context->extradata_size = input_codec_context->extradata_size;
  46.  
  47.     // Some codecs put the wrong ticks_per_frame in (in the thousands instead of tens)
  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.  
  56.     switch (input_codec_context->codec_type) {
  57.         case AVMEDIA_TYPE_AUDIO:
  58.             output_codec_context->channel_layout = input_codec_context->channel_layout;
  59.             output_codec_context->sample_rate = input_codec_context->sample_rate;
  60.             output_codec_context->channels = input_codec_context->channels;
  61.             output_codec_context->frame_size = input_codec_context->frame_size;
  62.             if ((input_codec_context->block_align == 1 && input_codec_context->codec_id == AV_CODEC_ID_MP3) || input_codec_context->codec_id == AV_CODEC_ID_AC3) {
  63.                 output_codec_context->block_align = 0;
  64.             }
  65.             else {
  66.                 output_codec_context->block_align = input_codec_context->block_align;
  67.             }
  68.             fprintf(stdout, "Audio time bases:\n");
  69.             break;
  70.         case AVMEDIA_TYPE_VIDEO:
  71.             output_codec_context->pix_fmt = input_codec_context->pix_fmt;
  72.             output_codec_context->width = input_codec_context->width;
  73.             output_codec_context->height = input_codec_context->height;
  74.             output_codec_context->has_b_frames = input_codec_context->has_b_frames;
  75.  
  76.             if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
  77.                 output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
  78.             }
  79.             fprintf(stdout, "Video time bases:\n");
  80.             break;
  81.         default:
  82.             break;
  83.     }
  84.  
  85.     fprintf(stdout, "- Input Codec: %d\n- Output Codec: %d\n", input_codec_context->time_base, output_codec_context->time_base);
  86.     fprintf(stdout, "- Input Stream: %d\n- Output Stream: %d\n", input_stream->time_base, output_stream->time_base);
  87.  
  88.     return output_stream;
  89. }
  90.  
  91. int main(int argc, char **argv)
  92. {
  93.     if (argc != 5)
  94.     {
  95.         fprintf(stderr, "Usage: %s <input filename> <playlist filename> <output prefix> <segment length>\n", argv[0]);
  96.         exit(1);
  97.     }
  98.  
  99.     config_info config;
  100.     memset(&config, 0, sizeof(config_info));
  101.  
  102.     config.input_filename = argv[1];
  103.     config.output_playlist = argv[2];
  104.     config.output_prefix = argv[3];
  105.     config.segment_length = atoi(argv[4]);
  106.  
  107.     char *output_filename = (char*) malloc(sizeof(char) * (strlen(config.output_prefix) + 10));
  108.     if (!output_filename)
  109.     {
  110.         fprintf(stderr, "Segmenter error: Could not allocate space for output filenames with prefix %s\n", config.output_prefix);
  111.         exit(1);
  112.     }
  113.  
  114.     avcodec_register_all();
  115.     av_register_all();
  116.  
  117.     AVInputFormat *input_format = av_find_input_format("mp4");
  118.     if (!input_format)
  119.     {
  120.         fprintf(stderr, "Segmenter error: Could not find MP4 demuxer.\n");
  121.         exit(1);
  122.     }
  123.  
  124.     AVFormatContext *input_context = NULL;
  125.     int ret = avformat_open_input(&input_context, config.input_filename, input_format, NULL);
  126.     if (ret != 0)
  127.     {
  128.         fprintf(stderr, "Segmenter error: Could not open input file, make sure it is an MP4 file: %d\n", ret);
  129.         exit(1);
  130.     }
  131.  
  132.     if (avformat_find_stream_info(input_context, NULL) < 0)
  133.     {
  134.         fprintf(stderr, "Segmenter error Could not read stream information.\n");
  135.         exit(1);
  136.     }
  137.  
  138.     AVOutputFormat *output_format = av_guess_format("mpegts", NULL, NULL);
  139.     if (!output_format)
  140.     {
  141.         fprintf(stderr, "Segmenter error: Could not find MPEG-TS muxer.\n");
  142.         exit(1);
  143.     }
  144.  
  145.     AVFormatContext *output_context = avformat_alloc_context();
  146.     if (!output_context)
  147.     {
  148.         fprintf(stderr, "Segmenter error: Could not allocate output context.\n");
  149.         exit(1);
  150.     }
  151.     output_context->oformat = output_format;
  152.  
  153.     int video_index = -1;
  154.     int audio_index = -1;
  155.  
  156.     AVStream *video_stream;
  157.     AVStream *audio_stream;
  158.  
  159.     int i;
  160.  
  161.     for (i = 0; i < input_context->nb_streams && (video_index < 0 || audio_index < 0); i++)
  162.     {
  163.         switch (input_context->streams[i]->codec->codec_type)
  164.         {
  165.             case AVMEDIA_TYPE_VIDEO:
  166.                 video_index = i;
  167.                 input_context->streams[i]->discard = AVDISCARD_NONE;
  168.                 video_stream = add_output_stream(output_context, input_context->streams[i]);
  169.                 break;
  170.  
  171.             case AVMEDIA_TYPE_AUDIO:
  172.                 audio_index = i;
  173.                 input_context->streams[i]->discard = AVDISCARD_NONE;
  174.                 audio_stream = add_output_stream(output_context, input_context->streams[i]);
  175.                 break;
  176.  
  177.             default:
  178.                 input_context->streams[i]->discard = AVDISCARD_ALL;
  179.                 break;
  180.         }
  181.     }
  182.  
  183.     if (video_index == -1 || audio_index == -1)
  184.     {
  185.         fprintf(stderr, "Segmenter error: Video or audio stream not found.\n");
  186.         exit(1);
  187.     }
  188.  
  189.     av_dump_format(input_context, 0, config.input_filename, 0);
  190.     av_dump_format(output_context, 0, config.output_prefix, 1);
  191.  
  192.     AVCodec *codec = avcodec_find_decoder(video_stream->codec->codec_id);
  193.     if (!codec)
  194.     {
  195.         fprintf(stderr, "Segmenter error: Could not find video decoder, key frames will not be honored.\n");
  196.     }
  197.  
  198.     if (avcodec_open2(video_stream->codec, codec, NULL) < 0)
  199.     {
  200.         fprintf(stderr, "Segmenter error: Could not open video decoder, key frames will not be honored.\n");
  201.     }
  202.  
  203.     AVCodec *codec2 = avcodec_find_decoder(audio_stream->codec->codec_id);
  204.     if (!codec2)
  205.     {
  206.         fprintf(stderr, "Segmenter error: Could not find audio decoder, key frames will not be honored.\n");
  207.     }
  208.  
  209.     if (avcodec_open2(audio_stream->codec, codec2, NULL) < 0)
  210.     {
  211.         fprintf(stderr, "Segmenter error: Could not open audio decoder, key frames will not be honored.\n");
  212.     }
  213.  
  214.     // Generate output filename
  215.     snprintf(output_filename, strlen(config.output_prefix) + 11, "%s_result.ts", config.output_prefix);
  216.  
  217.     if (avio_open(&output_context->pb, output_filename, AVIO_FLAG_WRITE) < 0)
  218.     {
  219.         fprintf(stderr, "Segmenter error: Could not open '%s'\n", output_filename);
  220.         exit(1);
  221.     }
  222.  
  223.     if (avformat_write_header(output_context, NULL))
  224.     {
  225.         fprintf(stderr, "Segmenter error: Could not write mpegts header to first output file.\n");
  226.         exit(1);
  227.     }
  228.  
  229.     // Required when muxing h264 into MPEG-TS
  230.     AVBitStreamFilterContext *bsf = av_bitstream_filter_init("h264_mp4toannexb");
  231.     if (!bsf)
  232.     {
  233.         fprintf(stderr, "Error initializing the h264_mp4toannexb filter!");
  234.         exit(1);
  235.     }
  236.  
  237.     int decode_done;
  238.     do
  239.     {
  240.         AVPacket packet;
  241.         av_init_packet(&packet);
  242.         decode_done = av_read_frame(input_context, &packet);
  243.         if (decode_done < 0)
  244.         {
  245.             break;
  246.         }
  247.  
  248.         if (av_dup_packet(&packet) < 0)
  249.         {
  250.             fprintf(stderr, "Segmenter error: Could not duplicate packet (av_dup_packet).\n");
  251.             av_free_packet(&packet);
  252.             break;
  253.         }
  254.  
  255.         if (packet.stream_index == video_index)
  256.         {
  257.             int a = av_bitstream_filter_filter(bsf, video_stream->codec, NULL,
  258.                                                &packet.data, &packet.size,
  259.                                                packet.data, packet.size,
  260.                                                packet.flags & AV_PKT_FLAG_KEY);
  261.  
  262.             if (a == 0)
  263.             {
  264.                 fprintf(stderr, "Eh?!\n");
  265.             }
  266.             else if (a < 0)
  267.             {
  268.                 fprintf(stderr, "%s failed for stream %d, codec %s, error %d\n", bsf->filter->name, packet.stream_index, video_stream->codec->codec->name, a);
  269.                 exit(1);
  270.             }
  271.         }
  272.  
  273.         // Correct time base stuff
  274.         // TOOODOOOOO!
  275.  
  276.         ret = av_interleaved_write_frame(output_context, &packet);
  277.         if (ret < 0)
  278.         {
  279.             fprintf(stderr, "Segmenter error: Could not write frame of stream: %d\n", ret);
  280.             exit(1);
  281.         }
  282.         else if (ret > 0)
  283.         {
  284.             fprintf(stderr, "Segmenter info: End of stream requested.\n");
  285.             av_free_packet(&packet);
  286.             break;
  287.         }
  288.  
  289.         av_free_packet(&packet);
  290.     } while (!decode_done);
  291.  
  292.     av_write_trailer(output_context);
  293.     avcodec_close(video_stream->codec);
  294.  
  295.     for (i = 0; i < output_context->nb_streams; i++)
  296.     {
  297.         av_freep(&output_context->streams[i]->codec);
  298.         av_freep(&output_context->streams[i]);
  299.     }
  300.  
  301.     av_bitstream_filter_close(bsf);
  302.  
  303.     avio_close(output_context->pb);
  304.     av_free(output_context);
  305.  
  306.     fprintf(stdout, "SUCCESS!!!\n");
  307.     exit(0);
  308. }
Advertisement
Add Comment
Please, Sign In to add comment