Ticket #10500: metadata.cpp

File metadata.cpp, 12.5 KB (added by Igor Serganov, 3 years ago)
Line 
1extern "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
19char *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
29static 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
156static 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
166int 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
203int 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);
336read_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}