| 1 | extern "C"
|
|---|
| 2 | {
|
|---|
| 3 | #include <libavformat/avformat.h>
|
|---|
| 4 | #include <libavcodec/avcodec.h>
|
|---|
| 5 | #include <libavfilter/avfilter.h>
|
|---|
| 6 | #include <libavutil/opt.h>
|
|---|
| 7 | #include <libavfilter/buffersink.h>
|
|---|
| 8 | #include <libavfilter/buffersrc.h>
|
|---|
| 9 | }
|
|---|
| 10 |
|
|---|
| 11 | #include <iostream>
|
|---|
| 12 | #include <string>
|
|---|
| 13 | #include <stdio.h>
|
|---|
| 14 | #include <stdarg.h>
|
|---|
| 15 | #include <stdlib.h>
|
|---|
| 16 | #include "define.h"
|
|---|
| 17 | #include <algorithm>
|
|---|
| 18 |
|
|---|
| 19 | char *getCmdOption(char **begin, char **end, const std::string &option)
|
|---|
| 20 | {
|
|---|
| 21 | char **itr = std::find(begin, end, option);
|
|---|
| 22 | if (itr != end && ++itr != end)
|
|---|
| 23 | {
|
|---|
| 24 | return *itr;
|
|---|
| 25 | }
|
|---|
| 26 | return 0;
|
|---|
| 27 | }
|
|---|
| 28 |
|
|---|
| 29 | static int init_filter_graph(AVFilterGraph *graph, AVFilterContext **src,
|
|---|
| 30 | AVFilterContext **sink, AVCodecContext *encctx, AVCodecContext *decctx)
|
|---|
| 31 | {
|
|---|
| 32 | AVFilterGraph *filter_graph;
|
|---|
| 33 | AVFilterContext *abuffer_ctx;
|
|---|
| 34 | const AVFilter *abuffer;
|
|---|
| 35 | AVFilterContext *abuffersink_ctx;
|
|---|
| 36 | const AVFilter *abuffersink;
|
|---|
| 37 | const AVFilter *aformat;
|
|---|
| 38 | AVFilterContext *aformat_ctx;
|
|---|
| 39 | char strbuf[1024];
|
|---|
| 40 |
|
|---|
| 41 | AVDictionary *options_dict = NULL;
|
|---|
| 42 | char ch_layout[64];
|
|---|
| 43 |
|
|---|
| 44 | int err;
|
|---|
| 45 |
|
|---|
| 46 | /* Create a new filtergraph, which will contain all the filters. */
|
|---|
| 47 | filter_graph = avfilter_graph_alloc();
|
|---|
| 48 | if (!filter_graph)
|
|---|
| 49 | {
|
|---|
| 50 | fprintf(stderr, "Unable to create filter graph.\n");
|
|---|
| 51 | return AVERROR(ENOMEM);
|
|---|
| 52 | }
|
|---|
| 53 |
|
|---|
| 54 | /* Create the abuffer filter;
|
|---|
| 55 | * it will be used for feeding the data into the graph. */
|
|---|
| 56 | abuffer = avfilter_get_by_name("abuffer");
|
|---|
| 57 | if (!abuffer)
|
|---|
| 58 | {
|
|---|
| 59 | fprintf(stderr, "Could not find the abuffer filter.\n");
|
|---|
| 60 | return AVERROR_FILTER_NOT_FOUND;
|
|---|
| 61 | }
|
|---|
| 62 |
|
|---|
| 63 | abuffer_ctx = avfilter_graph_alloc_filter(filter_graph, abuffer, "src");
|
|---|
| 64 | if (!abuffer_ctx)
|
|---|
| 65 | {
|
|---|
| 66 | fprintf(stderr, "Could not allocate the abuffer instance.\n");
|
|---|
| 67 | return AVERROR(ENOMEM);
|
|---|
| 68 | }
|
|---|
| 69 |
|
|---|
| 70 | /* Set the filter options through the AVOptions API. */
|
|---|
| 71 | av_channel_layout_describe(&decctx->ch_layout, ch_layout, sizeof(ch_layout));
|
|---|
| 72 | av_opt_set(abuffer_ctx, "channel_layout", ch_layout, AV_OPT_SEARCH_CHILDREN);
|
|---|
| 73 | av_opt_set(abuffer_ctx, "sample_fmt", av_get_sample_fmt_name(decctx->sample_fmt), AV_OPT_SEARCH_CHILDREN);
|
|---|
| 74 | av_opt_set_q(abuffer_ctx, "time_base", (AVRational){1, decctx->sample_rate}, AV_OPT_SEARCH_CHILDREN);
|
|---|
| 75 | av_opt_set_int(abuffer_ctx, "sample_rate", decctx->sample_rate, AV_OPT_SEARCH_CHILDREN);
|
|---|
| 76 |
|
|---|
| 77 | /* Now initialize the filter; we pass NULL options, since we have already
|
|---|
| 78 | * set all the options above. */
|
|---|
| 79 | err = avfilter_init_str(abuffer_ctx, NULL);
|
|---|
| 80 | if (err < 0)
|
|---|
| 81 | {
|
|---|
| 82 | fprintf(stderr, "Could not initialize the abuffer filter.\n");
|
|---|
| 83 | return err;
|
|---|
| 84 | }
|
|---|
| 85 |
|
|---|
| 86 | // create aformat filter
|
|---|
| 87 | aformat = avfilter_get_by_name("aformat");
|
|---|
| 88 | snprintf(strbuf, sizeof(strbuf),
|
|---|
| 89 | "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
|
|---|
| 90 | av_get_sample_fmt_name(encctx->sample_fmt), 44100,
|
|---|
| 91 | (uint64_t)AV_CH_LAYOUT_STEREO);
|
|---|
| 92 | fprintf(stderr, "aformat: %s\n", strbuf);
|
|---|
| 93 | aformat_ctx = avfilter_graph_alloc_filter(filter_graph, aformat, "aformat");
|
|---|
| 94 | if (err < 0)
|
|---|
| 95 | {
|
|---|
| 96 | av_log(NULL, AV_LOG_ERROR, "unable to create aformat filter\n");
|
|---|
| 97 | return err;
|
|---|
| 98 | }
|
|---|
| 99 | err = avfilter_init_str(aformat_ctx, strbuf);
|
|---|
| 100 | if (err < 0)
|
|---|
| 101 | {
|
|---|
| 102 | fprintf(stderr, "Could not initialize the aformat instance.\n");
|
|---|
| 103 | return err;
|
|---|
| 104 | }
|
|---|
| 105 |
|
|---|
| 106 | /* Finally create the abuffersink filter;
|
|---|
| 107 | * it will be used to get the filtered data out of the graph. */
|
|---|
| 108 | abuffersink = avfilter_get_by_name("abuffersink");
|
|---|
| 109 | if (!abuffersink)
|
|---|
| 110 | {
|
|---|
| 111 | fprintf(stderr, "Could not find the abuffersink filter.\n");
|
|---|
| 112 | return AVERROR_FILTER_NOT_FOUND;
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | abuffersink_ctx = avfilter_graph_alloc_filter(filter_graph, abuffersink, "sink");
|
|---|
| 116 | if (!abuffersink_ctx)
|
|---|
| 117 | {
|
|---|
| 118 | fprintf(stderr, "Could not allocate the abuffersink instance.\n");
|
|---|
| 119 | return AVERROR(ENOMEM);
|
|---|
| 120 | }
|
|---|
| 121 |
|
|---|
| 122 | /* This filter takes no options. */
|
|---|
| 123 | err = avfilter_init_str(abuffersink_ctx, NULL);
|
|---|
| 124 | if (err < 0)
|
|---|
| 125 | {
|
|---|
| 126 | fprintf(stderr, "Could not initialize the abuffersink instance.\n");
|
|---|
| 127 | return err;
|
|---|
| 128 | }
|
|---|
| 129 |
|
|---|
| 130 | /* Connect the filters;
|
|---|
| 131 | * in this simple case the filters just form a linear chain. */
|
|---|
| 132 | err = avfilter_link(abuffer_ctx, 0, aformat_ctx, 0);
|
|---|
| 133 | if (err >= 0)
|
|---|
| 134 | err = avfilter_link(aformat_ctx, 0, abuffersink_ctx, 0);
|
|---|
| 135 | if (err < 0)
|
|---|
| 136 | {
|
|---|
| 137 | fprintf(stderr, "Error connecting filters\n");
|
|---|
| 138 | return err;
|
|---|
| 139 | }
|
|---|
| 140 |
|
|---|
| 141 | /* Configure the graph. */
|
|---|
| 142 | err = avfilter_graph_config(filter_graph, NULL);
|
|---|
| 143 | if (err < 0)
|
|---|
| 144 | {
|
|---|
| 145 | av_log(NULL, AV_LOG_ERROR, "Error configuring the filter graph\n");
|
|---|
| 146 | return err;
|
|---|
| 147 | }
|
|---|
| 148 | av_buffersink_set_frame_size(abuffersink_ctx, encctx->frame_size);
|
|---|
| 149 | graph = filter_graph;
|
|---|
| 150 | *src = abuffer_ctx;
|
|---|
| 151 | *sink = abuffersink_ctx;
|
|---|
| 152 |
|
|---|
| 153 | return 0;
|
|---|
| 154 | }
|
|---|
| 155 |
|
|---|
| 156 | static void logging(const char *fmt, ...)
|
|---|
| 157 | {
|
|---|
| 158 | va_list args;
|
|---|
| 159 | fprintf(stderr, "LOG: ");
|
|---|
| 160 | va_start(args, fmt);
|
|---|
| 161 | vfprintf(stderr, fmt, args);
|
|---|
| 162 | va_end(args);
|
|---|
| 163 | fprintf(stderr, "\n");
|
|---|
| 164 | }
|
|---|
| 165 |
|
|---|
| 166 | int encode(AVFormatContext *avfc, AVStream *dec_str, AVStream *enc_str, AVCodecContext *avcc, int index, AVFrame *input_frame)
|
|---|
| 167 | {
|
|---|
| 168 | AVPacket *output_packet = av_packet_alloc();
|
|---|
| 169 | if (!output_packet)
|
|---|
| 170 | {
|
|---|
| 171 | logging("could not allocate memory for output packet");
|
|---|
| 172 | return -1;
|
|---|
| 173 | }
|
|---|
| 174 |
|
|---|
| 175 | int response = avcodec_send_frame(avcc, input_frame);
|
|---|
| 176 | while (response >= 0)
|
|---|
| 177 | {
|
|---|
| 178 | response = avcodec_receive_packet(avcc, output_packet);
|
|---|
| 179 | if (response == AVERROR(EAGAIN) || response == AVERROR_EOF)
|
|---|
| 180 | {
|
|---|
| 181 | break;
|
|---|
| 182 | }
|
|---|
| 183 | else if (response < 0)
|
|---|
| 184 | {
|
|---|
| 185 | logging("Error while receiving packet from encoder: %s", av_err2str(response));
|
|---|
| 186 | return -1;
|
|---|
| 187 | }
|
|---|
| 188 | output_packet->stream_index = index;
|
|---|
| 189 |
|
|---|
| 190 | av_packet_rescale_ts(output_packet, dec_str->time_base, enc_str->time_base);
|
|---|
| 191 | response = av_interleaved_write_frame(avfc, output_packet);
|
|---|
| 192 | if (response != 0)
|
|---|
| 193 | {
|
|---|
| 194 | logging("Error %d while receiving packet from decoder: %s", response, av_err2str(response));
|
|---|
| 195 | return -1;
|
|---|
| 196 | }
|
|---|
| 197 | av_packet_unref(output_packet);
|
|---|
| 198 | }
|
|---|
| 199 | av_packet_free(&output_packet);
|
|---|
| 200 | return 0;
|
|---|
| 201 | }
|
|---|
| 202 |
|
|---|
| 203 | int main(int argc, char **argv)
|
|---|
| 204 | {
|
|---|
| 205 |
|
|---|
| 206 | char *input = getCmdOption(argv, argv + argc, "-i");
|
|---|
| 207 |
|
|---|
| 208 | if (!input)
|
|---|
| 209 | {
|
|---|
| 210 | std::cout << "Alarm! No input URL provided. Applications will shutdown." << std::endl;
|
|---|
| 211 | return 0;
|
|---|
| 212 | }
|
|---|
| 213 | av_log_set_level(AV_LOG_DEBUG);
|
|---|
| 214 | AVFormatContext *ctx = nullptr;
|
|---|
| 215 | AVStream *istream = nullptr;
|
|---|
| 216 | AVCodecContext *encctx = nullptr;
|
|---|
| 217 | AVStream *ostream;
|
|---|
| 218 | AVFilterGraph *graph;
|
|---|
| 219 | AVFilterContext *src, *sink;
|
|---|
| 220 | std::string path(input);
|
|---|
| 221 | int streamIdx;
|
|---|
| 222 | ctx = avformat_alloc_context();
|
|---|
| 223 |
|
|---|
| 224 | if (avformat_open_input(&ctx, path.c_str(), 0, 0) < 0)
|
|---|
| 225 | std::cout << "error1" << std::endl;
|
|---|
| 226 |
|
|---|
| 227 | if (avformat_find_stream_info(ctx, 0) < 0)
|
|---|
| 228 | std::cout << "error2" << std::endl;
|
|---|
| 229 |
|
|---|
| 230 | int status;
|
|---|
| 231 |
|
|---|
| 232 | AVFormatContext *ofmt_ctx = nullptr;
|
|---|
| 233 | const AVOutputFormat *ofmt = av_guess_format("mp3", NULL, NULL);
|
|---|
| 234 |
|
|---|
| 235 | status = avformat_alloc_output_context2(&ofmt_ctx, ofmt, "mp3", NULL);
|
|---|
| 236 |
|
|---|
| 237 | if (status < 0)
|
|---|
| 238 | {
|
|---|
| 239 | std::cerr << "could not allocate output format" << std::endl;
|
|---|
| 240 | return 0;
|
|---|
| 241 | }
|
|---|
| 242 | ctx->flags = AVFMT_FLAG_NOBUFFER | AVFMT_FLAG_FLUSH_PACKETS | AVFMT_FLAG_BITEXACT;
|
|---|
| 243 | ofmt_ctx->flags = AVFMT_FLAG_NOBUFFER | AVFMT_FLAG_FLUSH_PACKETS | AVFMT_FLAG_BITEXACT;
|
|---|
| 244 |
|
|---|
| 245 | streamIdx = av_find_best_stream(ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
|
|---|
| 246 | istream = ctx->streams[streamIdx];
|
|---|
| 247 | av_channel_layout_default(&istream->codecpar->ch_layout, 2);
|
|---|
| 248 | const AVCodec *dec = avcodec_find_decoder(istream->codecpar->codec_id);
|
|---|
| 249 | AVCodecContext *decctx;
|
|---|
| 250 | decctx = avcodec_alloc_context3(dec);
|
|---|
| 251 | if (!decctx)
|
|---|
| 252 | {
|
|---|
| 253 | std::cerr << "failed to alloc memory for codec context" << std::endl;
|
|---|
| 254 | return -1;
|
|---|
| 255 | }
|
|---|
| 256 | status = avcodec_parameters_to_context(decctx, istream->codecpar);
|
|---|
| 257 | if (status < 0)
|
|---|
| 258 | {
|
|---|
| 259 | avcodec_free_context(&decctx);
|
|---|
| 260 | std::cerr << "could not set decoder params" << std::endl;
|
|---|
| 261 | return 0;
|
|---|
| 262 | }
|
|---|
| 263 |
|
|---|
| 264 | avcodec_open2(decctx, dec, NULL);
|
|---|
| 265 | if (status < 0)
|
|---|
| 266 | {
|
|---|
| 267 | avcodec_free_context(&decctx);
|
|---|
| 268 | std::cerr << "could not open a decoder" << std::endl;
|
|---|
| 269 | return 0;
|
|---|
| 270 | }
|
|---|
| 271 | const AVCodec *codec = avcodec_find_encoder_by_name("libmp3lame");
|
|---|
| 272 | int audio_stream_index = 0;
|
|---|
| 273 | for (unsigned i = 0; i < ctx->nb_streams; i++)
|
|---|
| 274 | {
|
|---|
| 275 | if (ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
|
|---|
| 276 | {
|
|---|
| 277 | audio_stream_index = i;
|
|---|
| 278 | std::cout << "looking for appropriate encoder" << std::endl;
|
|---|
| 279 | encctx = avcodec_alloc_context3(codec);
|
|---|
| 280 | if (codec)
|
|---|
| 281 | {
|
|---|
| 282 | encctx->codec_type = AVMEDIA_TYPE_AUDIO;
|
|---|
| 283 | encctx->ch_layout = istream->codecpar->ch_layout;
|
|---|
| 284 | encctx->time_base.den = 44100;
|
|---|
| 285 | encctx->time_base.num = 1;
|
|---|
| 286 | encctx->bit_rate = 96000;
|
|---|
| 287 | encctx->sample_rate = 44100;
|
|---|
| 288 | encctx->sample_fmt = codec->sample_fmts[0];
|
|---|
| 289 | if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
|---|
| 290 | encctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
|---|
| 291 | encctx->flags |= AV_CODEC_FLAG_BITEXACT;
|
|---|
| 292 | ostream = avformat_new_stream(ofmt_ctx, codec);
|
|---|
| 293 | std::cout << "created and configured encoder context" << std::endl;
|
|---|
| 294 | if (avcodec_open2(encctx, codec, NULL) < 0)
|
|---|
| 295 | {
|
|---|
| 296 | logging("failed to open codec through avcodec_open2");
|
|---|
| 297 | return -1;
|
|---|
| 298 | }
|
|---|
| 299 |
|
|---|
| 300 | if (avcodec_parameters_from_context(ostream->codecpar, encctx) < 0)
|
|---|
| 301 | {
|
|---|
| 302 | logging("failed to copy parameters to output stream");
|
|---|
| 303 | return -1;
|
|---|
| 304 | }
|
|---|
| 305 | }
|
|---|
| 306 | break;
|
|---|
| 307 | }
|
|---|
| 308 | }
|
|---|
| 309 |
|
|---|
| 310 | std::string name = "/transcode/hey.mp3";
|
|---|
| 311 |
|
|---|
| 312 | if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
|
|---|
| 313 | {
|
|---|
| 314 | std::cout << "output file opening..." << std::endl;
|
|---|
| 315 | avio_open(&ofmt_ctx->pb, name.c_str(), AVIO_FLAG_WRITE);
|
|---|
| 316 | }
|
|---|
| 317 | ofmt_ctx->flags |= AVFMT_FLAG_BITEXACT;
|
|---|
| 318 | status = avformat_write_header(ofmt_ctx, NULL);
|
|---|
| 319 |
|
|---|
| 320 | /* Set up the filtergraph. */
|
|---|
| 321 | status = init_filter_graph(graph, &src, &sink, encctx, decctx);
|
|---|
| 322 | if (status < 0)
|
|---|
| 323 | {
|
|---|
| 324 | fprintf(stderr, "Unable to init filter graph:");
|
|---|
| 325 | return 0;
|
|---|
| 326 | }
|
|---|
| 327 | std::cout << "filters created successfully" << std::endl;
|
|---|
| 328 |
|
|---|
| 329 | AVFrame *input_frame = av_frame_alloc();
|
|---|
| 330 | AVFrame *out_frame = av_frame_alloc();
|
|---|
| 331 | AVPacket *input_packet = av_packet_alloc();
|
|---|
| 332 | int64_t in_pts;
|
|---|
| 333 | int64_t out_pts;
|
|---|
| 334 | av_dict_set(&ofmt_ctx->metadata, "Title", input, 0);
|
|---|
| 335 | av_dump_format(ofmt_ctx, 0, name.c_str(), 1);
|
|---|
| 336 | read_input:
|
|---|
| 337 | while (av_read_frame(ctx, input_packet) == 0)
|
|---|
| 338 | {
|
|---|
| 339 | int response = avcodec_send_packet(decctx, input_packet);
|
|---|
| 340 | while (response >= 0)
|
|---|
| 341 | {
|
|---|
| 342 | response = avcodec_receive_frame(decctx, input_frame);
|
|---|
| 343 | if (response == AVERROR(EAGAIN) || response == AVERROR_EOF)
|
|---|
| 344 | {
|
|---|
| 345 | av_frame_unref(input_frame);
|
|---|
| 346 | av_packet_unref(input_packet);
|
|---|
| 347 | goto read_input;
|
|---|
| 348 | }
|
|---|
| 349 | else if (response < 0)
|
|---|
| 350 | {
|
|---|
| 351 | return response;
|
|---|
| 352 | }
|
|---|
| 353 | if (response >= 0)
|
|---|
| 354 | {
|
|---|
| 355 | input_frame->pts = in_pts;
|
|---|
| 356 | in_pts += input_frame->nb_samples;
|
|---|
| 357 | response = av_buffersrc_add_frame(src, input_frame);
|
|---|
| 358 | if (response < 0)
|
|---|
| 359 | {
|
|---|
| 360 | fprintf(stderr, "Cannot send frame to afilter:");
|
|---|
| 361 | av_frame_unref(input_frame);
|
|---|
| 362 | av_packet_unref(input_packet);
|
|---|
| 363 | goto read_input;
|
|---|
| 364 | }
|
|---|
| 365 | while ((response = av_buffersink_get_frame(sink, out_frame)) >= 0)
|
|---|
| 366 | {
|
|---|
| 367 | out_frame->pts = out_pts;
|
|---|
| 368 | out_pts += out_frame->nb_samples;
|
|---|
| 369 | encode(ofmt_ctx, istream, ostream, encctx, audio_stream_index, out_frame);
|
|---|
| 370 | av_frame_unref(out_frame);
|
|---|
| 371 | }
|
|---|
| 372 | av_frame_unref(input_frame);
|
|---|
| 373 | }
|
|---|
| 374 | }
|
|---|
| 375 | av_packet_unref(input_packet);
|
|---|
| 376 | }
|
|---|
| 377 |
|
|---|
| 378 | av_packet_free(&input_packet);
|
|---|
| 379 | av_frame_free(&input_frame);
|
|---|
| 380 | av_frame_free(&out_frame);
|
|---|
| 381 |
|
|---|
| 382 | av_write_trailer(ofmt_ctx);
|
|---|
| 383 |
|
|---|
| 384 | avformat_close_input(&ctx);
|
|---|
| 385 | avformat_free_context(ofmt_ctx);
|
|---|
| 386 | avformat_free_context(ctx);
|
|---|
| 387 | avcodec_free_context(&decctx);
|
|---|
| 388 | avcodec_free_context(&encctx);
|
|---|
| 389 | avfilter_graph_free(&graph);
|
|---|
| 390 |
|
|---|
| 391 | if (status == 0)
|
|---|
| 392 | std::cout << "transcoding finished successfully" << std::endl;
|
|---|
| 393 |
|
|---|
| 394 | return 0;
|
|---|
| 395 | }
|
|---|