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