From fcc9a6b1f606ac204f40727b0ea0c325204efa97 Mon Sep 17 00:00:00 2001 From: Leandro Dorileo Date: Thu, 19 Dec 2013 14:55:38 -0200 Subject: [PATCH] generic: implemente a generic parser plugin A generic parser plugin based on libavcoded, initially supporting mpeg1, mpeg2 and mp3. --- configure.ac | 7 + src/plugins/Makefile.am | 6 + src/plugins/generic/generic.c | 1098 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1111 insertions(+) create mode 100644 src/plugins/generic/generic.c diff --git a/configure.ac b/configure.ac index 9483a59..635f141 100644 --- a/configure.ac +++ b/configure.ac @@ -95,6 +95,12 @@ define([CHECK_MODULE_FLAC], AC_LMS_CHECK_PKG(FLAC, flac, [], [FLAC=false]) ]) +AM_CONDITIONAL(HAVE_GENERIC, false) +define([CHECK_MODULE_GENERIC], +[ + AC_LMS_CHECK_PKG(GENERIC, [libavcodec libavformat], [], [GENERIC=false]) +]) + # plugins declarations AC_LMS_OPTIONAL_MODULE([dummy], true) AC_LMS_OPTIONAL_MODULE([jpeg], true) @@ -110,6 +116,7 @@ AC_LMS_OPTIONAL_MODULE([mp4], true, [CHECK_MODULE_MP4]) AC_LMS_OPTIONAL_MODULE([id3], true) AC_LMS_OPTIONAL_MODULE([flac], true, [CHECK_MODULE_FLAC]) AC_LMS_OPTIONAL_MODULE([wave], true) +AC_LMS_OPTIONAL_MODULE([generic], true, [CHECK_MODULE_GENERIC]) AC_ARG_ENABLE([daemon], diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index d7c94ab..956ec8f 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -113,4 +113,10 @@ wave_wave_la_SOURCES = wave/wave.c wave_wave_la_LIBADD = $(PLUGINS_LIBADD) $(FLAC_LIBS) endif +if USE_MODULE_GENERIC +pkg_LTLIBRARIES += generic/generic.la +generic_generic_la_SOURCES = generic/generic.c +generic_generic_la_LIBADD = $(PLUGINS_LIBADD) $(GENERIC_LIBS) +endif + CLEAN_FILES = $(BUILT_SOURCES) diff --git a/src/plugins/generic/generic.c b/src/plugins/generic/generic.c new file mode 100644 index 0000000..99221d5 --- /dev/null +++ b/src/plugins/generic/generic.c @@ -0,0 +1,1098 @@ +/** + * Copyright (C) 2013 by Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * @author Leandro Dorileo + */ + +#include +#include +#include "libavutil/opt.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static const char _name[] = "generic"; + +static const struct lms_string_size _exts[] = { + LMS_STATIC_STRING_SIZE(".mpeg"), + LMS_STATIC_STRING_SIZE(".mpg"), + LMS_STATIC_STRING_SIZE(".mpeg2"), + LMS_STATIC_STRING_SIZE(".mp2"), + LMS_STATIC_STRING_SIZE(".mp3"), + LMS_STATIC_STRING_SIZE(".m3u"), +}; + +static const char *_cats[] = { + "multimedia", + "audio", + "video", + NULL +}; + +static const char *_authors[] = { + "Leandro Dorileo", + NULL +}; + +struct plugin { + struct lms_plugin plugin; + lms_db_audio_t *audio_db; + lms_db_video_t *video_db; +}; + +struct mpeg_info { + struct lms_string_size title; + struct lms_string_size artist; + struct lms_string_size album; + struct lms_string_size genre; +}; + +#define DECL_STR(cname, str) \ + static const struct lms_string_size cname = LMS_STATIC_STRING_SIZE(str) \ + +#define DLNA_VIDEO_RES(_width, _height) \ + &(struct dlna_video_res) {.width = _width, .height = _height} \ + +#define DLNA_VIDEO_RES_RANGE(_wmin, _wmax, _hmin, _hmax) \ + &(struct dlna_video_res_range) {.width_min = _wmin, \ + .width_max = _wmax, .height_min = _hmin, \ + .height_max = _hmax} \ + +#define DLNA_BITRATE(_min, _max) \ + &(struct dlna_bitrate) {.min = _min, .max = _max} \ + +#define DLNA_LEVEL(_val...) \ + &(struct dlna_level) {.levels = {_val, NULL}} \ + +#define DLNA_PROFILE(_val...) \ + &(struct dlna_profile) {.profiles = {_val, NULL}} \ + +#define DLNA_AUDIO_RATE(_val...) \ + &(struct dlna_audio_rate) {.rates = {_val, INT32_MAX}} \ + +#define DLNA_VIDEO_PIXEL_ASPECT(_val...) \ + &(struct dlna_video_pixel_aspect) { \ + .pixel_aspect_ratio = {_val, NULL}} \ + +#define DLNA_VIDEO_FRAMERATE(_val...) \ + &(struct dlna_video_framerate) {.framerate = {_val, INT32_MAX}} \ + +#define DLNA_VIDEO_PACKETSIZE(_packet_size) \ + &(struct dlna_video_packet_size) {.packet_size = _packet_size} \ + +#define MAX_AUDIO_MPEG_VERSIONS 2 +#define MAX_AUDIO_RATES 9 +#define MAX_AUDIO_LEVELS 5 +#define MAX_VIDEO_RULE_LEVEL 12 +#define MAX_VIDEO_RULE_PROFILE 3 +#define MAX_VIDEO_RULE_FRAMERATE 7 +#define MAX_VIDEO_PIXEL_ASPECT 3 + +struct dlna_bitrate { + const unsigned int min; + const unsigned int max; +}; + +struct dlna_video_res { + const unsigned int width; + const unsigned int height; +}; + +struct dlna_video_res_range { + const unsigned int width_min; + const unsigned int width_max; + const unsigned int height_min; + const unsigned int height_max; +}; + +struct dlna_level { + const char *levels[MAX_VIDEO_RULE_LEVEL]; +}; + +struct dlna_profile { + const char *profiles[MAX_VIDEO_RULE_PROFILE]; +}; + +struct dlna_video_framerate { + const double framerate[MAX_VIDEO_RULE_FRAMERATE]; +}; + +struct dlna_video_pixel_aspect { + const char *pixel_aspect_ratio[MAX_VIDEO_PIXEL_ASPECT]; +}; + +struct dlna_video_rule { + const struct dlna_video_res *res; + const struct dlna_video_res_range *res_range; + const struct dlna_bitrate *bitrate; + const struct dlna_profile *profiles; + const struct dlna_level *levels; + const struct dlna_video_framerate *framerate; + const struct dlna_video_pixel_aspect *pixel_aspect; +}; + +struct dlna_audio_rate { + const unsigned int rates[MAX_AUDIO_RATES]; +}; + +struct dlna_audio_rule { + const struct lms_string_size *codec; + const struct lms_string_size *container; + const struct dlna_audio_rate *rates; + const struct dlna_level *levels; + const struct dlna_bitrate *channels; + const struct dlna_bitrate *bitrate; +}; + +struct dlna_video_packet_size { + const int64_t packet_size; +}; + +struct dlna_video_profile { + const struct lms_string_size *dlna_profile; + const struct lms_string_size *dlna_mime; + const struct dlna_video_rule *video_rules; + const struct dlna_audio_rule *audio_rule; + const struct lms_string_size *container; + const struct dlna_video_packet_size *packet_size; +}; + +struct dlna_audio_profile { + const struct lms_string_size *dlna_profile; + const struct lms_string_size *dlna_mime; + const struct dlna_audio_rule *audio_rule; +}; + +DECL_STR(_dlna_profile_mpeg_ts_sd_eu_iso, "MPEG_TS_SD_EU_ISO"); +DECL_STR(_dlna_profile_mpeg_ts_sd_eu, "MPEG_TS_SD_EU"); +DECL_STR(_dlna_profile_mpeg_ts_hd_na_iso, "MPEG_TS_HD_NA_ISO"); +DECL_STR(_dlna_profile_mpeg_ts_hd_na, "MPEG_TS_HD_NA"); +DECL_STR(_dlna_profile_mpeg_ts_sd_na_iso, "MPEG_TS_SD_NA_ISO"); +DECL_STR(_dlna_profile_mpeg_ts_sd_na, "MPEG_TS_SD_NA"); +DECL_STR(_dlna_profile_mpeg_ps_pal, "MPEG_PS_PAL"); +DECL_STR(_dlna_profile_mpeg_ps_ntsc, "MPEG_PS_NTSC"); +DECL_STR(_dlna_profile_mpeg1, "MPEG1"); +DECL_STR(_dlna_profile_mp3, "MP3"); +DECL_STR(_dlna_profile_mp3x, "MP3X"); + +DECL_STR(_dlna_mime_audio, "audio/mpeg"); +DECL_STR(_dlna_mime_video, "video/mpeg"); +DECL_STR(_dlna_mim_video_tts, "video/vnd.dlna.mpeg-tts"); + +DECL_STR(_container_mpegts, "mpegts"); +DECL_STR(_container_mpeg, "mpeg"); +DECL_STR(_container_mp3, "mp3"); + +static const struct dlna_video_rule _dlna_video_rule_mpeg_ps_pal[] = { + { + .res = DLNA_VIDEO_RES(720, 576), + .bitrate = DLNA_BITRATE(1, 9800000), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main"), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("64:45", "16:15"), + }, + { + .res = DLNA_VIDEO_RES(704, 576), + .bitrate = DLNA_BITRATE(1, 9800000), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main"), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("64:45", "16:15"), + }, + { + .res = DLNA_VIDEO_RES(544, 576), + .bitrate = DLNA_BITRATE(1, 9800000), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main"), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("32:17", "24:17"), + }, + { + .res = DLNA_VIDEO_RES(480, 576), + .bitrate = DLNA_BITRATE(1, 9800000), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main"), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("32:15", "8:5"), + }, + { + .res = DLNA_VIDEO_RES(352, 576), + .bitrate = DLNA_BITRATE(1, 9800000), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main"), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("32:11", "24:11"), + }, + { + .res = DLNA_VIDEO_RES(352, 288), + .bitrate = DLNA_BITRATE(1, 9800000), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main"), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("16:11", "12:11"), + }, + { NULL }, +}; + +static const struct dlna_video_rule _dlna_video_rule_mpeg1[] = { + { + .res = DLNA_VIDEO_RES(352, 288), + .bitrate = DLNA_BITRATE(1150000, 1152000), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + }, + { + .res = DLNA_VIDEO_RES(352, 240), + .bitrate = DLNA_BITRATE(1150000, 1152000), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + }, + { + .res = DLNA_VIDEO_RES(352, 240), + .bitrate = DLNA_BITRATE(1150000, 1152000), + .framerate = DLNA_VIDEO_FRAMERATE(24000/1001), + }, + { + .bitrate = DLNA_BITRATE(1150000, 1152000), + }, + { NULL }, +}; + +static const struct dlna_video_rule _dlna_video_rule_mpeg_ps_ntsc[] = { + { + .res = DLNA_VIDEO_RES(720, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .bitrate = DLNA_BITRATE(1, 9800000), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("32:27", "8:9"), + }, + { + .res = DLNA_VIDEO_RES(704, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .bitrate = DLNA_BITRATE(1, 9800000), + .framerate = DLNA_VIDEO_FRAMERATE(24000/1001, 24/1, 30000/1001, 30/1, + 60000/1001, 60/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("40:33", "10:11"), + }, + { + .res = DLNA_VIDEO_RES(480, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .bitrate = DLNA_BITRATE(1, 9800000), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("16:9", "4:3"), + }, + { + .res = DLNA_VIDEO_RES(544, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .bitrate = DLNA_BITRATE(1, 9800000), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("80:51", "20:17"), + }, + { + .res = DLNA_VIDEO_RES(352, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .bitrate = DLNA_BITRATE(1, 9800000), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("80:33", "20:11"), + }, + { + .res = DLNA_VIDEO_RES(352, 240), + .profiles = DLNA_PROFILE("simple", "main"), + .bitrate = DLNA_BITRATE(1, 9800000), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("16:11", "12:11"), + }, + { NULL }, +}; + +static const struct dlna_video_rule _dlna_video_rule_mpeg_ts_sd_na[] = { + { + .res = DLNA_VIDEO_RES(720, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("32:27", "8:9"), + }, + { + .res = DLNA_VIDEO_RES(704, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(24000/1001, 24/1, 30000/1001, 30/1, + 60000/1001, 60/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("40:33", "10:11"), + }, + { + .res = DLNA_VIDEO_RES(640, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(24000/1001, 24/1, 30000/1001, 30/1, + 60000/1001, 60/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("1:1", "4:3"), + }, + { + .res = DLNA_VIDEO_RES(480, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("16:9", "4:3"), + }, + { + .res = DLNA_VIDEO_RES(544, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("80:51", "20:17"), + }, + { + .res = DLNA_VIDEO_RES(352, 480), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("80:33", "20:11"), + }, + { NULL }, +}; + +static const struct dlna_video_rule _dlna_video_rule_mpeg_ts_hd_na[] = { + { + .res = DLNA_VIDEO_RES(1920, 1080), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001, 30/1, 24000/1001, 24/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("1:1", "9:16"), + }, + { + .res = DLNA_VIDEO_RES(1280, 720), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001, 30/1, 24000/1001, 24/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("1:1", "9:16"), + }, + { + .res = DLNA_VIDEO_RES(1080, 1440), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001, 30/1, 24000/1001, 24/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("4:3"), + }, + { + .res = DLNA_VIDEO_RES(1080, 1280), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(30000/1001, 30/1, 24000/1001, 24/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("3:2"), + }, + { NULL }, +}; + +static const struct dlna_video_rule _dlna_video_rule_mpeg_ts_sd_eu[] = { + { + .res = DLNA_VIDEO_RES(720, 576), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("64:45", "16:15"), + }, + { + .res = DLNA_VIDEO_RES(544, 576), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("32:17", "24:17"), + }, + { + .res = DLNA_VIDEO_RES(480, 576), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("32:15", "8:15"), + }, + { + .res = DLNA_VIDEO_RES(352, 576), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("32:11", "24:11"), + }, + { + .res = DLNA_VIDEO_RES(352, 288), + .profiles = DLNA_PROFILE("simple", "main"), + .levels = DLNA_LEVEL("low", "main", "high-1440", "high"), + .bitrate = DLNA_BITRATE(1, 18881700), + .framerate = DLNA_VIDEO_FRAMERATE(25/1), + .pixel_aspect = DLNA_VIDEO_PIXEL_ASPECT("16:11", "12:11"), + }, + { NULL }, +}; + +static const struct dlna_audio_rule _dlna_audio_rule_mpeg_ps = { + .rates = DLNA_AUDIO_RATE(48000), +}; + +static const struct dlna_audio_rule _dlna_audio_rule_mpeg1 = { + .bitrate = DLNA_BITRATE(224000, 224000), + .channels = DLNA_BITRATE(2, 2), + .rates = DLNA_AUDIO_RATE(44100), +}; + +static const struct dlna_audio_rule _dlna_audio_rule_mpeg_ts_sd_hd_na = { + .bitrate = DLNA_BITRATE(1, 448000), + .channels = DLNA_BITRATE(1, 6), + .rates = DLNA_AUDIO_RATE(48000), +}; + +static const struct dlna_audio_rule _dlna_audio_rule_mpeg_ts_sd_eu = { + .bitrate = DLNA_BITRATE(1, 448000), + .channels = DLNA_BITRATE(1, 6), + .rates = DLNA_AUDIO_RATE(32000, 44100, 48000), +}; + +static const struct dlna_video_profile _dlna_video_profile_rules[] = { + { + .dlna_profile = &_dlna_profile_mpeg_ps_pal, + .dlna_mime = &_dlna_mime_video, + .video_rules = _dlna_video_rule_mpeg_ps_pal, + .audio_rule = &_dlna_audio_rule_mpeg_ps, + .container = &_container_mpeg, + }, + { + .dlna_profile = &_dlna_profile_mpeg1, + .dlna_mime = &_dlna_mime_video, + .video_rules = _dlna_video_rule_mpeg1, + .audio_rule = &_dlna_audio_rule_mpeg1, + .container = &_container_mpeg, + }, + { + .dlna_profile = &_dlna_profile_mpeg_ps_ntsc, + .dlna_mime = &_dlna_mime_video, + .video_rules = _dlna_video_rule_mpeg_ps_ntsc, + .audio_rule = &_dlna_audio_rule_mpeg_ps, + .container = &_container_mpeg, + }, + { + .dlna_profile = &_dlna_profile_mpeg_ts_sd_na, + .dlna_mime = &_dlna_mim_video_tts, + .video_rules = _dlna_video_rule_mpeg_ts_sd_na, + .audio_rule = &_dlna_audio_rule_mpeg_ts_sd_hd_na, + .packet_size = DLNA_VIDEO_PACKETSIZE(192), + .container = &_container_mpegts, + }, + { + .dlna_profile = &_dlna_profile_mpeg_ts_sd_na_iso, + .dlna_mime = &_dlna_mime_video, + .video_rules = _dlna_video_rule_mpeg_ts_sd_na, + .audio_rule = &_dlna_audio_rule_mpeg_ts_sd_hd_na, + .packet_size = DLNA_VIDEO_PACKETSIZE(188), + .container = &_container_mpegts, + }, + { + .dlna_profile = &_dlna_profile_mpeg_ts_hd_na, + .dlna_mime = &_dlna_mim_video_tts, + .video_rules = _dlna_video_rule_mpeg_ts_hd_na, + .audio_rule = &_dlna_audio_rule_mpeg_ts_sd_hd_na, + .packet_size = DLNA_VIDEO_PACKETSIZE(192), + .container = &_container_mpegts, + }, + { + .dlna_profile = &_dlna_profile_mpeg_ts_hd_na_iso, + .dlna_mime = &_dlna_mime_video, + .video_rules = _dlna_video_rule_mpeg_ts_hd_na, + .audio_rule = &_dlna_audio_rule_mpeg_ts_sd_hd_na, + .packet_size = DLNA_VIDEO_PACKETSIZE(188), + .container = &_container_mpegts, + }, + { + .dlna_profile = &_dlna_profile_mpeg_ts_sd_eu, + .dlna_mime = &_dlna_mim_video_tts, + .video_rules = _dlna_video_rule_mpeg_ts_sd_eu, + .audio_rule = &_dlna_audio_rule_mpeg_ts_sd_eu, + .packet_size = DLNA_VIDEO_PACKETSIZE(192), + .container = &_container_mpegts, + }, + { + .dlna_profile = &_dlna_profile_mpeg_ts_sd_eu_iso, + .dlna_mime = &_dlna_mime_video, + .video_rules = _dlna_video_rule_mpeg_ts_sd_eu, + .audio_rule = &_dlna_audio_rule_mpeg_ts_sd_eu, + .packet_size = DLNA_VIDEO_PACKETSIZE(188), + .container = &_container_mpegts, + }, +}; + +static const struct dlna_audio_rule _dlna_audio_rule_mp3 = { + .container = &_container_mp3, + .rates = DLNA_AUDIO_RATE(32000, 44100, 48000), + .bitrate = DLNA_BITRATE(320000, 320000), + .channels = DLNA_BITRATE(1, 2), +}; + +static const struct dlna_audio_rule _dlna_audio_rule_mp3x = { + .container = &_container_mp3, + .rates = DLNA_AUDIO_RATE(16000, 22050, 24000, 32000, 44100, 48000), + .bitrate = DLNA_BITRATE(8000, 320000), + .channels = DLNA_BITRATE(1, 2), +}; + +static const struct dlna_audio_profile _dlna_audio_profile_rules[] = { + { + .dlna_profile = &_dlna_profile_mp3, + .dlna_mime = &_dlna_mime_audio, + .audio_rule = &_dlna_audio_rule_mp3, + }, + { + .dlna_profile = &_dlna_profile_mp3x, + .dlna_mime = &_dlna_mime_audio, + .audio_rule = &_dlna_audio_rule_mp3x, + }, + {NULL}, +}; + +static bool +_uint_vector_has_value(const unsigned int *list, unsigned int wanted) +{ + int i; + unsigned int curr; + + for (i = 0, curr = list[i]; curr != INT32_MAX; i++, curr = list[i]) + if (curr == wanted) return true; + + return false; +} + +static bool +_double_vector_has_value(const double *list, const double wanted) +{ + int i; + double curr; + + for (i = 0, curr = list[i]; (int)curr != INT32_MAX; i++, curr = list[i]) { + if (!(curr < wanted && curr > wanted)) return true; + } + + return false; +} + +static bool +_string_vector_has_value(const char **list, const char *wanted) +{ + int i; + const char *curr; + + for (i = 0, curr = list[i]; curr != NULL; i++, curr = list[i]) { + if (!strcmp(curr, wanted)) return true; + } + + return false; +} + +static const struct dlna_video_profile * +_get_video_dlna_profile(const struct lms_stream_audio_info *audio, + const struct lms_stream *audio_stream, + const struct lms_stream_video_info *video, + const struct lms_stream *video_stream, + int64_t packet_size, + struct lms_string_size *container) +{ + int i, length; + const struct dlna_video_profile *curr; + char *level, *profile, *profile_level; + + profile_level = strstr(video_stream->codec.str, "-p"); + level = strstr(profile_level, "-l"); + + profile = calloc(1, sizeof(char) * (sizeof(level) - 2)); + strncpy(profile, profile_level, sizeof(level) - 2); + + length = sizeof(_dlna_video_profile_rules) / sizeof(struct dlna_video_profile); + + for (i = 0, curr = &_dlna_video_profile_rules[i]; i < length; i++, + curr = &_dlna_video_profile_rules[i]) { + const struct dlna_video_rule *video_rule; + const struct dlna_audio_rule *audio_rule; + const struct dlna_video_rule *r; + + audio_rule = curr->audio_rule; + r = curr->video_rules; + + if (curr->container && + strcmp(curr->container->str, container->str)) + continue; + + if (curr->packet_size && + curr->packet_size->packet_size != packet_size) + continue; + + if (audio) { + + if (audio_rule->bitrate && + (audio->bitrate < audio_rule->bitrate->min || + audio->bitrate > audio_rule->bitrate->max)) { + continue; + } + + if (audio_rule->rates && + !_uint_vector_has_value(audio_rule->rates->rates, + audio->sampling_rate)) + continue; + + if (audio_rule->channels && + (audio->channels < audio_rule->channels->min || + audio->channels > audio_rule->channels->max)) + continue; + } + + if (audio_stream && audio_rule->codec && + strcmp(audio_stream->codec.str, audio_rule->codec->str)) + continue; + + while (true) { + video_rule = r++; + + if (!video_rule->res && !video_rule->bitrate && !video_rule->levels) + break; + + if (video_rule->res && (video->width != video_rule->res->width && + video->height != video_rule->res->height)) + continue; + + if (video_rule->res_range && + !(video->width >= video_rule->res_range->width_min && + video->width <= video_rule->res_range->width_max && + video->height >= video_rule->res_range->height_min && + video->height <= video_rule->res_range->height_max)) + continue; + + if (video_rule->framerate && + !_double_vector_has_value(video_rule->framerate->framerate, + video->framerate)) + continue; + + if (video_rule->pixel_aspect && !_string_vector_has_value + ((const char **)video_rule->pixel_aspect->pixel_aspect_ratio, + video->aspect_ratio.str)) + continue; + + if (video_rule->bitrate && + !(video->bitrate >= video_rule->bitrate->min && + video->bitrate <= video_rule->bitrate->max)) + continue; + + if (video_rule->levels && + !_string_vector_has_value + ((const char **)video_rule->levels->levels, level + 2)) + continue; + + if (video_rule->profiles && + !_string_vector_has_value + ((const char **)video_rule->profiles->profiles, profile + 2)) + continue; + + free(profile); + return curr; + } + } + + free(profile); + return NULL; +} + +static void +_fill_video_dlna_profile(struct lms_video_info *info, + int64_t packet_size, + struct lms_string_size *container) +{ + const struct dlna_video_profile *profile; + const struct lms_stream *s, *audio_stream, *video_stream; + const struct lms_stream_audio_info *audio; + const struct lms_stream_video_info *video; + + audio_stream = video_stream = NULL; + audio = NULL; + video = NULL; + + for (s = info->streams; s; s = s->next) { + if (s->type == LMS_STREAM_TYPE_VIDEO) { + video = &s->video; + video_stream = s; + if (audio) break; + } else if (s->type == LMS_STREAM_TYPE_AUDIO) { + audio = &s->audio; + audio_stream = s; + if (video) break; + } + } + + profile = _get_video_dlna_profile(audio, audio_stream, video, + video_stream, packet_size, container); + if (!profile) return; + + info->dlna_profile = *profile->dlna_profile; + info->dlna_mime = *profile->dlna_mime; +} + +static void +_fill_audio_dlna_profile(struct lms_audio_info *info) +{ + int i = 0; + + while (true) { + const struct dlna_audio_profile *curr = _dlna_audio_profile_rules + i; + const struct dlna_audio_rule *rule; + + if (curr->dlna_profile == NULL && curr->dlna_mime == NULL && + curr->audio_rule == NULL) + break; + + i++; + rule = curr->audio_rule; + + if (rule->bitrate && info->bitrate && + (info->bitrate < rule->bitrate->min || + info->bitrate > rule->bitrate->max)) + continue; + + if (rule->rates && + !_uint_vector_has_value(rule->rates->rates, + info->sampling_rate)) + continue; + + if (rule->channels && + (info->channels < rule->channels->min || + info->channels > rule->channels->max)) + continue; + + if (rule->codec && strcmp(rule->codec->str, info->codec.str)) + continue; + + if (rule->container && strcmp(rule->container->str, info->container.str)) + continue; + + info->dlna_mime = *curr->dlna_mime; + info->dlna_profile = *curr->dlna_profile; + break; + } +} + +static void * +_match(struct plugin *p, const char *path, int len, int base) +{ + long i; + + i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts)); + if (i < 0) + return NULL; + else + return (void*)(i + 1); +} + +static char * +_get_dict_value(AVDictionary *dict, const char *key) +{ + AVDictionaryEntry *tag = NULL; + tag = av_dict_get(dict, key, tag, AV_DICT_IGNORE_SUFFIX); + if (!tag) return NULL; + return tag->value; +} + +static int +_parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match) +{ + int ret; + int64_t packet_size = 0; + unsigned int i; + AVFormatContext *fmt_ctx = NULL; + struct mpeg_info info = { }; + char *metadata; + struct lms_audio_info audio_info = { }; + struct lms_video_info video_info = { }; + struct lms_string_size container = { }; + bool video = false; + + if ((ret = avformat_open_input(&fmt_ctx, finfo->path, NULL, NULL))) + return ret; + + if (avformat_find_stream_info(fmt_ctx, NULL) < 0) + goto fail; + + metadata = _get_dict_value(fmt_ctx->metadata, "title"); + if (metadata) + lms_string_size_strndup(&info.title, metadata, -1); + + metadata = _get_dict_value(fmt_ctx->metadata, "artist"); + if (metadata) + lms_string_size_strndup(&info.artist, metadata, -1); + + metadata = _get_dict_value(fmt_ctx->metadata, "album"); + if (metadata) + lms_string_size_strndup(&info.album, metadata, -1); + + metadata = _get_dict_value(fmt_ctx->metadata, "genre"); + if (metadata) + lms_string_size_strndup(&info.genre, metadata, -1); + + av_opt_get_int(fmt_ctx, "ts_packetsize", AV_OPT_SEARCH_CHILDREN, + &packet_size); + + for (i = 0; i < fmt_ctx->nb_streams; i++) { + AVStream *stream = fmt_ctx->streams[i]; + AVCodec *codec; + + if (stream->codec->codec_id == AV_CODEC_ID_PROBE) { + printf("Failed to probe codec for input stream %d\n", stream->index); + } else if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) { + printf("Unsupported codec with id %d for input stream %d\n", + stream->codec->codec_id, stream->index); + } else { + if (stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + audio_info.bitrate = stream->codec->bit_rate; + audio_info.channels = av_get_channel_layout_nb_channels + (stream->codec->channel_layout); + audio_info.sampling_rate = stream->codec->sample_rate; + } else if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + const char *codec_name, *str_profile, *str_level; + char buf[256], aspect_ratio[256]; + const char *language; + struct lms_stream *s; + + s = calloc(1, sizeof(*s)); + s->stream_id = (unsigned int)stream->id; + + language = _get_dict_value(fmt_ctx->metadata, "language"); + if (metadata) + lms_string_size_strndup(&s->lang, language, -1); + + s->type = LMS_STREAM_TYPE_VIDEO; + video = true; + + codec_name = avcodec_get_name(stream->codec->codec_id); + if (codec_name) { + str_profile = NULL; + str_level = NULL; + + switch (stream->codec->profile) { + case 6: + str_profile = "simple"; + break; + case 4: + str_profile = "main"; + break; + } + + switch (stream->codec->level) { + case 5: + case 8: + str_level = "main"; + break; + case 2: + case 4: + str_level = "high"; + break; + case 6: + str_level = "high"; + break; + } + + snprintf(buf, sizeof(buf), "%s-p%s-l%s", + codec_name, str_profile, str_level); + + lms_string_size_strndup(&s->codec, buf, -1); + } + + s->video.bitrate = stream->codec->bit_rate; + if (!s->video.bitrate) + s->video.bitrate = stream->codec->rc_max_rate; + + s->video.width = stream->codec->width; + s->video.height = stream->codec->height; + + + if (stream->r_frame_rate.den) + s->video.framerate = stream->r_frame_rate.num / + stream->r_frame_rate.den; + + snprintf(aspect_ratio, sizeof(aspect_ratio), "%d:%d", + stream->codec->sample_aspect_ratio.num, + stream->codec->sample_aspect_ratio.den); + + lms_string_size_strndup(&s->video.aspect_ratio, + aspect_ratio, -1); + + s->next = video_info.streams; + video_info.streams = s; + } + } + } + + lms_string_size_strip_and_free(&info.title); + lms_string_size_strip_and_free(&info.artist); + lms_string_size_strip_and_free(&info.album); + lms_string_size_strip_and_free(&info.genre); + + if (!info.title.str) + lms_name_from_path(&info.title, finfo->path, finfo->path_len, + finfo->base, _exts[((long) match) - 1].len, + NULL); + + if (info.title.str) + lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len); + if (info.artist.str) + lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len); + if (info.album.str) + lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len); + if (info.genre.str) + lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len); + + + lms_string_size_strndup(&container, fmt_ctx->iformat->name, -1); + + if (video) { + video_info.id = finfo->id; + video_info.title = info.title; + video_info.artist = info.artist; + video_info.container = container; + + _fill_video_dlna_profile(&video_info, packet_size, &container); + ret = lms_db_video_add(plugin->video_db, &video_info); + } else { + audio_info.id = finfo->id; + audio_info.title = info.title; + audio_info.artist = info.artist; + audio_info.album = info.album; + audio_info.genre = info.genre; + audio_info.container = container; + + _fill_audio_dlna_profile(&audio_info); + ret = lms_db_audio_add(plugin->audio_db, &audio_info); + } + + free(info.title.str); + free(info.artist.str); + free(info.album.str); + free(info.genre.str); + free(container.str); + + while (video_info.streams) { + struct lms_stream *s = video_info.streams; + video_info.streams = s->next; + free(s->codec.str); + if (s->type == LMS_STREAM_TYPE_VIDEO) { + free(s->video.aspect_ratio.str); + } + free(s->lang.str); + free(s); + } + + fail: + avformat_close_input(&fmt_ctx); + return ret; +} + +static int +_close(struct plugin *plugin) +{ + free(plugin); + return 0; +} + +static int +_setup(struct plugin *plugin, struct lms_context *ctxt) +{ + av_register_all(); + plugin->audio_db = lms_db_audio_new(ctxt->db); + if (!plugin->audio_db) + return -1; + plugin->video_db = lms_db_video_new(ctxt->db); + if (!plugin->video_db) + return -1; + + return 0; +} + +static int +_start(struct plugin *plugin, struct lms_context *ctxt) +{ + int r; + r = lms_db_audio_start(plugin->audio_db); + r |= lms_db_video_start(plugin->video_db); + return r; +} + +static int +_finish(struct plugin *plugin, struct lms_context *ctxt) +{ + if (plugin->audio_db) + lms_db_audio_free(plugin->audio_db); + if (plugin->video_db) + lms_db_video_free(plugin->video_db); + + return 0; +} + +API struct lms_plugin * +lms_plugin_open(void) +{ + struct plugin *plugin; + + plugin = (struct plugin *)malloc(sizeof(*plugin)); + plugin->plugin.name = _name; + plugin->plugin.match = (lms_plugin_match_fn_t)_match; + plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse; + plugin->plugin.close = (lms_plugin_close_fn_t)_close; + plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup; + plugin->plugin.start = (lms_plugin_start_fn_t)_start; + plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish; + plugin->plugin.order = INT32_MAX; + + return (struct lms_plugin *)plugin; +} + +API const struct lms_plugin_info * +lms_plugin_info(void) +{ + static struct lms_plugin_info info = { + _name, + _cats, + "libavcodec", + PACKAGE_VERSION, + _authors, + "http://github.com/profusion/lightmediascanner" + }; + + return &info; +} -- 2.7.4