Advertisement
Guest User

Untitled

a guest
Oct 20th, 2014
213
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.99 KB | None | 0 0
  1. // ConsoleApplication1.cpp : main project file.
  2.  
  3. #include "stdafx.h"
  4.  
  5. using namespace System;
  6. using namespace System::Runtime::InteropServices;
  7.  
  8. #define OUTPUT_BIT_RATE 16000
  9. /** The number of output channels */
  10. #define OUTPUT_CHANNELS 1
  11. /** The audio sample output format */
  12. #define OUTPUT_SAMPLE_FORMAT AV_SAMPLE_FMT_S16P
  13.  
  14. const char *get_error_text(const int error)
  15. {
  16.     static char error_buffer[255];
  17.     av_strerror(error, error_buffer, sizeof(error_buffer));
  18.     return error_buffer;
  19. }
  20.  
  21. // open an input file and load the required decoder
  22. int open_input_file(const char *filename, AVFormatContext **input_format_context, AVCodecContext **input_codec_context)
  23. {
  24.     AVCodec *input_codec;
  25.     int error;
  26.  
  27.     // open the input file and read to buffer
  28.     if ((error = avformat_open_input(input_format_context, filename, NULL, NULL)) < 0)
  29.     {
  30.         fprintf(stderr, "Could not open input file '%s' (error '$s')\n",
  31.             filename, get_error_text(error));
  32.         *input_format_context = NULL;
  33.         return error;
  34.     }
  35.  
  36.     // get the input audio info
  37.     if ((error = avformat_find_stream_info(*input_format_context, NULL)) < 0)
  38.     {
  39.         fprintf(stderr, "Could not determine audio info (error '$s')\n",
  40.             get_error_text(error));
  41.         avformat_close_input(input_format_context);
  42.         return error;
  43.     }
  44.  
  45.     // make sure there's only one audio stream
  46.     if ((*input_format_context)->nb_streams != 1)
  47.     {
  48.         fprintf(stderr, "Expected a single audio input stream, but found %d\n",
  49.             (*input_format_context)->nb_streams);
  50.  
  51.         avformat_close_input(input_format_context);
  52.         return AVERROR_EXIT;
  53.     }
  54.  
  55.     // find the audio decoder
  56.     if (!(input_codec = avcodec_find_decoder((*input_format_context)->streams[0]->codec->codec_id)))
  57.     {
  58.         fprintf(stderr, "Could not find input codec\n");
  59.         avformat_close_input(input_format_context);
  60.         return AVERROR_EXIT;
  61.     }
  62.  
  63.     // open the decoder for the audio stream
  64.     if ((error = avcodec_open2((*input_format_context)->streams[0]->codec, input_codec, NULL)) < 0)
  65.     {
  66.         fprintf(stderr, "Could not open input codec (error '%s')\n",
  67.             get_error_text(error));
  68.         avformat_close_input(input_format_context);
  69.         return error;
  70.     }
  71.  
  72.     // save the decoder context for use later
  73.     *input_codec_context = (*input_format_context)->streams[0]->codec;
  74.  
  75.     return 0;
  76. }
  77.  
  78. // open the output file and encoder
  79. int open_output_file(const char *filename,
  80.     AVCodecContext *input_codec_context,
  81.     AVFormatContext **output_format_context,
  82.     AVCodecContext **output_codec_context)
  83. {
  84.     AVIOContext *output_io_context = NULL;
  85.     AVStream *stream = NULL;
  86.     AVCodec *output_codec = NULL;
  87.     int error;
  88.  
  89.     // open output file
  90.     if ((error = avio_open(&output_io_context, filename, AVIO_FLAG_WRITE)) < 0)
  91.     {
  92.         fprintf(stderr, "Could not open output file '%s' (error '%s')\n",
  93.             filename, get_error_text(error));
  94.         return error;
  95.     }
  96.  
  97.     // create new output format context
  98.     if (!(*output_format_context = avformat_alloc_context()))
  99.     {
  100.         fprintf(stderr, "Could not allocate output format context\n");
  101.         return AVERROR(ENOMEM);
  102.     }
  103.  
  104.     // Associate the output file with the container format context
  105.     (*output_format_context)->pb = output_io_context;
  106.  
  107.     // guess the format based on file extension
  108.     if (!((*output_format_context)->oformat = av_guess_format(NULL, filename, NULL)))
  109.     {
  110.         fprintf(stderr, "Could not find output file format\n");
  111.         goto cleanup;
  112.     }
  113.  
  114.     av_strlcpy((*output_format_context)->filename, filename,
  115.         sizeof((*output_format_context)->filename));
  116.  
  117.     // find the required encoder
  118.     if (!(output_codec = avcodec_find_encoder(AV_CODEC_ID_AAC)))
  119.     {
  120.         fprintf(stderr, "Could not find an AAC encoder.\n");
  121.         goto cleanup;
  122.     }
  123.  
  124.     if (!(stream = avformat_new_stream(*output_format_context, output_codec)))
  125.     {
  126.         fprintf(stderr, "Could not create new stream\n");
  127.         error = AVERROR(ENOMEM);
  128.         goto cleanup;
  129.     }
  130.  
  131.     // save encoder for easier access later.
  132.     *output_codec_context = stream->codec;
  133.  
  134.     // set encoder parameters
  135.     (*output_codec_context)->channels = OUTPUT_CHANNELS;
  136.     (*output_codec_context)->channel_layout = av_get_default_channel_layout(OUTPUT_CHANNELS);
  137.     (*output_codec_context)->sample_rate = 16000;
  138.     (*output_codec_context)->sample_fmt = AV_SAMPLE_FMT_S16P;
  139.     (*output_codec_context)->bit_rate = OUTPUT_BIT_RATE;
  140.  
  141.     /**
  142.     * Some container formats (like MP4) require global headers to be present
  143.     * Mark the encoder so that it behaves accordingly.
  144.     */
  145.     if ((*output_format_context)->oformat->flags & AVFMT_GLOBALHEADER)
  146.         (*output_codec_context)->flags |= CODEC_FLAG_GLOBAL_HEADER;
  147.  
  148.     // open the encoder for the audio stream
  149.     if ((error = avcodec_open2(*output_codec_context, output_codec, NULL)) < 0)
  150.     {
  151.         fprintf(stderr, "Could not open output codec (error '%s')\n",
  152.             get_error_text(error));
  153.         goto cleanup;
  154.     }
  155.  
  156.     return 0;
  157.  
  158. cleanup:
  159.     avio_close((*output_format_context)->pb);
  160.     avformat_free_context(*output_format_context);
  161.     *output_format_context = NULL;
  162.     return error < 0 ? error : AVERROR_EXIT;
  163. }
  164.  
  165. // init data packet for reading or writing
  166. void init_packet(AVPacket *packet)
  167. {
  168.     av_init_packet(packet);
  169.     // set the packet data and size so that it's seen as empty
  170.     packet->data = NULL;
  171.     packet->size = 0;
  172. }
  173.  
  174. int init_input_frame(AVFrame **frame)
  175. {
  176.     if (!(*frame = av_frame_alloc()))
  177.     {
  178.         fprintf(stderr, "Could not allocate input frame\n");
  179.         return AVERROR(ENOMEM);
  180.     }
  181.     return 0;
  182. }
  183.  
  184. // init audio resampler based on input and output codecs
  185. int init_resampler(AVCodecContext *input_codec_context,
  186.     AVCodecContext *output_codec_context,
  187.     SwrContext **resample_context)
  188. {
  189.     int error;
  190.  
  191.     /**
  192.     * Create a resampler context for the conversion.
  193.     * Set the conversion parameters.
  194.     * Default channel layouts based on the number of channels
  195.     * are assumed for simplicity (they are sometimes not detected
  196.     * properly by the demuxer and/or decoder).
  197.     */
  198.     *resample_context = swr_alloc_set_opts(NULL,
  199.         av_get_default_channel_layout(output_codec_context->channels),
  200.         output_codec_context->sample_fmt,
  201.         output_codec_context->sample_rate,
  202.         av_get_default_channel_layout(input_codec_context->channels),
  203.         input_codec_context->sample_fmt,
  204.         input_codec_context->sample_rate,
  205.         0, NULL);
  206.  
  207.     if (!*resample_context)
  208.     {
  209.         fprintf(stderr, "Could not allocate resample context\n");
  210.         return AVERROR(ENOMEM);
  211.     }
  212.  
  213.     // sanity check
  214.     //av_assert0(output_codec_context->sample_rate == input_codec_context->sample_rate);
  215.  
  216.     // open resampler with parms
  217.     if ((error = swr_init(*resample_context)) < 0)
  218.     {
  219.         fprintf(stderr, "Could not open resample context\n");
  220.         swr_free(resample_context);
  221.         return error;
  222.     }
  223.     return 0;
  224. }
  225.  
  226. // init FIFO buffer for encoding samples
  227. int init_fifo(AVAudioFifo **fifo)
  228. {
  229.     if (!(*fifo = av_audio_fifo_alloc(OUTPUT_SAMPLE_FORMAT, OUTPUT_CHANNELS, 1)))
  230.     {
  231.         fprintf(stderr, "Could not allocate FIFO\n");
  232.         return AVERROR(ENOMEM);
  233.     }
  234.     return 0;
  235. }
  236.  
  237. // write output file container header
  238. int write_output_file_header(AVFormatContext *output_format_context)
  239. {
  240.     int error;
  241.     if ((error = avformat_write_header(output_format_context, NULL)) < 0)
  242.     {
  243.         fprintf(stderr, "Could not write output file header (error '%s')\n",
  244.             get_error_text(error));
  245.         return error;
  246.     }
  247.     return 0;
  248. }
  249.  
  250. // decode one audio frame
  251. int decode_audio_frame(AVFrame *frame,
  252.     AVFormatContext *input_format_context,
  253.     AVCodecContext *input_codec_context,
  254.     int *data_present, int *finished)
  255. {
  256.     // temp storage
  257.     AVPacket input_packet;
  258.     int error;
  259.     init_packet(&input_packet);
  260.  
  261.     // read one frame from the input to the temp packet
  262.     if ((error = av_read_frame(input_format_context, &input_packet)) < 0)
  263.     {
  264.         // if we hit the EOF, flush the decoder
  265.         if (error == AVERROR_EOF)
  266.             *finished = 1;
  267.         else
  268.         {
  269.             fprintf(stderr, "Could not read frame (error '%s')\n",
  270.                 get_error_text(error));
  271.             return error;
  272.         }
  273.     }
  274.  
  275.     // decode the audio frame
  276.     if ((error = avcodec_decode_audio4(input_codec_context, frame,
  277.         data_present, &input_packet)) < 0)
  278.     {
  279.         fprintf(stderr, "Could not decode frame (error '%s')\n",
  280.             get_error_text(error));
  281.         av_free_packet(&input_packet);
  282.         return error;
  283.     }
  284.  
  285.     // if the decoder isn't completely flushed, we have to flush it again
  286.     if (*finished && *data_present)
  287.         *finished = 0;
  288.     av_free_packet(&input_packet);
  289.     return 0;
  290. }
  291.  
  292. // init temp storage for the audio samples
  293. int init_converted_samples(uint8_t ***converted_input_samples,
  294.     AVCodecContext *output_codec_context,
  295.     int frame_size)
  296. {
  297.     int error;
  298.  
  299.     // allocate as many pointers as there are audio channels
  300.     if (!(*converted_input_samples = (uint8_t**)calloc(output_codec_context->channels,
  301.         sizeof(**converted_input_samples))))
  302.     {
  303.         fprintf(stderr, "could not allocate converted input sample pointers\n");
  304.         return AVERROR(ENOMEM);
  305.     }
  306.  
  307.     // allocate memory for the samples
  308.     if ((error = av_samples_alloc(*converted_input_samples, NULL,
  309.         output_codec_context->channels,
  310.         frame_size,
  311.         output_codec_context->sample_fmt, 0)) < 0)
  312.     {
  313.         fprintf(stderr, "Could not allocate converted input samples (error '%s')\n",
  314.             get_error_text(error));
  315.         av_freep(&(*converted_input_samples)[0]);
  316.         free(*converted_input_samples);
  317.         return error;
  318.     }
  319.     return 0;
  320. }
  321.  
  322. // convert the input audio samples into the output format
  323. int convert_samples(const uint8_t **input_data,
  324.     uint8_t **converted_data, const int output_size, const int frame_size,
  325.     SwrContext *resample_context)
  326. {
  327.     int error;
  328.  
  329.     // convert samples
  330.     if ((error = swr_convert(resample_context,
  331.         converted_data, output_size,
  332.         input_data, frame_size)) < 0)
  333.     {
  334.         fprintf(stderr, "Could not convert input samples (error '%s')\n",
  335.             get_error_text(error));
  336.         return error;
  337.     }
  338.  
  339.     return 0;
  340. }
  341.  
  342. // Add converted input samples to the FIFO buffer
  343. int add_samples_to_fifo(AVAudioFifo *fifo,
  344.     uint8_t **converted_input_samples,
  345.     const int frame_size)
  346. {
  347.     int error;
  348.  
  349.     // make FIFO the size it needs to hold both old and new samples
  350.     if ((error = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + frame_size)) < 0)
  351.     {
  352.         fprintf(stderr, "Could not reallocate FIFO\n");
  353.         return error;
  354.     }
  355.  
  356.     // store the new samples in the FIFO buffer
  357.     if (av_audio_fifo_write(fifo, (void **)converted_input_samples,
  358.         frame_size) < frame_size)
  359.     {
  360.         fprintf(stderr, "Could not write data to FIFO\n");
  361.         return AVERROR_EXIT;
  362.     }
  363.     return 0;
  364. }
  365.  
  366. // read one audio frame from the input file, decode, convert and store it in the FIFO buffer
  367. int read_decode_convert_and_store(AVAudioFifo *fifo,
  368.     AVFormatContext *input_format_context,
  369.     AVCodecContext *input_codec_context,
  370.     AVCodecContext *output_codec_context,
  371.     SwrContext *resampler_context,
  372.     int *finished)
  373. {
  374.     // temp storage for input frames
  375.     AVFrame *input_frame = NULL;
  376.     // temp storage for input samples
  377.     uint8_t **converted_input_samples = NULL;
  378.     int data_present;
  379.     int ret = AVERROR_EXIT;
  380.  
  381.     // init temp storage for one frame
  382.     if (init_input_frame(&input_frame))
  383.         goto cleanup;
  384.  
  385.     // decode one frame
  386.     if (decode_audio_frame(input_frame, input_format_context,
  387.         input_codec_context, &data_present, finished))
  388.         goto cleanup;
  389.  
  390.     /**
  391.     * If we are at the end of the file and there are no more samples
  392.     * in the decoder which are delayed, we are actually finished.
  393.     * This must not be treated as an error.
  394.     */
  395.     if (*finished && !data_present)
  396.     {
  397.         ret = 0;
  398.         goto cleanup;
  399.     }
  400.     int max_samples = av_rescale_rnd(input_frame->nb_samples, output_codec_context->sample_rate, input_codec_context->sample_rate, AV_ROUND_UP);
  401.     // if there's decoded data, convert and store
  402.     if (data_present)
  403.     {
  404.         int out_samples = av_rescale_rnd(swr_get_delay(resampler_context, input_codec_context->sample_rate) + input_frame->nb_samples,
  405.             output_codec_context->sample_rate, input_codec_context->sample_rate, AV_ROUND_UP);
  406.  
  407.         // init the temp storage for the samples
  408.         if (init_converted_samples(&converted_input_samples, output_codec_context,
  409.             out_samples))
  410.             goto cleanup;
  411.  
  412.         // convert samples
  413.         if (convert_samples((const uint8_t**)input_frame->extended_data, converted_input_samples, out_samples,
  414.             input_frame->nb_samples, resampler_context))
  415.             goto cleanup;
  416.        
  417.         // add the samples to the FIFO buffer
  418.         if (add_samples_to_fifo(fifo, converted_input_samples,
  419.             out_samples))
  420.             goto cleanup;
  421.         ret = 0;
  422.     }
  423.     ret = 0;
  424.  
  425. cleanup:
  426.     if (converted_input_samples)
  427.     {
  428.         av_freep(&converted_input_samples[0]);
  429.         free(converted_input_samples);
  430.     }
  431.     av_frame_free(&input_frame);
  432.     return ret;
  433. }
  434.  
  435. // init frame for writing
  436. int init_output_frame(AVFrame **frame,
  437.     AVCodecContext *output_codec_context,
  438.     int frame_size)
  439. {
  440.     int error;
  441.  
  442.     // create a new frame to store samples
  443.     if (!(*frame = av_frame_alloc()))
  444.     {
  445.         fprintf(stderr, "Could not allocate output frame\n");
  446.         return AVERROR_EXIT;
  447.     }
  448.  
  449.     /**
  450.     * Set the frame's parameters, especially its size and format.
  451.     * av_frame_get_buffer needs this to allocate memory for the
  452.     * audio samples of the frame.
  453.     * Default channel layouts based on the number of channels
  454.     * are assumed for simplicity.
  455.     */
  456.     (*frame)->nb_samples = frame_size;
  457.     (*frame)->channel_layout = output_codec_context->channel_layout;
  458.     (*frame)->format = output_codec_context->sample_fmt;
  459.     (*frame)->sample_rate = output_codec_context->sample_rate;
  460.  
  461.     // allocate the samples of the frame
  462.     if ((error = av_frame_get_buffer(*frame, 0)) < 0)
  463.     {
  464.         fprintf(stderr, "Couldn't allocate output frame samples (error '%s')\n",
  465.             get_error_text(error));
  466.         av_frame_free(frame);
  467.         return error;
  468.     }
  469.  
  470.     return 0;
  471. }
  472.  
  473. // encode one frame of audio to the output file
  474. int encode_audio_frame(AVFrame *frame,
  475.     AVFormatContext *output_format_context,
  476.     AVCodecContext *output_codec_context,
  477.     int *data_present)
  478. {
  479.     // packet for temp storage
  480.     AVPacket output_packet;
  481.     int error;
  482.     init_packet(&output_packet);
  483.    
  484.     // encode the audio frame and store it in a packet
  485.     if ((error = avcodec_encode_audio2(output_codec_context, &output_packet,
  486.         frame, data_present)) < 0)
  487.     {
  488.         fprintf(stderr, "Could not encode frame (error '%s')\n",
  489.             get_error_text(error));
  490.         av_free_packet(&output_packet);
  491.         return error;
  492.     }
  493.  
  494.     // write one audio frame to the output file
  495.     if (*data_present)
  496.     {
  497.         if ((error = av_write_frame(output_format_context, &output_packet)) < 0)
  498.         {
  499.             fprintf(stderr, "Could not write frame (error '%s'\n",
  500.                 get_error_text(error));
  501.             av_free_packet(&output_packet);
  502.             return error;
  503.         }
  504.  
  505.         av_free_packet(&output_packet);
  506.     }
  507.     return 0;
  508. }
  509.  
  510. // load a frame from the FIFO buffer, encode and then write it to the output
  511. int load_encode_and_write(AVAudioFifo *fifo,
  512.     AVFormatContext *output_format_context,
  513.     AVCodecContext *output_codec_context)
  514. {
  515.     AVFrame *output_frame;
  516.     const int frame_size = FFMIN(av_audio_fifo_size(fifo), output_codec_context->frame_size);
  517.  
  518.     int data_written;
  519.  
  520.     // init temp storage for the frame
  521.     if (init_output_frame(&output_frame, output_codec_context, frame_size))
  522.         return AVERROR_EXIT;
  523.  
  524.     // read the samples required to fill the frame
  525.     if (av_audio_fifo_read(fifo, (void **)output_frame->data, frame_size) < frame_size)
  526.     {
  527.         fprintf(stderr, "Could not read data from FIFO\n");
  528.         av_frame_free(&output_frame);
  529.         return AVERROR_EXIT;
  530.     }
  531.  
  532.     // Encode one frame worth of audio samples
  533.     if (encode_audio_frame(output_frame, output_format_context,
  534.         output_codec_context, &data_written))
  535.     {
  536.         av_frame_free(&output_frame);
  537.         return AVERROR_EXIT;
  538.     }
  539.  
  540.     av_frame_free(&output_frame);
  541.     return 0;
  542. }
  543.  
  544. // write the trailer of the output file container
  545. int write_output_file_trailer(AVFormatContext *output_format_context)
  546. {
  547.     int error;
  548.     if ((error = av_write_trailer(output_format_context)) < 0)
  549.     {
  550.         fprintf(stderr, "Could not write output file trailer (error '%s')\n",
  551.             get_error_text(error));
  552.         return error;
  553.     }
  554.     return 0;
  555. }
  556.  
  557. // convert an audio file to AAC in a MP4 container
  558. int ConvertFile(String^ inputFile, String^ outputFile)
  559. {
  560.     AVFormatContext *input_format_context = NULL, *output_format_context = NULL;
  561.     AVCodecContext *input_codec_context = NULL, *output_codec_context = NULL;
  562.     SwrContext *resample_context = NULL;
  563.     AVAudioFifo *fifo = NULL;
  564.     int ret = AVERROR_EXIT;
  565.  
  566.     av_register_all();
  567.     IntPtr ptrInput = Marshal::StringToHGlobalAnsi(inputFile);
  568.     char* nativeInput = static_cast<char*>(ptrInput.ToPointer());
  569.  
  570.     IntPtr ptrOutput = Marshal::StringToHGlobalAnsi(outputFile);
  571.     char* nativeOutput = static_cast<char*>(ptrOutput.ToPointer());
  572.  
  573.     if (open_input_file(nativeInput, &input_format_context, &input_codec_context))
  574.         goto cleanup;
  575.     if (open_output_file(nativeOutput, input_codec_context, &output_format_context, &output_codec_context))
  576.         goto cleanup;
  577.  
  578.     if (init_resampler(input_codec_context, output_codec_context, &resample_context))
  579.         goto cleanup;
  580.  
  581.     if (init_fifo(&fifo))
  582.         goto cleanup;
  583.  
  584.     if (write_output_file_header(output_format_context))
  585.         goto cleanup;
  586.  
  587.     // loop until we run out of samples
  588.     while (1)
  589.     {
  590.         const int output_frame_size = output_codec_context->frame_size;
  591.         int finished = 0;
  592.  
  593.         while (av_audio_fifo_size(fifo) < output_frame_size)
  594.         {
  595.             if (read_decode_convert_and_store(fifo, input_format_context,
  596.                 input_codec_context, output_codec_context, resample_context, &finished))
  597.                 goto cleanup;
  598.  
  599.             if (finished)
  600.                 break;
  601.         }
  602.  
  603.         while (av_audio_fifo_size(fifo) >= output_frame_size || (finished && av_audio_fifo_size(fifo) > 0))
  604.             if (load_encode_and_write(fifo, output_format_context, output_codec_context))
  605.                 goto cleanup;
  606.  
  607.         if (finished)
  608.         {
  609.             int data_written;
  610.             do
  611.             {
  612.                 if (encode_audio_frame(NULL, output_format_context, output_codec_context, &data_written))
  613.                     goto cleanup;
  614.             } while (data_written);
  615.             break;
  616.         }
  617.     }
  618.  
  619.     if (write_output_file_trailer(output_format_context))
  620.         goto cleanup;
  621.     ret = 0;
  622.  
  623. cleanup:
  624.     if (fifo)
  625.         av_audio_fifo_free(fifo);
  626.     swr_free(&resample_context);
  627.  
  628.     if (output_codec_context)
  629.         avcodec_close(output_codec_context);
  630.     if (output_format_context)
  631.     {
  632.         avio_close(output_format_context->pb);
  633.         avformat_free_context(output_format_context);
  634.     }
  635.     if (input_codec_context)
  636.         avcodec_close(input_codec_context);
  637.     if (input_format_context)
  638.         avformat_close_input(&input_format_context);
  639.  
  640.     return ret;
  641. }
  642.  
  643. int main(array<System::String ^> ^args)
  644. {
  645.     ConvertFile(args[0], args[1]);
  646. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement