diff -Nru lightmediascanner-0.4.5.0~201309060911/README lightmediascanner-0.4.5.0~201401231350/README --- lightmediascanner-0.4.5.0~201309060911/README 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/README 2013-12-09 17:51:42.000000000 +0000 @@ -17,3 +17,41 @@ parsing files that are already up-to-date. This SQLite connection and the file id within the master table 'files' are handled to plugins for relationship with other tables. + + +DAEMON +~~~~~~ + +LMS provides `lightmediascannerd', a D-Bus daemon that uses the library +and provides a common use case in a centralized way. It will scan +directories based on some categories (defaults to FreeDesktop.Org/XDG +directories specification, but can be manually specified in the +command line), manages a DataBase WriteLock (remember SQLite3 will +produce annoying 'database is locked' errors if writes are done by one +process while another is using it) and information of database changes +through properties. + +Service Information: + * Well Known Name: org.lightmediascanner + * Path: /org/lightmediascanner/Scanner1 + * Interfaces: org.lightmediascanner.Scanner1, org.freedesktop.DBus.Properties, + org.freedesktop.DBus.Introspectable + +There is a tool `lightmediascannerctl' to print server status, monitor +properties (useful to see WriteLocked, IsScanning and UpdateID), +request write lock or request scan. + +Both tools are written using glib and it's gio/gdbus, so they depend +on these libraries during both compile and runtime. + +NOTE: + + If you're installing to custom directories (~/usr, /opt, etc) make + sure the service file is installed in a directory known to + dbus-daemon, usually /usr/share/dbus-1/services or + ~/.local/share/dbus-1/services. Example: + + ./configure \ + --prefix=$HOME/lms \ + --enable-daemon \ + --with-dbus-services=$HOME/.local/share/dbus-1/services diff -Nru lightmediascanner-0.4.5.0~201309060911/configure.ac lightmediascanner-0.4.5.0~201401231350/configure.ac --- lightmediascanner-0.4.5.0~201309060911/configure.ac 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/configure.ac 2013-12-09 17:51:42.000000000 +0000 @@ -19,7 +19,7 @@ AC_C_CONST define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl -AC_PROG_LIBTOOL +LT_INIT([disable-static]) VMAJ=`echo $PACKAGE_VERSION | awk -F. '{printf("%s", $1);}'` VMIN=`echo $PACKAGE_VERSION | awk -F. '{printf("%s", $2);}'` @@ -65,32 +65,25 @@ AM_CONDITIONAL(HAVE_MP4V2, false) define([CHECK_MODULE_MP4], [ - AC_CHECK_HEADERS(mp4.h, HAVE_MP4_HEADERS=yes, HAVE_MP4_HEADERS=no) AC_CHECK_HEADERS(mp4v2/mp4v2.h, HAVE_MP4V2_HEADERS=yes, HAVE_MP4V2_HEADERS=no) - if test "x$HAVE_MP4_HEADERS" = "xyes" -o "x$HAVE_MP4V2_HEADERS" = "xyes"; then + if test "x$HAVE_MP4V2_HEADERS" = "xyes"; then AC_CHECK_LIB(mp4v2, MP4Read, [MP4=true], [MP4=false]) MP4V2_LIBS="-lmp4v2" AC_SUBST(MP4V2_LIBS) - if test "x$HAVE_MP4V2_HEADERS" = "xyes"; then - AC_DEFINE(HAVE_MP4V2, 1, Define if mp4v2.h is present) - fi - if test "x$HAVE_MP4_HEADERS" = "xyes"; then - AC_DEFINE(HAVE_MP4, 1, Define if mp4.h is present) - fi - - # test for new 2.0 api - if test "x$HAVE_MP4V2_HEADERS" = "xyes"; then - AC_MSG_CHECKING([mp4v2 2.0 API (MP4Read and MP4Close)]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM( - [[#include ]], - [[MP4FileHandle fh = MP4Read("/tmp"); - MP4Close(fh, 0); - ]])], - [ - AC_MSG_RESULT([yes]) - AC_DEFINE(HAVE_MP4V2_2_0_API, 1, Define to 1 if you have mp4v2 2.0 api) - ], [AC_MSG_RESULT([no])]) - fi + + # strict about new 2.0 api + AC_MSG_CHECKING([mp4v2 2.0 API (MP4Read and MP4Close)]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[#include ]], + [[MP4FileHandle fh = MP4Read("/tmp"); + MP4Close(fh, 0); + ]])], + [ + MP4=true + AC_MSG_RESULT([yes]) + ], [ + MP4=false + AC_MSG_RESULT([no])]) else MP4=false fi diff -Nru lightmediascanner-0.4.5.0~201309060911/debian/changelog lightmediascanner-0.4.5.0~201401231350/debian/changelog --- lightmediascanner-0.4.5.0~201309060911/debian/changelog 2013-09-06 05:11:16.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/debian/changelog 2014-01-23 09:50:33.000000000 +0000 @@ -1,4 +1,4 @@ -lightmediascanner (0.4.5.0~201309060911-1ppa1~raring) raring; urgency=high +lightmediascanner (0.4.5.0~201401231350-1ppa1~raring) raring; urgency=high * Autobuild. diff -Nru lightmediascanner-0.4.5.0~201309060911/src/bin/lightmediascannerctl.c lightmediascanner-0.4.5.0~201401231350/src/bin/lightmediascannerctl.c --- lightmediascanner-0.4.5.0~201309060911/src/bin/lightmediascannerctl.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/bin/lightmediascannerctl.c 2013-12-09 17:51:42.000000000 +0000 @@ -4,6 +4,47 @@ #include #include +static gboolean +start_service_by_name(void) +{ + GError *error = NULL; + GVariant *var; + GDBusConnection *conn; + + conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); + if (error) { + fprintf(stderr, "Could not get session bus connection: %s\n", + error->message); + g_error_free(error); + return FALSE; + } + + var = g_dbus_connection_call_sync(conn, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "StartServiceByName", + g_variant_new("(su)", + "org.lightmediascanner", 0), + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + 10000, + NULL, + &error); + g_object_unref(conn); + if (var) + g_variant_unref(var); + + if (error) { + fprintf(stderr, "Could not start org.lightmediascanner: %s\n", + error->message); + g_error_free(error); + return FALSE; + } + + return TRUE; +} + struct app { int ret; int argc; @@ -237,8 +278,10 @@ app->ret = EXIT_SUCCESS; g_main_loop_quit(app->loop); } + g_variant_unref(value); break; - } + } else + g_variant_unref(value); } g_variant_iter_free(itr); } @@ -257,6 +300,30 @@ } static void +on_signal(GDBusProxy *proxy, gchar *sender, gchar *signal, GVariant *params, gpointer user_data) +{ + if (g_str_equal(signal, "ScanProgress")) { + const gchar *category = NULL, *path = NULL; + guint64 uptodate = 0, processed = 0, deleted = 0, + skipped = 0, errors = 0; + + g_variant_get(params, "(&s&sttttt)", + &category, + &path, + &uptodate, + &processed, + &deleted, + &skipped, + &errors); + + printf("Scan Progress %s:%s uptodate=%"G_GUINT64_FORMAT", " + "processed=%"G_GUINT64_FORMAT", deleted=%"G_GUINT64_FORMAT", " + "skipped=%"G_GUINT64_FORMAT", errors=%"G_GUINT64_FORMAT"\n", + category, path, uptodate, processed, deleted, skipped, errors); + } +} + +static void populate_scan_params(gpointer key, gpointer value, gpointer user_data) { const char *category = key; @@ -303,6 +370,9 @@ g_signal_connect(app->proxy, "g-properties-changed", G_CALLBACK(on_properties_changed_check_scan), app); + g_signal_connect(app->proxy, "g-signal", + G_CALLBACK(on_signal), + app); categories = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, do_free_array); for (i = 0; i < app->argc; i++) { @@ -451,6 +521,9 @@ return EXIT_FAILURE; } + if (!start_service_by_name()) + return EXIT_FAILURE; + app.timer = NULL; app.loop = g_main_loop_new(NULL, FALSE); app.proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, diff -Nru lightmediascanner-0.4.5.0~201309060911/src/bin/lightmediascannerd.c lightmediascanner-0.4.5.0~201401231350/src/bin/lightmediascannerd.c --- lightmediascanner-0.4.5.0~201309060911/src/bin/lightmediascannerd.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/bin/lightmediascannerd.c 2013-12-09 17:51:42.000000000 +0000 @@ -11,8 +11,10 @@ static GHashTable *categories = NULL; static int commit_interval = 100; static int slave_timeout = 60; +static int delete_older_than = 30; static gboolean vacuum = FALSE; static gboolean startup_scan = FALSE; +static gboolean omit_scan_progress = FALSE; static GDBusNodeInfo *introspection_data = NULL; @@ -22,6 +24,7 @@ static const char introspection_xml[] = "" " " + " " " " " " " " @@ -32,6 +35,15 @@ " " " " " " + " " + " " + " " + " " + " " + " " + " " + " " + " " " " ""; @@ -48,6 +60,34 @@ GList *paths; } scanner_pending_t; +typedef struct scan_progress { + GDBusConnection *conn; + gchar *category; + gchar *path; + guint64 uptodate; + guint64 processed; + guint64 deleted; + guint64 skipped; + guint64 errors; + time_t last_report_time; + gint updated; +} scan_progress_t; + +/* Scan progress signals will be issued if the time since last + * emission is greated than SCAN_PROGRESS_UPDATE_TIMEOUT _and_ number + * of items is greater than the SCAN_PROGRESS_UPDATE_COUNT. + * + * Be warned that D-Bus signal will wake-up the dbus-daemon (unless + * k-dbus) and all listener clients, which may hurt scan performance, + * thus we keep these good enough for GUI to look responsive while + * conservative to not hurt performance. + * + * Note that at after a path is scanned (check/progress) the signal is + * emitted even if count or timeout didn't match. + */ +#define SCAN_PROGRESS_UPDATE_TIMEOUT 1 /* in seconds */ +#define SCAN_PROGRESS_UPDATE_COUNT 50 /* in number of items */ + typedef struct scanner { GDBusConnection *conn; char *write_lock; @@ -56,6 +96,7 @@ GList *pending_scan; /* of scanner_pending_t, see scanner_thread_work */ GThread *thread; /* see scanner_thread_work */ unsigned cleanup_thread_idler; /* see scanner_thread_work */ + scan_progress_t *scan_progress; guint64 update_id; struct { unsigned idler; /* not a flag, but g_source tag */ @@ -146,6 +187,47 @@ } static void +do_delete_old(void) +{ + const char sql[] = "DELETE FROM files WHERE dtime > 0 and dtime <= ?"; + sqlite3 *db; + sqlite3_stmt *stmt; + gint64 dtime; + int ret; + + ret = sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READWRITE, NULL); + if (ret != SQLITE_OK) { + g_warning("Couldn't open '%s': %s", db_path, sqlite3_errmsg(db)); + goto end; + } + + if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) { + g_warning("Couldn't prepare delete old from %s: %s", + db_path, sqlite3_errmsg(db)); + goto end; + } + + dtime = (gint64)time(NULL) - delete_older_than * (24 * 60 * 60); + if (sqlite3_bind_int64(stmt, 1, dtime) != SQLITE_OK) { + g_warning("Couldn't bind delete old dtime '%"G_GINT64_FORMAT + "'from %s: %s", dtime, db_path, sqlite3_errmsg(db)); + goto cleanup; + } + + ret = sqlite3_step(stmt); + if (ret != SQLITE_DONE) + g_warning("Couldn't run SQL delete old dtime '%"G_GINT64_FORMAT + "', ret=%d: %s", dtime, ret, sqlite3_errmsg(db)); + +cleanup: + sqlite3_reset(stmt); + sqlite3_finalize(stmt); + +end: + sqlite3_close(db); +} + +static void do_vacuum(void) { const char sql[] = "VACUUM"; @@ -499,12 +581,87 @@ scanner->changed_props.is_scanning = TRUE; } +static gboolean +report_scan_progress(gpointer data) +{ + scan_progress_t *sp = data; + GError *error = NULL; + + g_dbus_connection_emit_signal(sp->conn, + NULL, + BUS_PATH, + BUS_IFACE, + "ScanProgress", + g_variant_new("(ssttttt)", + sp->category, + sp->path, + sp->uptodate, + sp->processed, + sp->deleted, + sp->skipped, + sp->errors), + &error); + g_assert_no_error(error); + + return FALSE; +} + +static gboolean +report_scan_progress_and_free(gpointer data) +{ + scan_progress_t *sp = data; + + if (sp->updated) + report_scan_progress(sp); + + g_object_unref(sp->conn); + g_free(sp->category); + g_free(sp->path); + g_free(sp); + return FALSE; +} + static void scan_progress_cb(lms_t *lms, const char *path, int pathlen, lms_progress_status_t status, void *data) { const scanner_t *scanner = data; + scan_progress_t *sp = scanner->scan_progress; + if (scanner->pending_stop) lms_stop_processing(lms); + + if (!sp) + return; + + switch (status) { + case LMS_PROGRESS_STATUS_UP_TO_DATE: + sp->uptodate++; + break; + case LMS_PROGRESS_STATUS_PROCESSED: + sp->processed++; + break; + case LMS_PROGRESS_STATUS_DELETED: + sp->deleted++; + break; + case LMS_PROGRESS_STATUS_KILLED: + case LMS_PROGRESS_STATUS_ERROR_PARSE: + case LMS_PROGRESS_STATUS_ERROR_COMM: + sp->errors++; + break; + case LMS_PROGRESS_STATUS_SKIPPED: + sp->skipped++; + break; + } + + sp->updated++; + if (sp->updated > SCAN_PROGRESS_UPDATE_COUNT) { + time_t now = time(NULL); + if (sp->last_report_time + SCAN_PROGRESS_UPDATE_TIMEOUT < now) { + sp->last_report_time = now; + sp->updated = 0; + g_idle_add(report_scan_progress, sp); + } + } } static lms_t * @@ -632,6 +789,7 @@ if (lms) { while (pending->paths) { char *path; + scan_progress_t *sp = NULL; if (scanner->pending_stop) break; @@ -641,12 +799,24 @@ pending->paths); g_debug("scan category %s, path %s", pending->category, path); + + if (!omit_scan_progress) { + sp = g_new0(scan_progress_t, 1); + sp->conn = g_object_ref(scanner->conn); + sp->category = g_strdup(pending->category); + sp->path = g_strdup(path); + scanner->scan_progress = sp; + } + if (!scanner->pending_stop) lms_check(lms, path); if (!scanner->pending_stop && g_file_test(path, G_FILE_TEST_EXISTS)) lms_process(lms, path); + if (sp) + g_idle_add(report_scan_progress_and_free, sp); + g_free(path); } lms_free(lms); @@ -657,6 +827,12 @@ g_debug("finished scanner thread"); + if (delete_older_than >= 0) { + g_debug("Delete from DB files with dtime older than %d days.", + delete_older_than); + do_delete_old(); + } + if (vacuum) { GTimer *timer = g_timer_new(); @@ -802,7 +978,9 @@ scanner_t *scanner = data; GVariant *ret; - if (strcmp(prop, "IsScanning") == 0) + if (strcmp(prop, "DataBasePath") == 0) + ret = g_variant_new_string(db_path); + else if (strcmp(prop, "IsScanning") == 0) ret = g_variant_new_boolean(scanner->thread != NULL); else if (strcmp(prop, "WriteLocked") == 0) ret = g_variant_new_boolean(check_write_locked(scanner)); @@ -1154,10 +1332,26 @@ "Number of seconds to wait for slave to reply, otherwise kills it. " "Defaults to 60.", "SECONDS"}, + {"delete-older-than", 'd', 0, G_OPTION_ARG_INT, &delete_older_than, + "Delete from database files that have 'dtime' older than the given " + "number of DAYS. If not specified LightMediaScanner will keep the " + "files in the database even if they are gone from the file system " + "and if they appear again and have the same 'mtime' and 'size' " + "it will be restored ('dtime' set to 0) without the need to parse " + "the file again (much faster). This is useful for removable media. " + "Use a negative number to disable this behavior. " + "Defaults to 30.", + "DAYS"}, {"vacuum", 'V', 0, G_OPTION_ARG_NONE, &vacuum, "Execute SQL VACUUM after every scan.", NULL}, {"startup-scan", 'S', 0, G_OPTION_ARG_NONE, &startup_scan, "Execute full scan on startup.", NULL}, + {"omit-scan-progress", 0, 0, G_OPTION_ARG_NONE, &omit_scan_progress, + "Omit the ScanProgress signal during scans. This will avoid the " + "overhead of D-Bus signal emission and may slightly improve the " + "performance, but will make the listener user-interfaces less " + "responsive as they won't be able to tell the user what is happening.", + NULL}, {"charset", 'C', 0, G_OPTION_ARG_STRING_ARRAY, &charsets, "Extra charset to use. (Multiple use)", "CHARSET"}, {"parser", 'P', 0, G_OPTION_ARG_STRING_ARRAY, &parsers, @@ -1269,6 +1463,7 @@ g_debug("db-path: %s", db_path); g_debug("commit-interval: %d files", commit_interval); g_debug("slave-timeout: %d seconds", slave_timeout); + g_debug("delete-older-than: %d days", delete_older_than); if (charsets) { char *tmp = g_strjoinv(", ", charsets); diff -Nru lightmediascanner-0.4.5.0~201309060911/src/lib/Makefile.am lightmediascanner-0.4.5.0~201401231350/src/lib/Makefile.am --- lightmediascanner-0.4.5.0~201309060911/src/lib/Makefile.am 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/lib/Makefile.am 2013-12-09 17:51:42.000000000 +0000 @@ -29,9 +29,3 @@ liblightmediascanner_la_LIBADD = -ldl @SQLITE3_LIBS@ @LTLIBICONV@ liblightmediascanner_la_LDFLAGS = -version-info @version_info@ - -# shared sources/headers -noinst_LTLIBRARIES = shared/libshared.la -shared_libshared_la_SOURCES = shared/util.c - -noinst_HEADERS += shared/util.h diff -Nru lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_db.h lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_db.h --- lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_db.h 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_db.h 2013-12-09 17:51:42.000000000 +0000 @@ -136,6 +136,7 @@ }; struct lms_stream_audio_info { + unsigned int sampling_rate; unsigned int bitrate; uint8_t channels; }; @@ -156,6 +157,7 @@ int64_t id; struct lms_string_size title; struct lms_string_size artist; + struct lms_string_size container; struct lms_string_size dlna_profile; struct lms_string_size dlna_mime; unsigned int length; @@ -169,6 +171,8 @@ API int lms_db_video_free(lms_db_video_t *ldv) GNUC_NON_NULL(1); API int lms_db_video_add(lms_db_video_t *ldv, struct lms_video_info *info) GNUC_NON_NULL(1, 2); + API int lms_stream_video_info_aspect_ratio_guess(struct lms_stream_video_info *info) GNUC_NON_NULL(1); + /* Playlist Records */ struct lms_playlist_info { int64_t id; diff -Nru lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_db_video.c lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_db_video.c --- lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_db_video.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_db_video.c 2013-12-09 17:51:42.000000000 +0000 @@ -113,6 +113,7 @@ r = sqlite3_exec( db, "BEGIN TRANSACTION;" + "ALTER TABLE videos ADD COLUMN container TEXT DEFAULT NULL;" "ALTER TABLE videos ADD COLUMN dlna_profile TEXT DEFAULT NULL;" "ALTER TABLE videos ADD COLUMN dlna_mime TEXT DEFAULT NULL;" "COMMIT;", @@ -163,6 +164,7 @@ "codec TEXT, " "lang TEXT, " "channels INTEGER, " + "sampling_rate INTEGER, " "bitrate INTEGER)", NULL, NULL, &errmsg); if (r != SQLITE_OK) { @@ -322,8 +324,8 @@ ldv->insert = lms_db_compile_stmt(ldv->db, "INSERT OR REPLACE INTO videos (id, title, artist, length, " - "dlna_profile, dlna_mime) " - "VALUES (?, ?, ?, ?, ?, ?)"); + "container, dlna_profile, dlna_mime) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"); if (!ldv->insert) return -2; @@ -337,8 +339,8 @@ ldv->insert_audio_streams = lms_db_compile_stmt( ldv->db, "INSERT OR REPLACE INTO videos_audios (" - "video_id, stream_id, codec, lang, channels, bitrate) VALUES (" - "?, ?, ?, ?, ?, ?)"); + "video_id, stream_id, codec, lang, channels, sampling_rate, bitrate) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"); if (!ldv->insert_audio_streams) return -1; @@ -444,6 +446,7 @@ lms_db_bind_text(stmt, ++col, s->codec.str, s->codec.len) || lms_db_bind_text(stmt, ++col, s->lang.str, s->lang.len) || lms_db_bind_int(stmt, ++col, s->audio.channels) || + lms_db_bind_int(stmt, ++col, s->audio.sampling_rate) || lms_db_bind_int(stmt, ++col, s->audio.bitrate)) { fprintf(stderr, "ERROR: Failed to bind value to column %d\n", col); ret = -1; @@ -529,12 +532,17 @@ if (ret != 0) goto done; - ret = lms_db_bind_text(stmt, 5, info->dlna_profile.str, + ret = lms_db_bind_text(stmt, 5, info->container.str, + info->container.len); + if (ret != 0) + goto done; + + ret = lms_db_bind_text(stmt, 6, info->dlna_profile.str, info->dlna_profile.len); if (ret != 0) goto done; - ret = lms_db_bind_text(stmt, 6, info->dlna_mime.str, info->dlna_mime.len); + ret = lms_db_bind_text(stmt, 7, info->dlna_mime.str, info->dlna_mime.len); if (ret != 0) goto done; @@ -608,3 +616,25 @@ return r; } + +/** + * If aspect ratio wasn't set yet, guess it from stream video info size. + * + * This function calls lms_aspect_ratio_guess() and thus the + * aspect_ratio string will be allocated with malloc(). Remember to + * free() it afterwards. + * + * @param info where to query width and height, then setting aspect_ratio string. + * + * @return 1 on success, 0 on failure. + */ +int +lms_stream_video_info_aspect_ratio_guess(struct lms_stream_video_info *info) +{ + if (info->aspect_ratio.len > 0) + return 1; + + return lms_aspect_ratio_guess(&info->aspect_ratio, + info->width, + info->height); +} diff -Nru lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_process.c lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_process.c --- lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_process.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_process.c 2013-12-09 17:51:42.000000000 +0000 @@ -565,7 +565,9 @@ _slave_send_reply(fds, r); - if (r < 0 || r == LMS_PROGRESS_STATUS_UP_TO_DATE) + if (r < 0 || + (r == LMS_PROGRESS_STATUS_UP_TO_DATE || + r == LMS_PROGRESS_STATUS_SKIPPED)) continue; counter++; @@ -842,7 +844,7 @@ info, path, new_len, LMS_PROGRESS_STATUS_ERROR_PARSE); return reply; } - _report_progress(info, path, new_len, r); + _report_progress(info, path, new_len, reply); return reply; } } diff -Nru lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_utils.c lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_utils.c --- lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_utils.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_utils.c 2013-12-09 17:51:42.000000000 +0000 @@ -22,8 +22,11 @@ #include #include +#include #include +#include #include +#include /** * Strips string, in place. @@ -125,6 +128,143 @@ } } +/** + * lms_string_size version of strdup(). + * + * @param dst where to return the duplicated value. + * @param src pointer to lms_string_size to be duplicated. + * + * @return 1 on success, 0 on failure. + */ +int +lms_string_size_dup(struct lms_string_size *dst, const struct lms_string_size *src) +{ + if (src->len == 0) { + dst->str = NULL; + dst->len = 0; + return 1; + } + + dst->str = malloc(src->len + 1); + if (!dst->str) { + dst->len = 0; + return 0; + } + + dst->len = src->len; + memcpy(dst->str, src->str, dst->len); + dst->str[dst->len] = '\0'; + return 1; +} + +/** + * Similar to lms_string_size_dup(), but from a simple string. + * + * @param dst where to return the duplicated value. + * @param src pointer to string to be duplicated. + * @param size size to copy or -1 to auto-calculate. + * + * @return 1 on success, 0 on failure. + */ +int +lms_string_size_strndup(struct lms_string_size *dst, const char *src, int size) +{ + if (size < 0) { + if (!src) + size = 0; + else + size = strlen(src); + } + + if (size == 0) { + dst->str = NULL; + dst->len = 0; + return 1; + } + + dst->str = malloc(size + 1); + if (!dst->str) { + dst->len = 0; + return 0; + } + + dst->len = size; + memcpy(dst->str, src, dst->len); + dst->str[dst->len] = '\0'; + return 1; +} + +/* Euclidean algorithm + * http://en.wikipedia.org/wiki/Euclidean_algorithm */ +static unsigned int +gcd(unsigned int a, unsigned int b) +{ + unsigned int t; + + while (b) { + t = b; + b = a % t; + a = t; + } + + return a; +} + +/** + * Guess aspect ratio from known ratios or Greatest Common Divisor. + * + * @param ret where to store the newly allocated string with ratio. + * @param width frame width to guess aspect ratio. + * @param height frame height to guess aspect ratio. + * @return 1 on success and @c ret->str must be @c free()d, 0 on failure. + */ +int +lms_aspect_ratio_guess(struct lms_string_size *ret, int width, int height) +{ + static struct { + double ratio; + struct lms_string_size str; + } *itr, known_ratios[] = { + {16.0 / 9.0, LMS_STATIC_STRING_SIZE("16:9")}, + {4.0 / 3.0, LMS_STATIC_STRING_SIZE("4:3")}, + {3.0 / 2.0, LMS_STATIC_STRING_SIZE("3:2")}, + {5.0 / 3.0, LMS_STATIC_STRING_SIZE("5:3")}, + {8.0 / 5.0, LMS_STATIC_STRING_SIZE("8:5")}, + {1.85, LMS_STATIC_STRING_SIZE("1.85:1")}, + {1.4142, LMS_STATIC_STRING_SIZE("1.41:1")}, + {2.39, LMS_STATIC_STRING_SIZE("2.39:1")}, + {16.18 / 10.0, LMS_STATIC_STRING_SIZE("16.18:10")}, + {-1.0, {NULL, 0}} + }; + double ratio; + unsigned num, den, f; + + if (width <= 0 || height <= 0) { + ret->len = 0; + ret->str = NULL; + return 0; + } + + ratio = (double)width / (double)height; + for (itr = known_ratios; itr->ratio > 0.0; itr++) { + if (fabs(ratio - itr->ratio) <= 0.01) + return lms_string_size_dup(ret, &itr->str); + } + + f = gcd(width, height); + + num = width / f; + den = height / f; + ret->len = asprintf(&ret->str, "%u:%u", num, den); + if (ret->len == (unsigned int)-1) { + ret->len = 0; + ret->str = NULL; + return 0; + } + + return 1; +} + /** * Find out which of the given extensions matches the given name. @@ -177,3 +317,35 @@ return -1; } + +/** + * Extract name from a path given its path string, length, base and extension. + * + * @param name where to store the result. + * @param path the input path to base the name on. + * @param pathlen the path size in bytes. + * @param baselen where (offset int bytes) in path starts the filename (base name). + * @param extlen the extension length in bytes. + * @param cs_conv charset conversion to use, if none use @c NULL. + * @return 1 on success, 0 on failure. + */ +int +lms_name_from_path(struct lms_string_size *name, const char *path, unsigned int pathlen, unsigned int baselen, unsigned int extlen, struct lms_charset_conv *cs_conv) +{ + int size = pathlen - baselen - extlen; + + name->str = malloc(size + 1); + if (!name->str) { + name->len = 0; + return 0; + } + + name->len = size; + memcpy(name->str, path + baselen, size); + name->str[size] = '\0'; + + if (cs_conv) + lms_charset_conv(cs_conv, &name->str, &name->len); + + return 1; +} diff -Nru lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_utils.h lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_utils.h --- lightmediascanner-0.4.5.0~201309060911/src/lib/lightmediascanner_utils.h 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/lib/lightmediascanner_utils.h 2013-12-09 17:51:42.000000000 +0000 @@ -45,6 +45,7 @@ extern "C" { #endif +#include struct lms_string_size { char *str; @@ -58,8 +59,15 @@ API void lms_strstrip(char *str, unsigned int *p_len) GNUC_NON_NULL(1, 2); API void lms_strstrip_and_free(char **p_str, unsigned int *p_len) GNUC_NON_NULL(1, 2); API void lms_string_size_strip_and_free(struct lms_string_size *p) GNUC_NON_NULL(1); + + API int lms_string_size_dup(struct lms_string_size *dst, const struct lms_string_size *src) GNUC_NON_NULL(1, 2); + API int lms_string_size_strndup(struct lms_string_size *dst, const char *src, int size) GNUC_NON_NULL(1); + + API int lms_aspect_ratio_guess(struct lms_string_size *ret, int width, int height) GNUC_NON_NULL(1); + API int lms_which_extension(const char *name, unsigned int name_len, const struct lms_string_size *exts, unsigned int exts_len) GNUC_NON_NULL(1, 3); + API int lms_name_from_path(struct lms_string_size *name, const char *path, unsigned int pathlen, unsigned int baselen, unsigned int extlen, struct lms_charset_conv *cs_conv) GNUC_NON_NULL(1, 2); #ifdef __cplusplus diff -Nru lightmediascanner-0.4.5.0~201309060911/src/lib/shared/util.c lightmediascanner-0.4.5.0~201401231350/src/lib/shared/util.c --- lightmediascanner-0.4.5.0~201309060911/src/lib/shared/util.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/lib/shared/util.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -/** - * Copyright (C) 2013 Intel Corporation. All rights reserved. - * - * 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 Lucas De Marchi - */ - -#include -#include - -#include "util.h" - -struct lms_string_size str_extract_name_from_path( - const char *path, unsigned int pathlen, unsigned int baselen, - const struct lms_string_size *ext, struct lms_charset_conv *cs_conv) -{ - struct lms_string_size str; - - str.len = pathlen - baselen - ext->len; - str.str = malloc(str.len + 1); - if (!str.str) - return (struct lms_string_size) { }; - memcpy(str.str, path + baselen, str.len); - str.str[str.len] = '\0'; - if (cs_conv) - lms_charset_conv(cs_conv, &str.str, &str.len); - - return str; -} - -/* Euclidean algorithm - * http://en.wikipedia.org/wiki/Euclidean_algorithm */ -static unsigned int gcd(unsigned int a, unsigned int b) -{ - unsigned int t; - - while (b) { - t = b; - b = a % t; - a = t; - } - - return a; -} - -void reduce_gcd(unsigned int w, unsigned int h, unsigned int *dw, - unsigned int *dh) -{ - unsigned int f; - - *dw = w; - *dh = h; - - if (!w || !h) - return; - - f = gcd(w, h); - *dw /= f; - *dh /= f; -} diff -Nru lightmediascanner-0.4.5.0~201309060911/src/lib/shared/util.h lightmediascanner-0.4.5.0~201401231350/src/lib/shared/util.h --- lightmediascanner-0.4.5.0~201309060911/src/lib/shared/util.h 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/lib/shared/util.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -/** - * Copyright (C) 2013 Intel Corporation. All rights reserved. - * - * 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 Lucas De Marchi - */ - -#include -#include -#include -#include - -#include -#include - -#define NSEC100_PER_SEC 10000000ULL -#define MSEC_PER_SEC 1000ULL - -#define get_unaligned(ptr) \ - ({ \ - struct __attribute__((packed)) { \ - typeof(*(ptr)) __v; \ - } *__p = (typeof(__p)) (ptr); \ - __p->__v; \ - }) - -#if __BYTE_ORDER == __LITTLE_ENDIAN -static inline uint64_t get_le64(const void *ptr) -{ - return get_unaligned((const uint64_t *) ptr); -} - -static inline uint64_t get_be64(const void *ptr) -{ - return bswap_64(get_unaligned((const uint64_t *) ptr)); -} - -static inline uint32_t get_le32(const void *ptr) -{ - return get_unaligned((const uint32_t *) ptr); -} - -static inline uint32_t get_be32(const void *ptr) -{ - return bswap_32(get_unaligned((const uint32_t *) ptr)); -} - -static inline uint16_t get_le16(const void *ptr) -{ - return get_unaligned((const uint16_t *) ptr); -} - -static inline uint16_t get_be16(const void *ptr) -{ - return bswap_16(get_unaligned((const uint16_t *) ptr)); -} - -#elif __BYTE_ORDER == __BIG_ENDIAN -static inline uint64_t get_le64(const void *ptr) -{ - return bswap_64(get_unaligned((const uint64_t *) ptr)); -} - -static inline uint64_t get_be64(const void *ptr) -{ - return get_unaligned((const uint64_t *) ptr); -} - -static inline uint32_t get_le32(const void *ptr) -{ - return bswap_32(get_unaligned((const uint32_t *) ptr)); -} - -static inline uint32_t get_be32(const void *ptr) -{ - return get_unaligned((const uint32_t *) ptr); -} - -static inline uint16_t get_le16(const void *ptr) -{ - return bswap_16(get_unaligned((const uint16_t *) ptr)); -} - -static inline uint16_t get_be16(const void *ptr) -{ - return get_unaligned((const uint16_t *) ptr); -} - -#else -#error "Unknown byte order" -#endif - - -struct lms_string_size str_extract_name_from_path( - const char *path, unsigned int pathlen, unsigned int baselen, - const struct lms_string_size *ext, struct lms_charset_conv *cs_conv); - -void reduce_gcd(unsigned int w, unsigned int h, unsigned int *dw, - unsigned int *dh); diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/Makefile.am lightmediascanner-0.4.5.0~201401231350/src/plugins/Makefile.am --- lightmediascanner-0.4.5.0~201309060911/src/plugins/Makefile.am 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/Makefile.am 2013-12-09 17:51:42.000000000 +0000 @@ -3,17 +3,16 @@ AM_LDFLAGS = -module -avoid-version -shared $(GCLDFLAGS) AM_CFLAGS = -fvisibility=hidden $(WARNINGFLAGS) AM_CPPFLAGS = -include $(top_builddir)/config.h \ - -I$(top_srcdir)/src/lib @SQLITE3_CFLAGS@ + -I$(top_srcdir)/src/lib -I$(srcdir) @SQLITE3_CFLAGS@ pkgdir = $(pluginsdir) -PLUGINS_LIBADD = $(top_builddir)/src/lib/liblightmediascanner.la \ - $(top_builddir)/src/lib/shared/libshared.la +PLUGINS_LIBADD = $(top_builddir)/src/lib/liblightmediascanner.la pkg_LTLIBRARIES = BUILT_SOURCES = EXTRA_DIST = SUBDIRS = -noinst_HEADERS = +noinst_HEADERS = shared/util.h if USE_MODULE_DUMMY pkg_LTLIBRARIES += dummy/dummy.la @@ -92,7 +91,7 @@ if USE_MODULE_ID3 pkg_LTLIBRARIES += id3/id3.la id3_id3_la_SOURCES = id3/id3.c id3/id3v1_genres.c -id3_id3_la_LIBADD = $(PLUGINS_LIBADD) $(MP4V2_LIBS) +id3_id3_la_LIBADD = $(PLUGINS_LIBADD) id3/id3v1_genres.c: $(srcdir)/id3/id3v1_genres.def $(srcdir)/id3/id3v1_genres_gen.awk $(AWK) -f $(srcdir)/id3/id3v1_genres_gen.awk $(srcdir)/id3/id3v1_genres.def > $@ diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/asf/asf.c lightmediascanner-0.4.5.0~201401231350/src/plugins/asf/asf.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/asf/asf.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/asf/asf.c 2013-12-09 17:51:42.000000000 +0000 @@ -430,6 +430,7 @@ s->base.codec = *_audio_codec_id_to_str(_read_word(fd)); s->base.audio.channels = _read_word(fd); s->priv.sampling_rate = _read_dword(fd); + s->base.audio.sampling_rate = s->priv.sampling_rate; s->base.audio.bitrate = _read_dword(fd) * 8; } else { struct { @@ -448,7 +449,6 @@ /* other fields are ignored */ } __attribute__((packed)) video; - unsigned int num, den; r = read(fd, &video, sizeof(video)); if (r != sizeof(video)) @@ -461,11 +461,7 @@ s->base.codec = *_video_codec_id_to_str(video.compression_id); s->base.video.width = get_le32(&video.width); s->base.video.height = get_le32(&video.height); - - reduce_gcd(s->base.video.width, s->base.video.height, &num, &den); - asprintf(&s->base.video.aspect_ratio.str, "%u:%u", num, den); - s->base.video.aspect_ratio.len = s->base.video.aspect_ratio.str ? - strlen(s->base.video.aspect_ratio.str) : 0; + lms_stream_video_info_aspect_ratio_guess(&s->base.video); } _stream_copy_extension_properties(s); @@ -795,10 +791,9 @@ lms_string_size_strip_and_free(&info.genre); if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - ctxt->cs_conv); + lms_name_from_path(&info.title, finfo->path, finfo->path_len, + finfo->base, _exts[((long) match) - 1].len, + ctxt->cs_conv); if (info.type == LMS_STREAM_TYPE_AUDIO) { struct lms_audio_info audio_info = { }; @@ -831,6 +826,7 @@ video_info.title = info.title; video_info.artist = info.artist; video_info.length = info.length; + video_info.container = _container; video_info.streams = (struct lms_stream *) info.streams; r = lms_db_video_add(plugin->video_db, &video_info); } diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/audio-dummy/audio-dummy.c lightmediascanner-0.4.5.0~201401231350/src/plugins/audio-dummy/audio-dummy.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/audio-dummy/audio-dummy.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/audio-dummy/audio-dummy.c 2013-12-09 17:51:42.000000000 +0000 @@ -69,10 +69,8 @@ long ext_idx; ext_idx = ((long)match) - 1; - info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len; - info.title.str = malloc((info.title.len + 1) * sizeof(char)); - memcpy(info.title.str, finfo->path + finfo->base, info.title.len); - info.title.str[info.title.len] = '\0'; + lms_string_size_strndup(&info.title, finfo->path + finfo->base, + finfo->path_len - finfo->base - _exts[ext_idx].len); lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len); info.id = finfo->id; diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/flac/flac.c lightmediascanner-0.4.5.0~201401231350/src/plugins/flac/flac.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/flac/flac.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/flac/flac.c 2013-12-09 17:51:42.000000000 +0000 @@ -26,7 +26,6 @@ #include #include -#include #include #include @@ -100,26 +99,14 @@ for (i = 0; i < tags->data.vorbis_comment.num_comments; i++) { str = (char *) tags->data.vorbis_comment.comments[i].entry; len = tags->data.vorbis_comment.comments[i].length; - if (strncmp(str, "TITLE=", 6) == 0) { - info.title.str = malloc((len - 6 + 1) * sizeof(char)); - strcpy(info.title.str, str + 6); - info.title.len = len - 6; - } - else if (strncmp(str, "ARTIST=", 7) == 0) { - info.artist.str = malloc((len - 7 + 1) * sizeof(char)); - strcpy(info.artist.str, str + 7); - info.artist.len = len - 7; - } - else if (strncmp(str, "ALBUM=", 6) == 0) { - info.album.str = malloc((len - 6 + 1) * sizeof(char)); - strcpy(info.album.str, str + 6); - info.album.len = len - 6; - } - else if (strncmp(str, "GENRE=", 6) == 0) { - info.genre.str = malloc((len - 6 + 1) * sizeof(char)); - strcpy(info.genre.str, str + 6); - info.genre.len = len - 6; - } + if (strncmp(str, "TITLE=", 6) == 0) + lms_string_size_strndup(&info.title, str + 6, len - 6); + else if (strncmp(str, "ARTIST=", 7) == 0) + lms_string_size_strndup(&info.artist, str + 7, len - 7); + else if (strncmp(str, "ALBUM=", 6) == 0) + lms_string_size_strndup(&info.album, str + 6, len - 6); + else if (strncmp(str, "GENRE=", 6) == 0) + lms_string_size_strndup(&info.genre, str + 6, len - 6); else if (strncmp(str, "TRACKNUMBER=", 12) == 0) info.trackno = atoi(str + 12); } @@ -133,10 +120,9 @@ title_fallback: if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - NULL); + 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); diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/id3/id3.c lightmediascanner-0.4.5.0~201401231350/src/plugins/id3/id3.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/id3/id3.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/id3/id3.c 2013-12-09 17:51:42.000000000 +0000 @@ -1269,10 +1269,9 @@ } if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - ctxt->cs_conv); + lms_name_from_path(&info.title, finfo->path, finfo->path_len, + finfo->base, _exts[((long) match) - 1].len, + ctxt->cs_conv); if (info.trackno == -1) info.trackno = 0; diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/jpeg/jpeg.c lightmediascanner-0.4.5.0~201401231350/src/plugins/jpeg/jpeg.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/jpeg/jpeg.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/jpeg/jpeg.c 2013-12-09 17:51:42.000000000 +0000 @@ -681,10 +681,9 @@ info.date = finfo->mtime; if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - ctxt->cs_conv); + lms_name_from_path(&info.title, finfo->path, finfo->path_len, + finfo->base, _exts[((long) match) - 1].len, + ctxt->cs_conv); if (info.artist.str) lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len); diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/m3u/m3u.c lightmediascanner-0.4.5.0~201401231350/src/plugins/m3u/m3u.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/m3u/m3u.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/m3u/m3u.c 2013-12-09 17:51:42.000000000 +0000 @@ -133,10 +133,8 @@ finfo->path); ext_idx = ((long)match) - 1; - info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len; - info.title.str = malloc((info.title.len + 1) * sizeof(char)); - memcpy(info.title.str, finfo->path + finfo->base, info.title.len); - info.title.str[info.title.len] = '\0'; + lms_string_size_strndup(&info.title, finfo->path + finfo->base, + finfo->path_len - finfo->base - _exts[ext_idx].len); lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len); info.id = finfo->id; diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/mp4/mp4.c lightmediascanner-0.4.5.0~201401231350/src/plugins/mp4/mp4.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/mp4/mp4.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/mp4/mp4.c 2013-12-09 17:51:42.000000000 +0000 @@ -28,22 +28,19 @@ #include #include -#include -#ifdef HAVE_MP4V2 #include -#else -#include -#endif #include #include +#include struct mp4_info { struct lms_string_size title; struct lms_string_size artist; struct lms_string_size album; struct lms_string_size genre; - u_int16_t trackno; + uint16_t trackno; + uint64_t length; }; struct plugin { @@ -52,6 +49,272 @@ lms_db_video_t *video_db; }; +#define DECL_STR(cname, str) \ + static const struct lms_string_size cname = LMS_STATIC_STRING_SIZE(str) + +DECL_STR(_container, "mp4"); + +DECL_STR(_codec_audio_mpeg4aac_main, "mpeg4aac-main"); +DECL_STR(_codec_audio_mpeg4aac_lc, "mpeg4aac-lc"); +DECL_STR(_codec_audio_mpeg4aac_ssr, "mpeg4aac-ssr"); +DECL_STR(_codec_audio_mpeg4aac_ltp, "mpeg4aac-ltp"); +DECL_STR(_codec_audio_mpeg4aac_he, "mpeg4aac-he"); +DECL_STR(_codec_audio_mpeg4aac_scalable, "mpeg4aac-scalable"); +DECL_STR(_codec_audio_mpeg4_twinvq, "mpeg4twinvq"); +DECL_STR(_codec_audio_mpeg4_celp, "mpeg4celp"); +DECL_STR(_codec_audio_mpeg4_hvxc, "mpeg4hvxc"); +DECL_STR(_codec_audio_mpeg4_tssi, "mpeg4ttsi"); +DECL_STR(_codec_audio_mpeg4_main_synthetic,"mpeg4main-synthetic"); +DECL_STR(_codec_audio_mpeg4_wavetable_syn, "mpeg4wavetable-syn"); +DECL_STR(_codec_audio_mpeg4_general_midi, "mpeg4general-midi"); +DECL_STR(_codec_audio_mpeg4_algo_syn_and_audio_fx, "mpeg4algo-syn-and-audio-fx"); +DECL_STR(_codec_audio_mpeg4_er_aac_lc, "mpeg4er-aac-lc"); +DECL_STR(_codec_audio_mpeg4_er_aac_ltp, "mpeg4er-aac-ltp"); +DECL_STR(_codec_audio_mpeg4_er_aac_scalable, "mpeg4er-aac-scalable"); +DECL_STR(_codec_audio_mpeg4_er_twinvq, "mpeg4er-twinvq"); +DECL_STR(_codec_audio_mpeg4_er_bsac, "mpeg4er-bsac"); +DECL_STR(_codec_audio_mpeg4_er_acc_ld, "mpeg4er-acc-ld"); +DECL_STR(_codec_audio_mpeg4_er_celp, "mpeg4er-celp"); +DECL_STR(_codec_audio_mpeg4_er_hvxc, "mpeg4er-hvxc"); +DECL_STR(_codec_audio_mpeg4_er_hiln, "mpeg4er-hiln"); +DECL_STR(_codec_audio_mpeg4_er_parametric, "mpeg4er-parametric"); +DECL_STR(_codec_audio_mpeg4_ssc, "mpeg4ssc"); +DECL_STR(_codec_audio_mpeg4_ps, "mpeg4ps"); +DECL_STR(_codec_audio_mpeg4_mpeg_surround, "mpeg4mpeg-surround"); +DECL_STR(_codec_audio_mpeg4_layer1, "mpeg4layer1"); +DECL_STR(_codec_audio_mpeg4_layer2, "mpeg4layer2"); +DECL_STR(_codec_audio_mpeg4_layer3, "mpeg4layer3"); +DECL_STR(_codec_audio_mpeg4_dst, "mpeg4dst"); +DECL_STR(_codec_audio_mpeg4_audio_lossless, "mpeg4audio-lossless"); +DECL_STR(_codec_audio_mpeg4_sls, "mpeg4sls"); +DECL_STR(_codec_audio_mpeg4_sls_non_core, "mpeg4sls-non-core"); + +DECL_STR(_codec_audio_mpeg2aac_main, "mpeg2aac-main"); +DECL_STR(_codec_audio_mpeg2aac_lc, "mpeg2aac-lc"); +DECL_STR(_codec_audio_mpeg2aac_ssr, "mpeg2aac-ssr"); +DECL_STR(_codec_audio_mpeg2audio, "mpeg2audio"); +DECL_STR(_codec_audio_mpeg1audio, "mpeg1audio"); +DECL_STR(_codec_audio_pcm16le, "pcm16le"); +DECL_STR(_codec_audio_vorbis, "vorbis"); +DECL_STR(_codec_audio_alaw, "alaw"); +DECL_STR(_codec_audio_ulaw, "ulaw"); +DECL_STR(_codec_audio_g723, "g723"); +DECL_STR(_codec_audio_pcm16be, "pcm16be"); + +DECL_STR(_codec_video_mpeg4_simple_l1, "mpeg4-simple-l1"); +DECL_STR(_codec_video_mpeg4_simple_l2, "mpeg4-simple-l2"); +DECL_STR(_codec_video_mpeg4_simple_l3, "mpeg4-simple-l3"); +DECL_STR(_codec_video_mpeg4_simple_l0, "mpeg4-simple-l0"); +DECL_STR(_codec_video_mpeg4_simple_scalable_l1, "mpeg4-simple-scalable-l1"); +DECL_STR(_codec_video_mpeg4_simple_scalable_l2, "mpeg4-simple-scalable-l2"); +DECL_STR(_codec_video_mpeg4_core_l1, "mpeg4-core-l1"); +DECL_STR(_codec_video_mpeg4_core_l2, "mpeg4-core-l2"); +DECL_STR(_codec_video_mpeg4_main_l2, "mpeg4-main-l2"); +DECL_STR(_codec_video_mpeg4_main_l3, "mpeg4-main-l3"); +DECL_STR(_codec_video_mpeg4_main_l4, "mpeg4-main-l4"); +DECL_STR(_codec_video_mpeg4_nbit_l2, "mpeg4-nbit-l2"); +DECL_STR(_codec_video_mpeg4_scalable_texture_l1, "mpeg4-scalable-texture-l1"); +DECL_STR(_codec_video_mpeg4_simple_face_anim_l1, "mpeg4-simple-face-anim-l1"); +DECL_STR(_codec_video_mpeg4_simple_face_anim_l2, "mpeg4-simple-face-anim-l2"); +DECL_STR(_codec_video_mpeg4_simple_fba_l1, "mpeg4-simple-fba-l1"); +DECL_STR(_codec_video_mpeg4_simple_fba_l2, "mpeg4-simple-fba-l2"); +DECL_STR(_codec_video_mpeg4_basic_anim_text_l1, "mpeg4-basic-anim-text-l1"); +DECL_STR(_codec_video_mpeg4_basic_anim_text_l2, "mpeg4-basic-anim-text-l2"); +DECL_STR(_codec_video_mpeg4_hybrid_l1, "mpeg4-hybrid-l1"); +DECL_STR(_codec_video_mpeg4_hybrid_l2, "mpeg4-hybrid-l2"); +DECL_STR(_codec_video_mpeg4_adv_rt_simple_l1, "mpeg4-adv-rt-simple-l1"); +DECL_STR(_codec_video_mpeg4_adv_rt_simple_l2, "mpeg4-adv-rt-simple-l2"); +DECL_STR(_codec_video_mpeg4_adv_rt_simple_l3, "mpeg4-adv-rt-simple-l3"); +DECL_STR(_codec_video_mpeg4_adv_rt_simple_l4, "mpeg4-adv-rt-simple-l4"); +DECL_STR(_codec_video_mpeg4_core_scalable_l1, "mpeg4-core-scalable-l1"); +DECL_STR(_codec_video_mpeg4_core_scalable_l2, "mpeg4-core-scalable-l2"); +DECL_STR(_codec_video_mpeg4_core_scalable_l3, "mpeg4-core-scalable-l3"); +DECL_STR(_codec_video_mpeg4_adv_coding_efficiency_l1, "mpeg4-adv-coding-efficiency-l1"); +DECL_STR(_codec_video_mpeg4_adv_coding_efficiency_l2, "mpeg4-adv-coding-efficiency-l2"); +DECL_STR(_codec_video_mpeg4_adv_coding_efficiency_l3, "mpeg4-adv-coding-efficiency-l3"); +DECL_STR(_codec_video_mpeg4_adv_coding_efficiency_l4, "mpeg4-adv-coding-efficiency-l4"); +DECL_STR(_codec_video_mpeg4_adv_core_l1, "mpeg4-adv-core-l1"); +DECL_STR(_codec_video_mpeg4_adv_core_l2, "mpeg4-adv-core-l2"); +DECL_STR(_codec_video_mpeg4_adv_scalable_texture_l1, "mpeg4-adv-scalable-texture-l1"); +DECL_STR(_codec_video_mpeg4_adv_scalable_texture_l2, "mpeg4-adv-scalable-texture-l2"); +DECL_STR(_codec_video_mpeg4_adv_scalable_texture_l3, "mpeg4-adv-scalable-texture-l3"); +DECL_STR(_codec_video_mpeg4_simple_studio_l1, "mpeg4-simple-studio-l1"); +DECL_STR(_codec_video_mpeg4_simple_studio_l2, "mpeg4-simple-studio-l2"); +DECL_STR(_codec_video_mpeg4_simple_studio_l3, "mpeg4-simple-studio-l3"); +DECL_STR(_codec_video_mpeg4_simple_studio_l4, "mpeg4-simple-studio-l4"); +DECL_STR(_codec_video_mpeg4_core_studio_l1, "mpeg4-core-studio-l1"); +DECL_STR(_codec_video_mpeg4_core_studio_l2, "mpeg4-core-studio-l2"); +DECL_STR(_codec_video_mpeg4_core_studio_l3, "mpeg4-core-studio-l3"); +DECL_STR(_codec_video_mpeg4_core_studio_l4, "mpeg4-core-studio-l4"); +DECL_STR(_codec_video_mpeg4_adv_simple_l0, "mpeg4-adv-simple-l0"); +DECL_STR(_codec_video_mpeg4_adv_simple_l1, "mpeg4-adv-simple-l1"); +DECL_STR(_codec_video_mpeg4_adv_simple_l2, "mpeg4-adv-simple-l2"); +DECL_STR(_codec_video_mpeg4_adv_simple_l3, "mpeg4-adv-simple-l3"); +DECL_STR(_codec_video_mpeg4_adv_simple_l4, "mpeg4-adv-simple-l4"); +DECL_STR(_codec_video_mpeg4_adv_simple_l5, "mpeg4-adv-simple-l5"); +DECL_STR(_codec_video_mpeg4_adv_simple_l3b, "mpeg4-adv-simple-l3b"); +DECL_STR(_codec_video_mpeg4_fgs_l0, "mpeg4-fgs-l0"); +DECL_STR(_codec_video_mpeg4_fgs_l1, "mpeg4-fgs-l1"); +DECL_STR(_codec_video_mpeg4_fgs_l2, "mpeg4-fgs-l2"); +DECL_STR(_codec_video_mpeg4_fgs_l3, "mpeg4-fgs-l3"); +DECL_STR(_codec_video_mpeg4_fgs_l4, "mpeg4-fgs-l4"); +DECL_STR(_codec_video_mpeg4_fgs_l5, "mpeg4-fgs-l5"); + +DECL_STR(_codec_video_mpeg2_simple, "mpeg2-simple"); +DECL_STR(_codec_video_mpeg2_main, "mpeg2-main"); +DECL_STR(_codec_video_mpeg2_snr, "mpeg2-snr"); +DECL_STR(_codec_video_mpeg2_spatial, "mpeg2-spatial"); +DECL_STR(_codec_video_mpeg2_high, "mpeg2-high"); +DECL_STR(_codec_video_mpeg2_422, "mpeg2-422"); +DECL_STR(_codec_video_mpeg1, "mpeg1"); +DECL_STR(_codec_video_jpeg, "jpeg"); +DECL_STR(_codec_video_yuv12, "yuv12"); +DECL_STR(_codec_video_h263, "h263"); +DECL_STR(_codec_video_h261, "h261"); + +DECL_STR(_codec_audio_amr, "amr"); +DECL_STR(_codec_audio_amr_wb, "amr-wb"); +#undef DECL_STR + +struct type_str { + uint8_t type; + const struct lms_string_size *str; +}; + +static const struct lms_string_size *_audio_codecs[] = { + &_codec_audio_mpeg4aac_main, + &_codec_audio_mpeg4aac_lc, + &_codec_audio_mpeg4aac_ssr, + &_codec_audio_mpeg4aac_ltp, + &_codec_audio_mpeg4aac_he, + &_codec_audio_mpeg4aac_scalable, + &_codec_audio_mpeg4_twinvq, + &_codec_audio_mpeg4_celp, + &_codec_audio_mpeg4_hvxc, + NULL, + NULL, + &_codec_audio_mpeg4_tssi, + &_codec_audio_mpeg4_main_synthetic, + &_codec_audio_mpeg4_wavetable_syn, + &_codec_audio_mpeg4_general_midi, + &_codec_audio_mpeg4_algo_syn_and_audio_fx, + &_codec_audio_mpeg4_er_aac_lc, + NULL, + &_codec_audio_mpeg4_er_aac_ltp, + &_codec_audio_mpeg4_er_aac_scalable, + &_codec_audio_mpeg4_er_twinvq, + &_codec_audio_mpeg4_er_bsac, + &_codec_audio_mpeg4_er_acc_ld, + &_codec_audio_mpeg4_er_celp, + &_codec_audio_mpeg4_er_hvxc, + &_codec_audio_mpeg4_er_hiln, + &_codec_audio_mpeg4_er_parametric, + &_codec_audio_mpeg4_ssc, + &_codec_audio_mpeg4_ps, + &_codec_audio_mpeg4_mpeg_surround, + NULL, + &_codec_audio_mpeg4_layer1, + &_codec_audio_mpeg4_layer2, + &_codec_audio_mpeg4_layer3, + &_codec_audio_mpeg4_dst, + &_codec_audio_mpeg4_audio_lossless, + &_codec_audio_mpeg4_sls, + &_codec_audio_mpeg4_sls_non_core +}; + +static const struct type_str _audio_types[] = { + {MP4_MPEG2_AAC_MAIN_AUDIO_TYPE, &_codec_audio_mpeg2aac_main}, + {MP4_MPEG2_AAC_LC_AUDIO_TYPE, &_codec_audio_mpeg2aac_lc}, + {MP4_MPEG2_AAC_SSR_AUDIO_TYPE, &_codec_audio_mpeg2aac_ssr}, + {MP4_MPEG2_AUDIO_TYPE, &_codec_audio_mpeg2audio}, + {MP4_MPEG1_AUDIO_TYPE, &_codec_audio_mpeg1audio}, + {MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE, &_codec_audio_pcm16le}, + {MP4_VORBIS_AUDIO_TYPE, &_codec_audio_vorbis}, + {MP4_ALAW_AUDIO_TYPE, &_codec_audio_alaw}, + {MP4_ULAW_AUDIO_TYPE, &_codec_audio_ulaw}, + {MP4_G723_AUDIO_TYPE, &_codec_audio_g723}, + {MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE, &_codec_audio_pcm16be}, + {} +}; + +static const struct type_str _video_vprofiles[] = { + {MPEG4_SP_L1, &_codec_video_mpeg4_simple_l1}, + {MPEG4_SP_L2, &_codec_video_mpeg4_simple_l2}, + {MPEG4_SP_L3, &_codec_video_mpeg4_simple_l3}, + {MPEG4_SP_L0, &_codec_video_mpeg4_simple_l0}, + {MPEG4_SSP_L1, &_codec_video_mpeg4_simple_scalable_l1}, + {MPEG4_SSP_L2, &_codec_video_mpeg4_simple_scalable_l2}, + {MPEG4_CP_L1, &_codec_video_mpeg4_core_l1}, + {MPEG4_CP_L2, &_codec_video_mpeg4_core_l2}, + {MPEG4_MP_L2, &_codec_video_mpeg4_main_l2}, + {MPEG4_MP_L3, &_codec_video_mpeg4_main_l3}, + {MPEG4_MP_L4, &_codec_video_mpeg4_main_l4}, + {MPEG4_NBP_L2, &_codec_video_mpeg4_nbit_l2}, + {MPEG4_STP_L1, &_codec_video_mpeg4_scalable_texture_l1}, + {MPEG4_SFAP_L1, &_codec_video_mpeg4_simple_face_anim_l1}, + {MPEG4_SFAP_L2, &_codec_video_mpeg4_simple_face_anim_l2}, + {MPEG4_SFBAP_L1, &_codec_video_mpeg4_simple_fba_l1}, + {MPEG4_SFBAP_L2, &_codec_video_mpeg4_simple_fba_l2}, + {MPEG4_BATP_L1, &_codec_video_mpeg4_basic_anim_text_l1}, + {MPEG4_BATP_L2, &_codec_video_mpeg4_basic_anim_text_l2}, + {MPEG4_HP_L1, &_codec_video_mpeg4_hybrid_l1}, + {MPEG4_HP_L2, &_codec_video_mpeg4_hybrid_l2}, + {MPEG4_ARTSP_L1, &_codec_video_mpeg4_adv_rt_simple_l1}, + {MPEG4_ARTSP_L2, &_codec_video_mpeg4_adv_rt_simple_l2}, + {MPEG4_ARTSP_L3, &_codec_video_mpeg4_adv_rt_simple_l3}, + {MPEG4_ARTSP_L4, &_codec_video_mpeg4_adv_rt_simple_l4}, + {MPEG4_CSP_L1, &_codec_video_mpeg4_core_scalable_l1}, + {MPEG4_CSP_L2, &_codec_video_mpeg4_core_scalable_l2}, + {MPEG4_CSP_L3, &_codec_video_mpeg4_core_scalable_l3}, + {MPEG4_ACEP_L1, &_codec_video_mpeg4_adv_coding_efficiency_l1}, + {MPEG4_ACEP_L2, &_codec_video_mpeg4_adv_coding_efficiency_l2}, + {MPEG4_ACEP_L3, &_codec_video_mpeg4_adv_coding_efficiency_l3}, + {MPEG4_ACEP_L4, &_codec_video_mpeg4_adv_coding_efficiency_l4}, + {MPEG4_ACP_L1, &_codec_video_mpeg4_adv_core_l1}, + {MPEG4_ACP_L2, &_codec_video_mpeg4_adv_core_l2}, + {MPEG4_AST_L1, &_codec_video_mpeg4_adv_scalable_texture_l1}, + {MPEG4_AST_L2, &_codec_video_mpeg4_adv_scalable_texture_l2}, + {MPEG4_AST_L3, &_codec_video_mpeg4_adv_scalable_texture_l3}, + {MPEG4_S_STUDIO_P_L1, &_codec_video_mpeg4_simple_studio_l1}, + {MPEG4_S_STUDIO_P_L2, &_codec_video_mpeg4_simple_studio_l2}, + {MPEG4_S_STUDIO_P_L3, &_codec_video_mpeg4_simple_studio_l3}, + {MPEG4_S_STUDIO_P_L4, &_codec_video_mpeg4_simple_studio_l4}, + {MPEG4_C_STUDIO_P_L1, &_codec_video_mpeg4_core_studio_l1}, + {MPEG4_C_STUDIO_P_L2, &_codec_video_mpeg4_core_studio_l2}, + {MPEG4_C_STUDIO_P_L3, &_codec_video_mpeg4_core_studio_l3}, + {MPEG4_C_STUDIO_P_L4, &_codec_video_mpeg4_core_studio_l4}, + {MPEG4_ASP_L0, &_codec_video_mpeg4_adv_simple_l0}, + {MPEG4_ASP_L1, &_codec_video_mpeg4_adv_simple_l1}, + {MPEG4_ASP_L2, &_codec_video_mpeg4_adv_simple_l2}, + {MPEG4_ASP_L3, &_codec_video_mpeg4_adv_simple_l3}, + {MPEG4_ASP_L4, &_codec_video_mpeg4_adv_simple_l4}, + {MPEG4_ASP_L5, &_codec_video_mpeg4_adv_simple_l5}, + {MPEG4_ASP_L3B, &_codec_video_mpeg4_adv_simple_l3b}, + {MPEG4_FGSP_L0, &_codec_video_mpeg4_fgs_l0}, + {MPEG4_FGSP_L1, &_codec_video_mpeg4_fgs_l1}, + {MPEG4_FGSP_L2, &_codec_video_mpeg4_fgs_l2}, + {MPEG4_FGSP_L3, &_codec_video_mpeg4_fgs_l3}, + {MPEG4_FGSP_L4, &_codec_video_mpeg4_fgs_l4}, + {MPEG4_FGSP_L5, &_codec_video_mpeg4_fgs_l5}, + {} +}; + +static const struct type_str _video_types[] = { + {MP4_MPEG2_SIMPLE_VIDEO_TYPE, &_codec_video_mpeg2_simple}, + {MP4_MPEG2_MAIN_VIDEO_TYPE, &_codec_video_mpeg2_main}, + {MP4_MPEG2_SNR_VIDEO_TYPE, &_codec_video_mpeg2_snr}, + {MP4_MPEG2_SPATIAL_VIDEO_TYPE, &_codec_video_mpeg2_spatial}, + {MP4_MPEG2_HIGH_VIDEO_TYPE, &_codec_video_mpeg2_high}, + {MP4_MPEG2_442_VIDEO_TYPE, &_codec_video_mpeg2_422}, + {MP4_MPEG1_VIDEO_TYPE, &_codec_video_mpeg1}, + {MP4_JPEG_VIDEO_TYPE, &_codec_video_jpeg}, + {MP4_YUV12_VIDEO_TYPE, &_codec_video_yuv12}, + {MP4_H263_VIDEO_TYPE, &_codec_video_h263}, + {MP4_H261_VIDEO_TYPE, &_codec_video_h261}, + {} +}; + + static const char _name[] = "mp4"; static const struct lms_string_size _exts[] = { LMS_STATIC_STRING_SIZE(".mp4"), @@ -69,9 +332,13 @@ }; static const char *_authors[] = { "Andre Moreira Magalhaes", + "Lucas De Marchi", + "Gustavo Sverzut Barbieri", NULL }; +static const struct lms_string_size nullstr = { }; + static void * _match(struct plugin *p, const char *path, int len, int base) { @@ -84,6 +351,153 @@ return (void*)(i + 1); } +static inline struct lms_string_size +_find_type_str(const struct type_str *base, uint8_t type) +{ + const struct type_str *itr; + + for (itr = base; itr->str != NULL; itr++) { + if (itr->type == type) + return *itr->str; + } + + return nullstr; +} + +static struct lms_string_size +_get_audio_codec(MP4FileHandle mp4_fh, MP4TrackId id) +{ + const char *data_name = MP4GetTrackMediaDataName(mp4_fh, id); + uint8_t type; + + if (!data_name) + return nullstr; + if (strcasecmp(data_name, "samr") == 0) + return _codec_audio_amr; + if (strcasecmp(data_name, "sawb") == 0) + return _codec_audio_amr_wb; + if (strcasecmp(data_name, "mp4a") != 0) + return nullstr; + + type = MP4GetTrackEsdsObjectTypeId(mp4_fh, id); + if (type == MP4_MPEG4_AUDIO_TYPE) { + type = MP4GetTrackAudioMpeg4Type(mp4_fh, id); + if (type == 0 || type > LMS_ARRAY_SIZE(_audio_codecs) || + _audio_codecs[type - 1] == NULL) + return nullstr; + + return *_audio_codecs[type - 1]; + } + + return _find_type_str(_audio_types, type); +} + +/* WARNING: returned str is malloc()'ed if not NULL, use free() */ +static struct lms_string_size +_get_video_codec(MP4FileHandle mp4_fh, MP4TrackId id) +{ + const char *data_name = MP4GetTrackMediaDataName(mp4_fh, id); + struct lms_string_size ret = {}, tmp; + char buf[256]; + char ofmt[8] = {}; + + if (!data_name) + return nullstr; + + if (strcasecmp(data_name, "encv") == 0) { + if (!MP4GetTrackMediaDataOriginalFormat(mp4_fh, id, ofmt, sizeof(ofmt))) + return nullstr; + } + + if (strcasecmp(data_name, "s263") == 0) { + ret = _codec_video_h263; + goto found; + } + + if (strcasecmp(data_name, "avc1") == 0 || + strcasecmp(ofmt, "264b") == 0) { + uint8_t profile, level; + char str_profile[64], str_level[64]; + + if (!MP4GetTrackH264ProfileLevel(mp4_fh, id, &profile, &level)) { + return nullstr; + } + + switch (profile) { + case 66: + memcpy(str_profile, "baseline", sizeof("baseline")); + break; + case 77: + memcpy(str_profile, "main", sizeof("main")); + break; + case 88: + memcpy(str_profile, "extended", sizeof("extended")); + break; + case 100: + memcpy(str_profile, "high", sizeof("high")); + break; + case 110: + memcpy(str_profile, "high-10", sizeof("high-10")); + break; + case 122: + memcpy(str_profile, "high-422", sizeof("high-422")); + break; + case 144: + memcpy(str_profile, "high-444", sizeof("high-444")); + break; + default: + snprintf(str_profile, sizeof(str_profile), "unknown-%d", profile); + } + + if (level % 10 == 0) + snprintf(str_level, sizeof(str_level), "%u", level / 10); + else + snprintf(str_level, sizeof(str_level), "%u.%u", level / 10, + level % 10); + + ret.len = snprintf(buf, sizeof(buf), "h264-%s-%s", + str_profile, str_level); + ret.str = buf; + goto found; + } else if (strcasecmp(data_name, "mp4v") == 0 || + strcasecmp(data_name, "encv") == 0) { + uint8_t type = MP4GetTrackEsdsObjectTypeId(mp4_fh, id); + + if (type == MP4_MPEG4_VIDEO_TYPE) { + type = MP4GetVideoProfileLevel(mp4_fh, id); + ret = _find_type_str(_video_vprofiles, type); + } else + ret = _find_type_str(_video_types, type); + goto found; + } + + return nullstr; + +found: /* ugly, but h264 codec is composed in runtime */ + + if (!lms_string_size_dup(&tmp, &ret)) + return nullstr; + return tmp; +} + +static struct lms_string_size +_get_lang(MP4FileHandle mp4_fh, MP4TrackId id) +{ + struct lms_string_size ret; + char buf[4]; + + if (!MP4GetTrackLanguage(mp4_fh, id, buf)) + return nullstr; + + if (memcmp(buf, "und", 4) == 0) + return nullstr; + + if (!lms_string_size_strndup(&ret, buf, -1)) + return nullstr; + + return ret; +} + static int _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match) { @@ -92,41 +506,87 @@ struct lms_video_info video_info = { }; int r, stream_type = LMS_STREAM_TYPE_AUDIO; MP4FileHandle mp4_fh; - u_int32_t num_tracks; + u_int32_t num_tracks, i; + const MP4Tags *tags; -#ifdef HAVE_MP4V2_2_0_API mp4_fh = MP4Read(finfo->path); -#else - mp4_fh = MP4Read(finfo->path, 0); -#endif if (mp4_fh == MP4_INVALID_FILE_HANDLE) { fprintf(stderr, "ERROR: cannot read mp4 file %s\n", finfo->path); return -1; } + tags = MP4TagsAlloc(); + if (!tags) + return -1; + + if (!MP4TagsFetch(tags, mp4_fh)) { + r = -1; + goto fail; + } + + lms_string_size_strndup(&info.title, tags->name, -1); + lms_string_size_strndup(&info.artist, tags->artist, -1); + /* check if the file contains a video track */ num_tracks = MP4GetNumberOfTracks(mp4_fh, MP4_VIDEO_TRACK_TYPE, 0); if (num_tracks > 0) stream_type = LMS_STREAM_TYPE_VIDEO; - MP4GetMetadataName(mp4_fh, &info.title.str); - if (info.title.str) - info.title.len = strlen(info.title.str); - MP4GetMetadataArtist(mp4_fh, &info.artist.str); - if (info.artist.str) - info.artist.len = strlen(info.artist.str); + info.length = MP4GetDuration(mp4_fh) / + MP4GetTimeScale(mp4_fh) ?: 1; if (stream_type == LMS_STREAM_TYPE_AUDIO) { - u_int16_t total_tracks; + MP4TrackId id; - MP4GetMetadataAlbum(mp4_fh, &info.album.str); - if (info.album.str) - info.album.len = strlen(info.album.str); - MP4GetMetadataGenre(mp4_fh, &info.genre.str); - if (info.genre.str) - info.genre.len = strlen(info.genre.str); - - MP4GetMetadataTrack(mp4_fh, &info.trackno, &total_tracks); + lms_string_size_strndup(&info.album, tags->album, -1); + lms_string_size_strndup(&info.genre, tags->genre, -1); + if (tags->track) + info.trackno = tags->track->index; + + id = MP4FindTrackId(mp4_fh, 0, MP4_AUDIO_TRACK_TYPE, 0); + audio_info.bitrate = MP4GetTrackBitRate(mp4_fh, id); + audio_info.channels = MP4GetTrackAudioChannels(mp4_fh, id); + audio_info.sampling_rate = MP4GetTrackTimeScale(mp4_fh, id); + audio_info.length = info.length; + audio_info.codec = _get_audio_codec(mp4_fh, id); + } else { + num_tracks = MP4GetNumberOfTracks(mp4_fh, NULL, 0); + for (i = 0; i < num_tracks; i++) { + MP4TrackId id = MP4FindTrackId(mp4_fh, i, NULL, 0); + const char *type = MP4GetTrackType(mp4_fh, id); + enum lms_stream_type lmstype; + struct lms_stream *s; + + if (strcmp(type, MP4_AUDIO_TRACK_TYPE) == 0) + lmstype = LMS_STREAM_TYPE_AUDIO; + else if (strcmp(type, MP4_VIDEO_TRACK_TYPE) == 0) + lmstype = LMS_STREAM_TYPE_VIDEO; + else + continue; + + s = calloc(1, sizeof(*s)); + s->type = lmstype; + s->stream_id = id; + s->lang = _get_lang(mp4_fh, id); + + if (lmstype == LMS_STREAM_TYPE_AUDIO) { + s->codec = _get_audio_codec(mp4_fh, id); + s->audio.sampling_rate = MP4GetTrackTimeScale(mp4_fh, id); + s->audio.bitrate = MP4GetTrackBitRate(mp4_fh, id); + s->audio.channels = MP4GetTrackAudioChannels(mp4_fh, id); + } else if (lmstype == LMS_STREAM_TYPE_VIDEO) { + s->codec = _get_video_codec(mp4_fh, id); /* malloc() */ + s->video.bitrate = MP4GetTrackBitRate(mp4_fh, id); + s->video.width = MP4GetTrackVideoWidth(mp4_fh, id); + s->video.height = MP4GetTrackVideoHeight(mp4_fh, id); + s->video.framerate = MP4GetTrackVideoFrameRate(mp4_fh, id); + lms_stream_video_info_aspect_ratio_guess(&s->video); + } + + s->next = video_info.streams; + video_info.streams = s; + } + video_info.length = info.length; } lms_string_size_strip_and_free(&info.title); @@ -135,13 +595,11 @@ lms_string_size_strip_and_free(&info.genre); if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - NULL); + 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) @@ -149,41 +607,44 @@ if (info.genre.str) lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len); -#if 0 - fprintf(stderr, "file %s info\n", finfo->path); - fprintf(stderr, "\ttitle='%s'\n", info.title.str); - fprintf(stderr, "\tartist='%s'\n", info.artist.str); - fprintf(stderr, "\talbum='%s'\n", info.album.str); - fprintf(stderr, "\tgenre='%s'\n", info.genre.str); -#endif - if (stream_type == LMS_STREAM_TYPE_AUDIO) { 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; audio_info.trackno = info.trackno; r = lms_db_audio_add(plugin->audio_db, &audio_info); - } - else { + } else { video_info.id = finfo->id; video_info.title = info.title; video_info.artist = info.artist; + video_info.container = _container; r = lms_db_video_add(plugin->video_db, &video_info); } -#ifdef HAVE_MP4V2_2_0_API - MP4Close(mp4_fh, 0); -#else - MP4Close(mp4_fh); -#endif +fail: + MP4TagsFree(tags); free(info.title.str); free(info.artist.str); free(info.album.str); free(info.genre.str); + while (video_info.streams) { + struct lms_stream *s = video_info.streams; + video_info.streams = s->next; + if (s->type == LMS_STREAM_TYPE_VIDEO) { + free(s->codec.str); /* ugly, but h264 needs alloc */ + free(s->video.aspect_ratio.str); + } + free(s->lang.str); + free(s); + } + + MP4Close(mp4_fh, 0); + return r; } diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/ogg/ogg.c lightmediascanner-0.4.5.0~201401231350/src/plugins/ogg/ogg.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/ogg/ogg.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/ogg/ogg.c 2013-12-09 17:51:42.000000000 +0000 @@ -33,7 +33,6 @@ #include #include #include -#include #include #include @@ -113,19 +112,10 @@ static void _set_lms_info(struct lms_string_size *info, const char *tag) { - int size; - - if (!info || !tag) + if (!info) return; - size = strlen(tag); - - if (!size) - return; - - info->len = size; - info->str = malloc(size * sizeof(char)); - memcpy(info->str, tag, size); + lms_string_size_strndup(info, tag, -1); lms_string_size_strip_and_free(info); } @@ -327,6 +317,7 @@ if (s->base.type == LMS_STREAM_TYPE_AUDIO) { s->base.codec = _audio_codec; s->base.audio.channels = s->audio.vi.channels; + s->base.audio.sampling_rate = s->audio.vi.rate; s->base.audio.bitrate = s->audio.vi.bitrate_nominal; } else if (s->base.type == LMS_STREAM_TYPE_VIDEO) { unsigned int num, den; @@ -342,13 +333,7 @@ num = s->video.ti.aspect_numerator; den = s->video.ti.aspect_denominator; - if (num && den) { - reduce_gcd(num, den, &num, &den); - asprintf(&s->base.video.aspect_ratio.str, "%u:%u", num, den); - s->base.video.aspect_ratio.len = - s->base.video.aspect_ratio.str ? - strlen(s->base.video.aspect_ratio.str) : 0; - } + lms_aspect_ratio_guess(&s->base.video.aspect_ratio, num, den); } } @@ -464,6 +449,7 @@ static const char _name[] = "ogg"; static const struct lms_string_size _exts[] = { LMS_STATIC_STRING_SIZE(".ogg"), + LMS_STATIC_STRING_SIZE(".oga"), LMS_STATIC_STRING_SIZE(".ogv") }; static const char *_cats[] = { @@ -507,10 +493,9 @@ goto done; if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - NULL); + 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) @@ -544,6 +529,7 @@ video_info.id = finfo->id; video_info.title = info.title; video_info.artist = info.artist; + video_info.container = _container; video_info.streams = (struct lms_stream *) info.streams; r = lms_db_video_add(plugin->video_db, &video_info); } diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/pls/pls.c lightmediascanner-0.4.5.0~201401231350/src/plugins/pls/pls.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/pls/pls.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/pls/pls.c 2013-12-09 17:51:42.000000000 +0000 @@ -315,10 +315,8 @@ } ext_idx = ((long)match) - 1; - info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len; - info.title.str = malloc((info.title.len + 1) * sizeof(char)); - memcpy(info.title.str, finfo->path + finfo->base, info.title.len); - info.title.str[info.title.len] = '\0'; + lms_string_size_strndup(&info.title, finfo->path + finfo->base, + finfo->path_len - finfo->base - _exts[ext_idx].len); lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len); info.id = finfo->id; diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/png/png.c lightmediascanner-0.4.5.0~201401231350/src/plugins/png/png.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/png/png.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/png/png.c 2013-12-09 17:51:42.000000000 +0000 @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -173,10 +172,9 @@ info.date = finfo->mtime; if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - NULL); + 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) diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/rm/rm.c lightmediascanner-0.4.5.0~201401231350/src/plugins/rm/rm.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/rm/rm.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/rm/rm.c 2013-12-09 17:51:42.000000000 +0000 @@ -29,7 +29,6 @@ #include #include -#include #include #include @@ -457,10 +456,9 @@ lms_string_size_strip_and_free(&info.artist); if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - NULL); + 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); diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/shared/util.h lightmediascanner-0.4.5.0~201401231350/src/plugins/shared/util.h --- lightmediascanner-0.4.5.0~201309060911/src/plugins/shared/util.h 1970-01-01 00:00:00.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/shared/util.h 2013-12-09 17:51:42.000000000 +0000 @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * 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 Lucas De Marchi + */ + +#include +#include +#include +#include + +#include + +#define NSEC100_PER_SEC 10000000ULL +#define MSEC_PER_SEC 1000ULL + +#define get_unaligned(ptr) \ + ({ \ + struct __attribute__((packed)) { \ + typeof(*(ptr)) __v; \ + } *__p = (typeof(__p)) (ptr); \ + __p->__v; \ + }) + +#if __BYTE_ORDER == __LITTLE_ENDIAN +static inline uint64_t get_le64(const void *ptr) +{ + return get_unaligned((const uint64_t *) ptr); +} + +static inline uint64_t get_be64(const void *ptr) +{ + return bswap_64(get_unaligned((const uint64_t *) ptr)); +} + +static inline uint32_t get_le32(const void *ptr) +{ + return get_unaligned((const uint32_t *) ptr); +} + +static inline uint32_t get_be32(const void *ptr) +{ + return bswap_32(get_unaligned((const uint32_t *) ptr)); +} + +static inline uint16_t get_le16(const void *ptr) +{ + return get_unaligned((const uint16_t *) ptr); +} + +static inline uint16_t get_be16(const void *ptr) +{ + return bswap_16(get_unaligned((const uint16_t *) ptr)); +} + +#elif __BYTE_ORDER == __BIG_ENDIAN +static inline uint64_t get_le64(const void *ptr) +{ + return bswap_64(get_unaligned((const uint64_t *) ptr)); +} + +static inline uint64_t get_be64(const void *ptr) +{ + return get_unaligned((const uint64_t *) ptr); +} + +static inline uint32_t get_le32(const void *ptr) +{ + return bswap_32(get_unaligned((const uint32_t *) ptr)); +} + +static inline uint32_t get_be32(const void *ptr) +{ + return get_unaligned((const uint32_t *) ptr); +} + +static inline uint16_t get_le16(const void *ptr) +{ + return bswap_16(get_unaligned((const uint16_t *) ptr)); +} + +static inline uint16_t get_be16(const void *ptr) +{ + return get_unaligned((const uint16_t *) ptr); +} + +#else +#error "Unknown byte order" +#endif diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/video-dummy/video-dummy.c lightmediascanner-0.4.5.0~201401231350/src/plugins/video-dummy/video-dummy.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/video-dummy/video-dummy.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/video-dummy/video-dummy.c 2013-12-09 17:51:42.000000000 +0000 @@ -76,10 +76,8 @@ long ext_idx; ext_idx = ((long)match) - 1; - info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len; - info.title.str = malloc((info.title.len + 1) * sizeof(char)); - memcpy(info.title.str, finfo->path + finfo->base, info.title.len); - info.title.str[info.title.len] = '\0'; + lms_string_size_strndup(&info.title, finfo->path + finfo->base, + finfo->path_len - finfo->base - _exts[ext_idx].len); lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len); info.id = finfo->id; diff -Nru lightmediascanner-0.4.5.0~201309060911/src/plugins/wave/wave.c lightmediascanner-0.4.5.0~201401231350/src/plugins/wave/wave.c --- lightmediascanner-0.4.5.0~201309060911/src/plugins/wave/wave.c 2013-08-30 16:25:48.000000000 +0000 +++ lightmediascanner-0.4.5.0~201401231350/src/plugins/wave/wave.c 2013-12-09 17:51:42.000000000 +0000 @@ -254,10 +254,9 @@ _parse_info(fd, &info); if (!info.title.str) - info.title = str_extract_name_from_path(finfo->path, finfo->path_len, - finfo->base, - &_exts[((long) match) - 1], - ctxt->cs_conv); + lms_name_from_path(&info.title, finfo->path, finfo->path_len, + finfo->base, _exts[((long) match) - 1].len, + ctxt->cs_conv); info.id = finfo->id;