Porting to libav v11
[platform/upstream/lightmediascanner.git] / src / plugins / generic / generic.c
1 /**
2  * Copyright (C) 2013 by Intel Corporation
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2.1 of
7  * the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17  * 02110-1301 USA
18  *
19  * @author Leandro Dorileo <leandro.maciel.dorileo@intel.com>
20  */
21
22 #include <libavformat/avformat.h>
23 #include <libavutil/dict.h>
24 #include "libavutil/opt.h"
25
26 #include <lightmediascanner_db.h>
27 #include <lightmediascanner_dlna.h>
28 #include <lightmediascanner_plugin.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <limits.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37
38 #define DECL_STR(cname, str)                                            \
39     static const struct lms_string_size cname = LMS_STATIC_STRING_SIZE(str)
40
41 static const char _name[] = "generic";
42
43 static const struct lms_string_size _exts[] = {
44     LMS_STATIC_STRING_SIZE(".mpeg"),
45     LMS_STATIC_STRING_SIZE(".mpg"),
46     LMS_STATIC_STRING_SIZE(".mpeg2"),
47     LMS_STATIC_STRING_SIZE(".mp2"),
48     LMS_STATIC_STRING_SIZE(".mp3"),
49     LMS_STATIC_STRING_SIZE(".adts"),
50     LMS_STATIC_STRING_SIZE(".m3u"),
51     LMS_STATIC_STRING_SIZE(".mp4"),
52     LMS_STATIC_STRING_SIZE(".wma"),
53     LMS_STATIC_STRING_SIZE(".ogg"),
54 };
55
56 DECL_STR(_codec_mpeg1layer3, "mpeg1layer3");
57 DECL_STR(_container_3gp, "3gp");
58 DECL_STR(_container_mp4, "mp4");
59 DECL_STR(_container_ogg, "ogg");
60
61 DECL_STR(_container_audio_wmav1, "wmav1");
62 DECL_STR(_container_audio_wmav2, "wmav2");
63 DECL_STR(_container_audio_wmavpro, "wmavpro");
64
65 DECL_STR(_codec_video_theora, "theora");
66 DECL_STR(_codec_audio_vorbis, "vorbis");
67 DECL_STR(_codec_audio_asf, "asf");
68 DECL_STR(_codec_audio_mpeg4aac_main, "mpeg4aac-main");
69 DECL_STR(_codec_audio_mpeg4aac_lc, "mpeg4aac-lc");
70 DECL_STR(_codec_audio_mpeg4aac_ssr, "mpeg4aac-ssr");
71 DECL_STR(_codec_audio_mpeg4aac_ltp, "mpeg4aac-ltp");
72 DECL_STR(_codec_audio_mpeg4aac_he, "mpeg4aac-he");
73 DECL_STR(_codec_audio_mpeg4aac_scalable, "mpeg4aac-scalable");
74
75 typedef void (* generic_codec_container_cb)(AVStream *stream, struct lms_string_size *value);
76
77 struct codec_container {
78     unsigned int id;
79     generic_codec_container_cb get_codec;
80     generic_codec_container_cb get_container;
81 };
82
83 struct codec_container_descriptor {
84     unsigned int id;
85     const struct lms_string_size *desc;
86 };
87
88 static const struct codec_container_descriptor _codec_list[] = {
89     {AV_CODEC_ID_MP3, &_codec_mpeg1layer3},
90     {AV_CODEC_ID_WMAV1, &_codec_audio_asf},
91     {AV_CODEC_ID_WMAV2, &_codec_audio_asf},
92     {AV_CODEC_ID_WMAPRO, &_codec_audio_asf},
93     {AV_CODEC_ID_VORBIS, &_codec_audio_vorbis},
94     {AV_CODEC_ID_THEORA, &_codec_video_theora},
95 };
96
97 static const struct codec_container_descriptor _container_list[] = {
98     {AV_CODEC_ID_MPEG2VIDEO, &_container_mp4},
99     {AV_CODEC_ID_AAC, &_container_3gp},
100     {AV_CODEC_ID_WMAV1, &_container_audio_wmav1},
101     {AV_CODEC_ID_WMAV2, &_container_audio_wmav2},
102     {AV_CODEC_ID_WMAPRO, &_container_audio_wmavpro},
103     {AV_CODEC_ID_H264, &_container_mp4},
104     {AV_CODEC_ID_VORBIS, &_container_ogg},
105     {AV_CODEC_ID_THEORA, &_container_ogg},
106 };
107
108 static void
109 _mp4_get_audio_codec(AVStream *stream, struct lms_string_size *value)
110 {
111     switch (stream->codec->profile) {
112     case FF_PROFILE_AAC_MAIN:
113         lms_string_size_dup(value, &_codec_audio_mpeg4aac_main);
114         break;
115     case FF_PROFILE_AAC_LOW:
116         lms_string_size_dup(value, &_codec_audio_mpeg4aac_lc);
117         break;
118     case FF_PROFILE_AAC_SSR:
119         lms_string_size_dup(value, &_codec_audio_mpeg4aac_ssr);
120         break;
121     case FF_PROFILE_AAC_LTP:
122         lms_string_size_dup(value, &_codec_audio_mpeg4aac_ltp);
123         break;
124     case FF_PROFILE_AAC_HE:
125         lms_string_size_dup(value, &_codec_audio_mpeg4aac_he);
126         break;
127     default:
128         lms_string_size_dup(value, &_codec_audio_mpeg4aac_scalable);
129         break;
130     }
131 }
132
133 /** TODO: for mp4 we're parsing a smaller subset of codec than mp4 plugin itself */
134 static void
135 _mp4_get_video_codec(AVStream *stream, struct lms_string_size *value)
136 {
137     struct lms_string_size ret = {};
138     char str_profile[64], str_level[64], buf[256];
139     int level, profile;
140
141     profile = stream->codec->profile;
142     level = stream->codec->level;
143
144     switch (profile) {
145     case 66:
146         memcpy(str_profile, "baseline", sizeof("baseline"));
147         break;
148     case 77:
149         memcpy(str_profile, "main", sizeof("main"));
150         break;
151     case 88:
152         memcpy(str_profile, "extended", sizeof("extended"));
153         break;
154     case 100:
155         memcpy(str_profile, "high", sizeof("high"));
156         break;
157     case 110:
158         memcpy(str_profile, "high-10", sizeof("high-10"));
159         break;
160     case 122:
161         memcpy(str_profile, "high-422", sizeof("high-422"));
162         break;
163     case 144:
164         memcpy(str_profile, "high-444", sizeof("high-444"));
165         break;
166     default:
167         snprintf(str_profile, sizeof(str_profile), "unknown-%d", profile);
168     }
169
170     if (level % 10 == 0)
171         snprintf(str_level, sizeof(str_level), "%u", level / 10);
172     else
173         snprintf(str_level, sizeof(str_level), "%u.%u", level / 10, level % 10);
174
175     ret.len = snprintf(buf, sizeof(buf), "h264-p%s-l%s", str_profile, str_level);
176     ret.str = buf;
177
178     lms_string_size_dup(value, &ret);
179 }
180
181 static void
182 _mpeg2_get_video_codec(AVStream *stream, struct lms_string_size *value)
183 {
184     const char *str_profile, *str_level;
185     const AVCodecDescriptor *codec;
186     char buf[256];
187
188     codec = avcodec_descriptor_get(stream->codec->codec_id);
189     if (!codec || !codec->name) return;
190
191     str_profile = NULL;
192     str_level = NULL;
193
194     switch (stream->codec->profile) {
195     case 6:
196         str_profile = "simple";
197         break;
198     case 4:
199         str_profile = "main";
200         break;
201     }
202
203     switch (stream->codec->level) {
204     case 5:
205     case 8:
206         str_level = "main";
207         break;
208     case 2:
209     case 4:
210         str_level = "high";
211         break;
212     case 6:
213         str_level = "high";
214         break;
215     }
216
217     snprintf(buf, sizeof(buf), "%s-p%s-l%s", codec->name, str_profile,
218              str_level);
219     lms_string_size_strndup(value, buf, -1);
220 }
221
222 static void
223 _get_common_codec(AVStream *stream, struct lms_string_size *value)
224 {
225     int length, i;
226
227     length = sizeof(_codec_list) / sizeof(struct codec_container_descriptor);
228     for (i = 0; i < length; i++) {
229         const struct codec_container_descriptor *curr = _codec_list + i;
230         if (curr->id == stream->codec->codec_id) {
231             lms_string_size_dup(value, curr->desc);
232             break;
233         }
234     }
235 }
236
237 static void
238 _get_common_container(AVStream *stream, struct lms_string_size *value)
239 {
240     int length, i;
241
242     length = sizeof(_container_list) / sizeof(struct codec_container_descriptor);
243     for (i = 0; i < length; i++) {
244         const struct codec_container_descriptor *curr = _container_list + i;
245         if (curr->id == stream->codec->codec_id) {
246             lms_string_size_dup(value, curr->desc);
247             break;
248         }
249     }
250 }
251
252 static const struct codec_container _codecs[] = {
253     {
254         .id = AV_CODEC_ID_MP3,
255         .get_codec = _get_common_codec,
256         .get_container = NULL,
257     },
258     {
259         .id = AV_CODEC_ID_VORBIS,
260         .get_codec = _get_common_codec,
261         .get_container = _get_common_container,
262     },
263     {
264         .id = AV_CODEC_ID_THEORA,
265         .get_codec = _get_common_codec,
266         .get_container = _get_common_container,
267     },
268     {
269         .id = AV_CODEC_ID_AAC,
270         .get_codec = _mp4_get_audio_codec,
271         .get_container = _get_common_container,
272     },
273     {
274         .id = AV_CODEC_ID_MPEG2VIDEO,
275         .get_codec = _mpeg2_get_video_codec,
276         .get_container = NULL,
277     },
278     {
279         .id = AV_CODEC_ID_MPEG2VIDEO,
280         .get_codec = _mp4_get_video_codec,
281         .get_container = _get_common_container,
282     },
283     {
284         .id = AV_CODEC_ID_H264,
285         .get_codec = _mp4_get_video_codec,
286         .get_container = _get_common_container,
287     },
288     {
289         .id = AV_CODEC_ID_WMAV1,
290         .get_codec = _get_common_codec,
291         .get_container = _get_common_container,
292     },
293     {
294         .id = AV_CODEC_ID_WMAV2,
295         .get_codec = _get_common_codec,
296         .get_container = _get_common_container,
297     },
298     {
299         .id = AV_CODEC_ID_WMAPRO,
300         .get_codec = _get_common_codec,
301         .get_container = _get_common_container,
302     },
303 };
304
305 static const char *_cats[] = {
306     "multimedia",
307     "audio",
308     "video",
309     NULL
310 };
311
312 static const char *_authors[] = {
313     "Leandro Dorileo",
314     NULL
315 };
316
317 struct plugin {
318     struct lms_plugin plugin;
319     lms_db_audio_t *audio_db;
320     lms_db_video_t *video_db;
321 };
322
323 struct mpeg_info {
324     struct lms_string_size title;
325     struct lms_string_size artist;
326     struct lms_string_size album;
327     struct lms_string_size genre;
328 };
329
330 static void *
331 _match(struct plugin *p, const char *path, int len, int base)
332 {
333     long i;
334
335     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
336     if (i < 0)
337       return NULL;
338     else
339       return (void*)(i + 1);
340 }
341
342 static char *
343 _get_dict_value(AVDictionary *dict, const char *key)
344 {
345     AVDictionaryEntry *tag = NULL;
346     tag = av_dict_get(dict, key, tag, AV_DICT_IGNORE_SUFFIX);
347     if (!tag) return NULL;
348     return tag->value;
349 }
350
351 static const struct codec_container *
352 _find_codec_container(unsigned int codec_id)
353 {
354     int i, length;
355     length = sizeof(_codecs) / sizeof(struct codec_container);
356
357     for (i = 0; i < length; i++) {
358       const struct codec_container *curr = &_codecs[i];
359       if (curr->id == codec_id) return curr;
360     }
361
362     return NULL;
363 }
364
365 static void
366 _get_codec(AVStream *stream, struct lms_string_size *value)
367 {
368     const struct codec_container *cc;
369
370     cc = _find_codec_container(stream->codec->codec_id);
371     if (!cc || !cc->get_codec) return;
372
373     cc->get_codec(stream, value);
374 }
375
376 static void
377 _get_container(AVStream *stream, struct lms_string_size *value)
378 {
379     const struct codec_container *cc;
380
381     cc = _find_codec_container(stream->codec->codec_id);
382     if (!cc || !cc->get_container) return;
383
384     cc->get_container(stream, value);
385 }
386
387 static int
388 _get_stream_duration(AVFormatContext *fmt_ctx)
389 {
390     int64_t duration;
391     if (fmt_ctx->duration == AV_NOPTS_VALUE) return 0;
392
393     duration = fmt_ctx->duration + 5000;
394     return (duration / AV_TIME_BASE);
395 }
396
397 static void
398 _parse_audio_stream(AVFormatContext *fmt_ctx, struct lms_audio_info *info, AVStream *stream)
399 {
400     AVCodecContext *ctx = stream->codec;
401
402     info->bitrate = ctx->bit_rate;
403     info->channels = ctx->channels;
404
405     if (!info->channels)
406         info->channels = av_get_channel_layout_nb_channels(ctx->channel_layout);
407
408     info->sampling_rate = ctx->sample_rate;
409     info->length = _get_stream_duration(fmt_ctx);
410
411     _get_codec(stream, &info->codec);
412 }
413
414 static void
415 _parse_video_stream(AVFormatContext *fmt_ctx, struct lms_video_info *info, AVStream *stream, char *language)
416 {
417     char aspect_ratio[256];
418     struct lms_stream *s;
419     AVCodecContext *ctx = stream->codec;
420
421     s = calloc(1, sizeof(*s));
422     if (!s) return;
423
424     s->stream_id = (unsigned int)stream->id;
425     lms_string_size_strndup(&s->lang, language, -1);
426
427     s->type = LMS_STREAM_TYPE_VIDEO;
428
429     _get_codec(stream, &s->codec);
430
431     s->video.bitrate = ctx->bit_rate;
432     if (!s->video.bitrate)
433         s->video.bitrate = ctx->rc_max_rate;
434
435     s->video.width = ctx->width;
436     s->video.height = ctx->height;
437
438     if (stream->avg_frame_rate.den)
439         s->video.framerate = stream->avg_frame_rate.num / stream->avg_frame_rate.den;
440
441     snprintf(aspect_ratio, sizeof(aspect_ratio), "%d:%d",
442              ctx->sample_aspect_ratio.num, ctx->sample_aspect_ratio.den);
443
444     lms_string_size_strndup(&s->video.aspect_ratio, aspect_ratio, -1);
445
446     s->next = info->streams;
447     info->streams = s;
448     info->length = _get_stream_duration(fmt_ctx);
449 }
450
451 static int
452 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
453 {
454     int ret;
455     int64_t packet_size = 0;
456     unsigned int i;
457     AVFormatContext *fmt_ctx = NULL;
458     struct mpeg_info info = { };
459     char *metadata, *language;
460     struct lms_audio_info audio_info = { };
461     struct lms_video_info video_info = { };
462     struct lms_string_size container = { };
463     bool video = false;
464     const struct lms_dlna_video_profile *video_dlna;
465     const struct lms_dlna_audio_profile *audio_dlna;
466
467     if (finfo->parsed)
468         return 0;
469
470     if ((ret = avformat_open_input(&fmt_ctx, finfo->path, NULL, NULL)))
471         return ret;
472
473     if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
474         goto fail;
475
476     metadata = _get_dict_value(fmt_ctx->metadata, "title");
477     if (metadata)
478         lms_string_size_strndup(&info.title, metadata, -1);
479
480     metadata = _get_dict_value(fmt_ctx->metadata, "artist");
481     if (metadata)
482         lms_string_size_strndup(&info.artist, metadata, -1);
483
484     metadata = _get_dict_value(fmt_ctx->metadata, "album");
485     if (metadata)
486         lms_string_size_strndup(&info.album, metadata, -1);
487
488     metadata = _get_dict_value(fmt_ctx->metadata, "genre");
489     if (metadata)
490         lms_string_size_strndup(&info.genre, metadata, -1);
491
492     av_opt_get_int(fmt_ctx, "ts_packetsize", AV_OPT_SEARCH_CHILDREN,
493                    &packet_size);
494
495     language = _get_dict_value(fmt_ctx->metadata, "language");
496
497     for (i = 0; i < fmt_ctx->nb_streams; i++) {
498         AVStream *stream = fmt_ctx->streams[i];
499         AVCodecContext *ctx = stream->codec;
500         AVCodec *codec;
501
502         if (ctx->codec_id == AV_CODEC_ID_PROBE) {
503             printf("Failed to probe codec for input stream %d\n", stream->index);
504             continue;
505         }
506         
507         if (!(codec = avcodec_find_decoder(ctx->codec_id))) {
508             printf("Unsupported codec with id %d for input stream %d\n",
509                    stream->codec->codec_id, stream->index);
510             continue;
511         }
512
513         _get_container(stream, &container);
514
515         if (ctx->codec_type == AVMEDIA_TYPE_AUDIO)
516             _parse_audio_stream(fmt_ctx, &audio_info, stream);
517         else if (ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
518             _parse_video_stream(fmt_ctx, &video_info, stream, language);
519             video = true;
520         }
521     }
522
523     lms_string_size_strip_and_free(&info.title);
524     lms_string_size_strip_and_free(&info.artist);
525     lms_string_size_strip_and_free(&info.album);
526     lms_string_size_strip_and_free(&info.genre);
527
528     if (!info.title.str)
529         lms_name_from_path(&info.title, finfo->path, finfo->path_len,
530                            finfo->base, _exts[((long) match) - 1].len,
531                            NULL);
532
533     if (info.title.str)
534         lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
535     if (info.artist.str)
536         lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
537     if (info.album.str)
538       lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
539     if (info.genre.str)
540         lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
541
542
543     if (!container.str)
544         lms_string_size_strndup(&container, fmt_ctx->iformat->name, -1);
545
546     if (video) {
547         video_info.id = finfo->id;
548         video_info.title = info.title;
549         video_info.artist = info.artist;
550         video_info.container = container;
551         video_info.packet_size = packet_size;
552
553         LMS_DLNA_GET_VIDEO_PROFILE_PATH_FB(&video_info, video_dlna,
554                                            finfo->path);
555         ret = lms_db_video_add(plugin->video_db, &video_info);
556     } else {
557         audio_info.id = finfo->id;
558         audio_info.title = info.title;
559         audio_info.artist = info.artist;
560         audio_info.album = info.album;
561         audio_info.genre = info.genre;
562         audio_info.container = container;
563
564         LMS_DLNA_GET_AUDIO_PROFILE_PATH_FB(&audio_info, audio_dlna,
565                                            finfo->path);
566         ret = lms_db_audio_add(plugin->audio_db, &audio_info);
567
568         lms_string_size_strip_and_free(&audio_info.codec);
569     }
570
571     free(info.title.str);
572     free(info.artist.str);
573     free(info.album.str);
574     free(info.genre.str);
575     free(container.str);
576
577     while (video_info.streams) {
578         struct lms_stream *s = video_info.streams;
579         video_info.streams = s->next;
580         free(s->codec.str);
581         if (s->type == LMS_STREAM_TYPE_VIDEO) {
582             free(s->video.aspect_ratio.str);
583         }
584         free(s->lang.str);
585         free(s);
586     }
587
588  fail:
589     avformat_close_input(&fmt_ctx);
590     return ret;
591 }
592
593 static int
594 _close(struct plugin *plugin)
595 {
596     free(plugin);
597     return 0;
598 }
599
600 static int
601 _setup(struct plugin *plugin,  struct lms_context *ctxt)
602 {
603     av_register_all();
604     plugin->audio_db = lms_db_audio_new(ctxt->db);
605     if (!plugin->audio_db)
606         return -1;
607     plugin->video_db = lms_db_video_new(ctxt->db);
608     if (!plugin->video_db)
609         return -1;
610
611     return 0;
612 }
613
614 static int
615 _start(struct plugin *plugin, struct lms_context *ctxt)
616 {
617     int r;
618     r = lms_db_audio_start(plugin->audio_db);
619     r |= lms_db_video_start(plugin->video_db);
620     return r;
621 }
622
623 static int
624 _finish(struct plugin *plugin, struct lms_context *ctxt)
625 {
626     if (plugin->audio_db)
627         lms_db_audio_free(plugin->audio_db);
628     if (plugin->video_db)
629         lms_db_video_free(plugin->video_db);
630
631     return 0;
632 }
633
634 API struct lms_plugin *
635 lms_plugin_open(void)
636 {
637     struct plugin *plugin;
638
639     plugin = (struct plugin *)malloc(sizeof(*plugin));
640     plugin->plugin.name = _name;
641     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
642     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
643     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
644     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
645     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
646     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
647     plugin->plugin.order = INT32_MAX;
648
649     return (struct lms_plugin *)plugin;
650 }
651
652 API const struct lms_plugin_info *
653 lms_plugin_info(void)
654 {
655     static struct lms_plugin_info info = {
656         _name,
657         _cats,
658         "libavcodec",
659         PACKAGE_VERSION,
660         _authors,
661         "http://github.com/profusion/lightmediascanner"
662     };
663
664     return &info;
665 }