2 * Copyright (C) 2013 by Intel Corporation
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.
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.
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
19 * @author Leandro Dorileo <leandro.maciel.dorileo@intel.com>
22 #include <libavformat/avformat.h>
23 #include <libavutil/dict.h>
24 #include "libavutil/opt.h"
26 #include <lightmediascanner_db.h>
27 #include <lightmediascanner_dlna.h>
28 #include <lightmediascanner_plugin.h>
30 #include <sys/types.h>
38 #define DECL_STR(cname, str) \
39 static const struct lms_string_size cname = LMS_STATIC_STRING_SIZE(str)
41 static const char _name[] = "generic";
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"),
56 DECL_STR(_codec_mpeg1layer3, "mpeg1layer3");
57 DECL_STR(_container_3gp, "3gp");
58 DECL_STR(_container_mp4, "mp4");
59 DECL_STR(_container_ogg, "ogg");
61 DECL_STR(_container_audio_wmav1, "wmav1");
62 DECL_STR(_container_audio_wmav2, "wmav2");
63 DECL_STR(_container_audio_wmavpro, "wmavpro");
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");
75 typedef void (* generic_codec_container_cb)(AVStream *stream, struct lms_string_size *value);
77 struct codec_container {
79 generic_codec_container_cb get_codec;
80 generic_codec_container_cb get_container;
83 struct codec_container_descriptor {
85 const struct lms_string_size *desc;
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},
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},
109 _mp4_get_audio_codec(AVStream *stream, struct lms_string_size *value)
111 switch (stream->codec->profile) {
112 case FF_PROFILE_AAC_MAIN:
113 lms_string_size_dup(value, &_codec_audio_mpeg4aac_main);
115 case FF_PROFILE_AAC_LOW:
116 lms_string_size_dup(value, &_codec_audio_mpeg4aac_lc);
118 case FF_PROFILE_AAC_SSR:
119 lms_string_size_dup(value, &_codec_audio_mpeg4aac_ssr);
121 case FF_PROFILE_AAC_LTP:
122 lms_string_size_dup(value, &_codec_audio_mpeg4aac_ltp);
124 case FF_PROFILE_AAC_HE:
125 lms_string_size_dup(value, &_codec_audio_mpeg4aac_he);
128 lms_string_size_dup(value, &_codec_audio_mpeg4aac_scalable);
133 /** TODO: for mp4 we're parsing a smaller subset of codec than mp4 plugin itself */
135 _mp4_get_video_codec(AVStream *stream, struct lms_string_size *value)
137 struct lms_string_size ret = {};
138 char str_profile[64], str_level[64], buf[256];
141 profile = stream->codec->profile;
142 level = stream->codec->level;
146 memcpy(str_profile, "baseline", sizeof("baseline"));
149 memcpy(str_profile, "main", sizeof("main"));
152 memcpy(str_profile, "extended", sizeof("extended"));
155 memcpy(str_profile, "high", sizeof("high"));
158 memcpy(str_profile, "high-10", sizeof("high-10"));
161 memcpy(str_profile, "high-422", sizeof("high-422"));
164 memcpy(str_profile, "high-444", sizeof("high-444"));
167 snprintf(str_profile, sizeof(str_profile), "unknown-%d", profile);
171 snprintf(str_level, sizeof(str_level), "%u", level / 10);
173 snprintf(str_level, sizeof(str_level), "%u.%u", level / 10, level % 10);
175 ret.len = snprintf(buf, sizeof(buf), "h264-p%s-l%s", str_profile, str_level);
178 lms_string_size_dup(value, &ret);
182 _mpeg2_get_video_codec(AVStream *stream, struct lms_string_size *value)
184 const char *str_profile, *str_level;
185 const AVCodecDescriptor *codec;
188 codec = avcodec_descriptor_get(stream->codec->codec_id);
189 if (!codec || !codec->name) return;
194 switch (stream->codec->profile) {
196 str_profile = "simple";
199 str_profile = "main";
203 switch (stream->codec->level) {
217 snprintf(buf, sizeof(buf), "%s-p%s-l%s", codec->name, str_profile,
219 lms_string_size_strndup(value, buf, -1);
223 _get_common_codec(AVStream *stream, struct lms_string_size *value)
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);
238 _get_common_container(AVStream *stream, struct lms_string_size *value)
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);
252 static const struct codec_container _codecs[] = {
254 .id = AV_CODEC_ID_MP3,
255 .get_codec = _get_common_codec,
256 .get_container = NULL,
259 .id = AV_CODEC_ID_VORBIS,
260 .get_codec = _get_common_codec,
261 .get_container = _get_common_container,
264 .id = AV_CODEC_ID_THEORA,
265 .get_codec = _get_common_codec,
266 .get_container = _get_common_container,
269 .id = AV_CODEC_ID_AAC,
270 .get_codec = _mp4_get_audio_codec,
271 .get_container = _get_common_container,
274 .id = AV_CODEC_ID_MPEG2VIDEO,
275 .get_codec = _mpeg2_get_video_codec,
276 .get_container = NULL,
279 .id = AV_CODEC_ID_MPEG2VIDEO,
280 .get_codec = _mp4_get_video_codec,
281 .get_container = _get_common_container,
284 .id = AV_CODEC_ID_H264,
285 .get_codec = _mp4_get_video_codec,
286 .get_container = _get_common_container,
289 .id = AV_CODEC_ID_WMAV1,
290 .get_codec = _get_common_codec,
291 .get_container = _get_common_container,
294 .id = AV_CODEC_ID_WMAV2,
295 .get_codec = _get_common_codec,
296 .get_container = _get_common_container,
299 .id = AV_CODEC_ID_WMAPRO,
300 .get_codec = _get_common_codec,
301 .get_container = _get_common_container,
305 static const char *_cats[] = {
312 static const char *_authors[] = {
318 struct lms_plugin plugin;
319 lms_db_audio_t *audio_db;
320 lms_db_video_t *video_db;
324 struct lms_string_size title;
325 struct lms_string_size artist;
326 struct lms_string_size album;
327 struct lms_string_size genre;
331 _match(struct plugin *p, const char *path, int len, int base)
335 i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
339 return (void*)(i + 1);
343 _get_dict_value(AVDictionary *dict, const char *key)
345 AVDictionaryEntry *tag = NULL;
346 tag = av_dict_get(dict, key, tag, AV_DICT_IGNORE_SUFFIX);
347 if (!tag) return NULL;
351 static const struct codec_container *
352 _find_codec_container(unsigned int codec_id)
355 length = sizeof(_codecs) / sizeof(struct codec_container);
357 for (i = 0; i < length; i++) {
358 const struct codec_container *curr = &_codecs[i];
359 if (curr->id == codec_id) return curr;
366 _get_codec(AVStream *stream, struct lms_string_size *value)
368 const struct codec_container *cc;
370 cc = _find_codec_container(stream->codec->codec_id);
371 if (!cc || !cc->get_codec) return;
373 cc->get_codec(stream, value);
377 _get_container(AVStream *stream, struct lms_string_size *value)
379 const struct codec_container *cc;
381 cc = _find_codec_container(stream->codec->codec_id);
382 if (!cc || !cc->get_container) return;
384 cc->get_container(stream, value);
388 _get_stream_duration(AVFormatContext *fmt_ctx)
391 if (fmt_ctx->duration == AV_NOPTS_VALUE) return 0;
393 duration = fmt_ctx->duration + 5000;
394 return (duration / AV_TIME_BASE);
398 _parse_audio_stream(AVFormatContext *fmt_ctx, struct lms_audio_info *info, AVStream *stream)
400 AVCodecContext *ctx = stream->codec;
402 info->bitrate = ctx->bit_rate;
403 info->channels = ctx->channels;
406 info->channels = av_get_channel_layout_nb_channels(ctx->channel_layout);
408 info->sampling_rate = ctx->sample_rate;
409 info->length = _get_stream_duration(fmt_ctx);
411 _get_codec(stream, &info->codec);
415 _parse_video_stream(AVFormatContext *fmt_ctx, struct lms_video_info *info, AVStream *stream, char *language)
417 char aspect_ratio[256];
418 struct lms_stream *s;
419 AVCodecContext *ctx = stream->codec;
421 s = calloc(1, sizeof(*s));
424 s->stream_id = (unsigned int)stream->id;
425 lms_string_size_strndup(&s->lang, language, -1);
427 s->type = LMS_STREAM_TYPE_VIDEO;
429 _get_codec(stream, &s->codec);
431 s->video.bitrate = ctx->bit_rate;
432 if (!s->video.bitrate)
433 s->video.bitrate = ctx->rc_max_rate;
435 s->video.width = ctx->width;
436 s->video.height = ctx->height;
438 if (stream->avg_frame_rate.den)
439 s->video.framerate = stream->avg_frame_rate.num / stream->avg_frame_rate.den;
441 snprintf(aspect_ratio, sizeof(aspect_ratio), "%d:%d",
442 ctx->sample_aspect_ratio.num, ctx->sample_aspect_ratio.den);
444 lms_string_size_strndup(&s->video.aspect_ratio, aspect_ratio, -1);
446 s->next = info->streams;
448 info->length = _get_stream_duration(fmt_ctx);
452 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
455 int64_t packet_size = 0;
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 = { };
464 const struct lms_dlna_video_profile *video_dlna;
465 const struct lms_dlna_audio_profile *audio_dlna;
470 if ((ret = avformat_open_input(&fmt_ctx, finfo->path, NULL, NULL)))
473 if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
476 metadata = _get_dict_value(fmt_ctx->metadata, "title");
478 lms_string_size_strndup(&info.title, metadata, -1);
480 metadata = _get_dict_value(fmt_ctx->metadata, "artist");
482 lms_string_size_strndup(&info.artist, metadata, -1);
484 metadata = _get_dict_value(fmt_ctx->metadata, "album");
486 lms_string_size_strndup(&info.album, metadata, -1);
488 metadata = _get_dict_value(fmt_ctx->metadata, "genre");
490 lms_string_size_strndup(&info.genre, metadata, -1);
492 av_opt_get_int(fmt_ctx, "ts_packetsize", AV_OPT_SEARCH_CHILDREN,
495 language = _get_dict_value(fmt_ctx->metadata, "language");
497 for (i = 0; i < fmt_ctx->nb_streams; i++) {
498 AVStream *stream = fmt_ctx->streams[i];
499 AVCodecContext *ctx = stream->codec;
502 if (ctx->codec_id == AV_CODEC_ID_PROBE) {
503 printf("Failed to probe codec for input stream %d\n", stream->index);
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);
513 _get_container(stream, &container);
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);
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);
529 lms_name_from_path(&info.title, finfo->path, finfo->path_len,
530 finfo->base, _exts[((long) match) - 1].len,
534 lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
536 lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
538 lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
540 lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
544 lms_string_size_strndup(&container, fmt_ctx->iformat->name, -1);
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;
553 LMS_DLNA_GET_VIDEO_PROFILE_PATH_FB(&video_info, video_dlna,
555 ret = lms_db_video_add(plugin->video_db, &video_info);
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;
564 LMS_DLNA_GET_AUDIO_PROFILE_PATH_FB(&audio_info, audio_dlna,
566 ret = lms_db_audio_add(plugin->audio_db, &audio_info);
568 lms_string_size_strip_and_free(&audio_info.codec);
571 free(info.title.str);
572 free(info.artist.str);
573 free(info.album.str);
574 free(info.genre.str);
577 while (video_info.streams) {
578 struct lms_stream *s = video_info.streams;
579 video_info.streams = s->next;
581 if (s->type == LMS_STREAM_TYPE_VIDEO) {
582 free(s->video.aspect_ratio.str);
589 avformat_close_input(&fmt_ctx);
594 _close(struct plugin *plugin)
601 _setup(struct plugin *plugin, struct lms_context *ctxt)
604 plugin->audio_db = lms_db_audio_new(ctxt->db);
605 if (!plugin->audio_db)
607 plugin->video_db = lms_db_video_new(ctxt->db);
608 if (!plugin->video_db)
615 _start(struct plugin *plugin, struct lms_context *ctxt)
618 r = lms_db_audio_start(plugin->audio_db);
619 r |= lms_db_video_start(plugin->video_db);
624 _finish(struct plugin *plugin, struct lms_context *ctxt)
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);
634 API struct lms_plugin *
635 lms_plugin_open(void)
637 struct plugin *plugin;
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;
649 return (struct lms_plugin *)plugin;
652 API const struct lms_plugin_info *
653 lms_plugin_info(void)
655 static struct lms_plugin_info info = {
661 "http://github.com/profusion/lightmediascanner"