#include <lightmediascanner_plugin.h>
#include <lightmediascanner_db.h>
-#include <shared/util.h>
#include <mp4v2/mp4v2.h>
#include <string.h>
#define DECL_STR(cname, str) \
static const struct lms_string_size cname = LMS_STATIC_STRING_SIZE(str)
-DECL_STR(_container, "mp4");
+DECL_STR(_container_mp4, "mp4");
+DECL_STR(_container_3gp, "3gp");
DECL_STR(_codec_audio_mpeg4aac_main, "mpeg4aac-main");
DECL_STR(_codec_audio_mpeg4aac_lc, "mpeg4aac-lc");
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_mpeg1audio, "mpeg4aac-lc");
DECL_STR(_codec_audio_pcm16le, "pcm16le");
DECL_STR(_codec_audio_vorbis, "vorbis");
DECL_STR(_codec_audio_alaw, "alaw");
DECL_STR(_codec_audio_amr, "amr");
DECL_STR(_codec_audio_amr_wb, "amr-wb");
-#undef DECL_STR
struct type_str {
uint8_t type;
"Andre Moreira Magalhaes",
"Lucas De Marchi",
"Gustavo Sverzut Barbieri",
+ "Leandro Dorileo",
NULL
};
_get_video_codec(MP4FileHandle mp4_fh, MP4TrackId id)
{
const char *data_name = MP4GetTrackMediaDataName(mp4_fh, id);
- struct lms_string_size ret = {};
+ struct lms_string_size ret = {}, tmp;
char buf[256];
char ofmt[8] = {};
snprintf(str_level, sizeof(str_level), "%u.%u", level / 10,
level % 10);
- ret.len = snprintf(buf, sizeof(buf), "h264-%s-%s",
+ /* fix constrained and 1b case for baseline and main */
+ if (profile == 66 || profile == 77) {
+ uint8_t *sps;
+ uint32_t spslen;
+ bool constrained = false;
+ bool level1b = false;
+
+ if (MP4HaveAtom(mp4_fh,
+ "moov.trak.mdia.minf.stbl.stsd.avc1.avcC") &&
+ MP4GetBytesProperty(mp4_fh, "moov.trak.mdia.minf.stbl.stsd."
+ "avc1.avcC.sequenceEntries."
+ "sequenceParameterSetNALUnit",
+ &sps, &spslen)) {
+ /* SPS (Sequence Parameter Set) is:
+ * 8 bits (1 byte) for profile_idc
+ * 1 bit for constraint_set0_flag
+ * 1 bit for constraint_set1_flag <- we use this for constr.
+ * 1 bit for constraint_set2_flag
+ * 1 bit for constraint_set3_flag <- we use this for 1b
+ * based on ffmpeg's (libavcodec/h264_ps.c) and
+ * x264 (encoder/set.c)
+ */
+ if (spslen > 1) {
+ if ((sps[1] >> 1) & 0x1)
+ constrained = true;
+ if (((sps[1] >> 3) & 0x1) && level / 10 == 1)
+ level1b = true;
+ }
+ free(sps);
+
+ if (constrained) {
+ if (profile == 66)
+ memcpy(str_profile, "constrained-baseline",
+ sizeof("constrained-baseline"));
+ else
+ memcpy(str_profile, "constrained-main",
+ sizeof("constrained-main"));
+ }
+
+ if (level1b)
+ memcpy(str_level, "1b", sizeof("1b"));
+ }
+ }
+
+ ret.len = snprintf(buf, sizeof(buf), "h264-p%s-l%s",
str_profile, str_level);
ret.str = buf;
goto found;
found: /* ugly, but h264 codec is composed in runtime */
- if (ret.str == NULL)
+ if (!lms_string_size_dup(&tmp, &ret))
return nullstr;
- else {
- struct lms_string_size tmp;
-
- tmp.str = malloc(ret.len + 1);
- if (!tmp.str)
- tmp.len = 0;
- else {
- tmp.len = ret.len;
- memcpy(tmp.str, ret.str, ret.len);
- tmp.str[tmp.len] = '\0';
- }
- return tmp;
- }
+ return tmp;
}
static struct lms_string_size
_get_lang(MP4FileHandle mp4_fh, MP4TrackId id)
{
- struct lms_string_size ret = { };
+ struct lms_string_size ret;
char buf[4];
if (!MP4GetTrackLanguage(mp4_fh, id, buf))
if (memcmp(buf, "und", 4) == 0)
return nullstr;
- buf[3] = '\0';
- ret.len = strlen(buf);
- ret.str = malloc(ret.len + 1);
- if (ret.str == NULL)
- ret.len = 0;
- else
- memcpy(ret.str, buf, 4);
+ if (!lms_string_size_strndup(&ret, buf, -1))
+ return nullstr;
return ret;
}
static struct lms_string_size
-_guess_aspect_ratio(const struct lms_stream_video_info *info)
+_get_container(MP4FileHandle mp4_fh)
{
- 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;
+ const char *brand;
- if (info->width == 0 || info->height == 0)
- return nullstr;
+ if (!MP4GetStringProperty(mp4_fh, "ftyp.majorBrand", &brand))
+ return _container_mp4;
- ratio = (double)info->width / (double)info->height;
- for (itr = known_ratios; itr->ratio > 0.0; itr++) {
- if (fabs(ratio - itr->ratio) <= 0.01)
- return itr->str;
- }
+ if (strncasecmp(brand, "3gp", 3) == 0)
+ return _container_3gp;
- return nullstr;
+ /* NOTE: this should be an array, but the C wrapper of mp4v2
+ * doesn't expose a way to give the index number and
+ * ftyp.compatibleBrands is not in counted format (name[idx])
+ */
+ if (!MP4GetStringProperty(mp4_fh, "ftyp.compatibleBrands", &brand))
+ return _container_mp4;
+
+ if (strncasecmp(brand, "3gp", 3) == 0)
+ return _container_3gp;
+
+ return _container_mp4;
}
static int
goto fail;
}
-#define STR_FIELD_FROM_TAG(_tags_field, _field) \
- do { \
- if (_tags_field) { \
- _field.len = strlen(_tags_field); \
- _field.str = malloc(_field.len); \
- memcpy(_field.str, _tags_field, _field.len + 1); \
- } \
- } while (0)
-
- STR_FIELD_FROM_TAG(tags->name, info.title);
- STR_FIELD_FROM_TAG(tags->artist, info.artist);
+ 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 (stream_type == LMS_STREAM_TYPE_AUDIO) {
MP4TrackId id;
- STR_FIELD_FROM_TAG(tags->album, info.album);
- STR_FIELD_FROM_TAG(tags->genre, info.genre);
+ 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;
s->video.width = MP4GetTrackVideoWidth(mp4_fh, id);
s->video.height = MP4GetTrackVideoHeight(mp4_fh, id);
s->video.framerate = MP4GetTrackVideoFrameRate(mp4_fh, id);
- s->video.aspect_ratio = _guess_aspect_ratio(&s->video);
+ lms_stream_video_info_aspect_ratio_guess(&s->video);
}
s->next = video_info.streams;
}
video_info.length = info.length;
}
-#undef STR_FIELD_FROM_TAG
lms_string_size_strip_and_free(&info.title);
lms_string_size_strip_and_free(&info.artist);
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)
audio_info.artist = info.artist;
audio_info.album = info.album;
audio_info.genre = info.genre;
- audio_info.container = _container;
+ audio_info.container = _get_container(mp4_fh);
audio_info.trackno = info.trackno;
+
r = lms_db_audio_add(plugin->audio_db, &audio_info);
} else {
video_info.id = finfo->id;
video_info.title = info.title;
video_info.artist = info.artist;
- video_info.container = _container;
+ video_info.container = _get_container(mp4_fh);
+
r = lms_db_video_add(plugin->video_db, &video_info);
}
while (video_info.streams) {
struct lms_stream *s = video_info.streams;
video_info.streams = s->next;
- if (s->type == LMS_STREAM_TYPE_VIDEO)
+ 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);
}
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 = 0;
return (struct lms_plugin *)plugin;
}
"MP4 files (MP4, M4A, MOV, QT, 3GP)",
PV,
_authors,
- "http://lms.garage.maemo.org"
+ "http://github.com/profusion/lightmediascanner"
};
return &info;