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