2 * Copyright (C) 2008 by INdT
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * @author Andre Moreira Magalhaes <andre.magalhaes@openbossa.org>
27 static const char PV[] = PACKAGE_VERSION; /* mp4.h screws PACKAGE_VERSION */
29 #include <lightmediascanner_plugin.h>
30 #include <lightmediascanner_db.h>
31 #include <shared/util.h>
34 #include <mp4v2/mp4v2.h>
42 struct lms_string_size title;
43 struct lms_string_size artist;
44 struct lms_string_size album;
45 struct lms_string_size genre;
51 struct lms_plugin plugin;
52 lms_db_audio_t *audio_db;
53 lms_db_video_t *video_db;
56 #define DECL_STR(cname, str) \
57 static const struct lms_string_size cname = LMS_STATIC_STRING_SIZE(str)
59 DECL_STR(_codec_audio_mpeg4aac_main, "mpeg4aac-main");
60 DECL_STR(_codec_audio_mpeg4aac_lc, "mpeg4aac-lc");
61 DECL_STR(_codec_audio_mpeg4aac_ssr, "mpeg4aac-ssr");
62 DECL_STR(_codec_audio_mpeg4aac_ltp, "mpeg4aac-ltp");
63 DECL_STR(_codec_audio_mpeg4aac_he, "mpeg4aac-he");
64 DECL_STR(_codec_audio_mpeg4aac_scalable, "mpeg4aac-scalable");
65 DECL_STR(_codec_audio_mpeg4_twinvq, "mpeg4twinvq");
66 DECL_STR(_codec_audio_mpeg4_celp, "mpeg4celp");
67 DECL_STR(_codec_audio_mpeg4_hvxc, "mpeg4hvxc");
69 DECL_STR(_codec_audio_mpeg4_tssi, "mpeg4ttsi");
70 DECL_STR(_codec_audio_mpeg4_main_synthetic,"mpeg4main-synthetic");
71 DECL_STR(_codec_audio_mpeg4_wavetable_syn, "mpeg4wavetable-syn");
72 DECL_STR(_codec_audio_mpeg4_general_midi, "mpeg4general-midi");
73 DECL_STR(_codec_audio_mpeg4_algo_syn_and_audio_fx, "mpeg4algo-syn-and-audio-fx");
74 DECL_STR(_codec_audio_mpeg4_er_aac_lc, "mpeg4er-aac-lc");
76 DECL_STR(_codec_audio_mpeg4_er_aac_ltp, "mpeg4er-aac-ltp");
77 DECL_STR(_codec_audio_mpeg4_er_aac_scalable, "mpeg4er-aac-scalable");
78 DECL_STR(_codec_audio_mpeg4_er_twinvq, "mpeg4er-twinvq");
79 DECL_STR(_codec_audio_mpeg4_er_bsac, "mpeg4er-bsac");
80 DECL_STR(_codec_audio_mpeg4_er_acc_ld, "mpeg4er-acc-ld");
81 DECL_STR(_codec_audio_mpeg4_er_celp, "mpeg4er-celp");
82 DECL_STR(_codec_audio_mpeg4_er_hvxc, "mpeg4er-hvxc");
83 DECL_STR(_codec_audio_mpeg4_er_hiln, "mpeg4er-hiln");
84 DECL_STR(_codec_audio_mpeg4_er_parametric, "mpeg4er-parametric");
85 DECL_STR(_codec_audio_mpeg4_ssc, "mpeg4ssc");
86 DECL_STR(_codec_audio_mpeg4_ps, "mpeg4ps");
87 DECL_STR(_codec_audio_mpeg4_mpeg_surround, "mpeg4mpeg-surround");
89 DECL_STR(_codec_audio_mpeg4_layer1, "mpeg4layer1");
90 DECL_STR(_codec_audio_mpeg4_layer2, "mpeg4layer2");
91 DECL_STR(_codec_audio_mpeg4_layer3, "mpeg4layer3");
92 DECL_STR(_codec_audio_mpeg4_dst, "mpeg4dst");
93 DECL_STR(_codec_audio_mpeg4_audio_lossless, "mpeg4audio-lossless");
94 DECL_STR(_codec_audio_mpeg4_sls, "mpeg4sls");
95 DECL_STR(_codec_audio_mpeg4_sls_non_core, "mpeg4sls-non-core");
98 DECL_STR(_codec_audio_amr, "amr");
99 DECL_STR(_codec_audio_amr_wb, "amr-wb");
102 static const struct lms_string_size *_audio_codecs[] = {
103 &_codec_audio_mpeg4aac_main,
104 &_codec_audio_mpeg4aac_lc,
105 &_codec_audio_mpeg4aac_ssr,
106 &_codec_audio_mpeg4aac_ltp,
107 &_codec_audio_mpeg4aac_he,
108 &_codec_audio_mpeg4aac_scalable,
109 &_codec_audio_mpeg4_twinvq,
110 &_codec_audio_mpeg4_celp,
111 &_codec_audio_mpeg4_hvxc,
113 &_codec_audio_mpeg4_tssi,
114 &_codec_audio_mpeg4_main_synthetic,
115 &_codec_audio_mpeg4_wavetable_syn,
116 &_codec_audio_mpeg4_general_midi,
117 &_codec_audio_mpeg4_algo_syn_and_audio_fx,
118 &_codec_audio_mpeg4_er_aac_lc,
120 &_codec_audio_mpeg4_er_aac_ltp,
121 &_codec_audio_mpeg4_er_aac_scalable,
122 &_codec_audio_mpeg4_er_twinvq,
123 &_codec_audio_mpeg4_er_bsac,
124 &_codec_audio_mpeg4_er_acc_ld,
125 &_codec_audio_mpeg4_er_celp,
126 &_codec_audio_mpeg4_er_hvxc,
127 &_codec_audio_mpeg4_er_hiln,
128 &_codec_audio_mpeg4_er_parametric,
129 &_codec_audio_mpeg4_ssc,
130 &_codec_audio_mpeg4_ps,
131 &_codec_audio_mpeg4_mpeg_surround,
133 &_codec_audio_mpeg4_layer1,
134 &_codec_audio_mpeg4_layer2,
135 &_codec_audio_mpeg4_layer3,
136 &_codec_audio_mpeg4_dst,
137 &_codec_audio_mpeg4_audio_lossless,
138 &_codec_audio_mpeg4_sls,
139 &_codec_audio_mpeg4_sls_non_core,
141 #define N_AUDIO_CODECS (sizeof(_audio_codecs) / sizeof(_audio_codecs[0]))
143 static const char _name[] = "mp4";
144 static const struct lms_string_size _exts[] = {
145 LMS_STATIC_STRING_SIZE(".mp4"),
146 LMS_STATIC_STRING_SIZE(".m4a"),
147 LMS_STATIC_STRING_SIZE(".m4v"),
148 LMS_STATIC_STRING_SIZE(".mov"),
149 LMS_STATIC_STRING_SIZE(".qt"),
150 LMS_STATIC_STRING_SIZE(".3gp")
152 static const char *_cats[] = {
158 static const char *_authors[] = {
159 "Andre Moreira Magalhaes",
164 _match(struct plugin *p, const char *path, int len, int base)
168 i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
172 return (void*)(i + 1);
175 #ifdef HAVE_MP4V2_2_0_API
176 static struct lms_string_size
177 _get_audio_codec(MP4FileHandle mp4_fh, MP4TrackId id)
179 const char *data_name = MP4GetTrackMediaDataName(mp4_fh, id);
180 struct lms_string_size nullret = { };
185 if (strcasecmp(data_name, "samr") == 0)
186 return _codec_audio_amr;
187 if (strcasecmp(data_name, "sawb") == 0)
188 return _codec_audio_amr_wb;
189 if ((strcasecmp(data_name, "mp4a") != 0) ||
190 (type = MP4GetTrackEsdsObjectTypeId(mp4_fh, id)) != MP4_MPEG4_AUDIO_TYPE ||
191 (type = MP4GetTrackAudioMpeg4Type(mp4_fh, id)) == 0 ||
192 (type > N_AUDIO_CODECS) ||
193 (_audio_codecs[type - 1] == NULL))
196 return *_audio_codecs[type - 1];
200 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
202 struct mp4_info info = { };
203 struct lms_audio_info audio_info = { };
204 struct lms_video_info video_info = { };
205 int r, stream_type = LMS_STREAM_TYPE_AUDIO;
206 MP4FileHandle mp4_fh;
207 u_int32_t num_tracks, i;
210 mp4_fh = MP4Read(finfo->path);
211 if (mp4_fh == MP4_INVALID_FILE_HANDLE) {
212 fprintf(stderr, "ERROR: cannot read mp4 file %s\n", finfo->path);
216 tags = MP4TagsAlloc();
220 if (!MP4TagsFetch(tags, mp4_fh)) {
225 #define STR_FIELD_FROM_TAG(_tags_field, _field) \
228 _field.len = strlen(_tags_field); \
229 _field.str = malloc(_field.len); \
230 memcpy(_field.str, _tags_field, _field.len + 1); \
234 STR_FIELD_FROM_TAG(tags->name, info.title);
235 STR_FIELD_FROM_TAG(tags->artist, info.artist);
237 /* check if the file contains a video track */
238 num_tracks = MP4GetNumberOfTracks(mp4_fh, MP4_VIDEO_TRACK_TYPE, 0);
240 stream_type = LMS_STREAM_TYPE_VIDEO;
242 info.length = MP4GetDuration(mp4_fh) /
243 MP4GetTimeScale(mp4_fh) ?: 1;
245 if (stream_type == LMS_STREAM_TYPE_AUDIO) {
248 STR_FIELD_FROM_TAG(tags->album, info.album);
249 STR_FIELD_FROM_TAG(tags->genre, info.genre);
251 info.trackno = tags->track->index;
253 id = MP4FindTrackId(mp4_fh, 0, MP4_AUDIO_TRACK_TYPE, 0);
254 audio_info.bitrate = MP4GetTrackBitRate(mp4_fh, id);
255 audio_info.channels = MP4GetTrackAudioChannels(mp4_fh, id);
256 audio_info.sampling_rate = MP4GetTrackTimeScale(mp4_fh, id);
257 audio_info.length = info.length;
258 audio_info.codec = _get_audio_codec(mp4_fh, id);
260 num_tracks = MP4GetNumberOfTracks(mp4_fh, NULL, 0);
261 for (i = 0; i < num_tracks; i++) {
262 MP4TrackId id = MP4FindTrackId(mp4_fh, i, NULL, 0);
263 const char *type = MP4GetTrackType(mp4_fh, id);
264 enum lms_stream_type lmstype;
265 struct lms_stream *s;
267 if (strcmp(type, MP4_AUDIO_TRACK_TYPE) == 0)
268 lmstype = LMS_STREAM_TYPE_AUDIO;
269 else if (strcmp(type, MP4_VIDEO_TRACK_TYPE) == 0)
270 lmstype = LMS_STREAM_TYPE_VIDEO;
274 s = calloc(1, sizeof(*s));
278 if (lmstype == LMS_STREAM_TYPE_AUDIO) {
279 s->audio.channels = MP4GetTrackAudioChannels(mp4_fh, id);
280 s->audio.bitrate = MP4GetTrackBitRate(mp4_fh, id);
281 s->codec = _get_audio_codec(mp4_fh, id);
282 } else if (lmstype == LMS_STREAM_TYPE_VIDEO) {
283 s->video.width = MP4GetTrackVideoWidth(mp4_fh, id);
284 s->video.height = MP4GetTrackVideoHeight(mp4_fh, id);
285 s->video.framerate = MP4GetTrackVideoFrameRate(mp4_fh, id);
288 s->next = video_info.streams;
289 video_info.streams = s;
291 video_info.length = info.length;
293 #undef STR_FIELD_FROM_TAG
295 lms_string_size_strip_and_free(&info.title);
296 lms_string_size_strip_and_free(&info.artist);
297 lms_string_size_strip_and_free(&info.album);
298 lms_string_size_strip_and_free(&info.genre);
301 info.title = str_extract_name_from_path(finfo->path, finfo->path_len,
303 &_exts[((long) match) - 1],
306 lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
308 lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
310 lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
312 lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
314 if (stream_type == LMS_STREAM_TYPE_AUDIO) {
315 audio_info.id = finfo->id;
316 audio_info.title = info.title;
317 audio_info.artist = info.artist;
318 audio_info.album = info.album;
319 audio_info.genre = info.genre;
320 audio_info.trackno = info.trackno;
321 r = lms_db_audio_add(plugin->audio_db, &audio_info);
323 video_info.id = finfo->id;
324 video_info.title = info.title;
325 video_info.artist = info.artist;
326 r = lms_db_video_add(plugin->video_db, &video_info);
332 free(info.title.str);
333 free(info.artist.str);
334 free(info.album.str);
335 free(info.genre.str);
337 while (video_info.streams) {
338 struct lms_stream *s = video_info.streams;
339 video_info.streams = s->next;
349 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
351 struct mp4_info info = { };
352 struct lms_audio_info audio_info = { };
353 struct lms_video_info video_info = { };
354 int r, stream_type = LMS_STREAM_TYPE_AUDIO;
355 MP4FileHandle mp4_fh;
356 u_int32_t num_tracks;
358 mp4_fh = MP4Read(finfo->path, 0);
359 if (mp4_fh == MP4_INVALID_FILE_HANDLE) {
360 fprintf(stderr, "ERROR: cannot read mp4 file %s\n", finfo->path);
364 /* check if the file contains a video track */
365 num_tracks = MP4GetNumberOfTracks(mp4_fh, MP4_VIDEO_TRACK_TYPE, 0);
367 stream_type = LMS_STREAM_TYPE_VIDEO;
369 MP4GetMetadataName(mp4_fh, &info.title.str);
371 info.title.len = strlen(info.title.str);
372 MP4GetMetadataArtist(mp4_fh, &info.artist.str);
374 info.artist.len = strlen(info.artist.str);
376 if (stream_type == LMS_STREAM_TYPE_AUDIO) {
377 u_int16_t total_tracks;
379 MP4GetMetadataAlbum(mp4_fh, &info.album.str);
381 info.album.len = strlen(info.album.str);
382 MP4GetMetadataGenre(mp4_fh, &info.genre.str);
384 info.genre.len = strlen(info.genre.str);
386 MP4GetMetadataTrack(mp4_fh, &info.trackno, &total_tracks);
389 lms_string_size_strip_and_free(&info.title);
390 lms_string_size_strip_and_free(&info.artist);
391 lms_string_size_strip_and_free(&info.album);
392 lms_string_size_strip_and_free(&info.genre);
395 info.title = str_extract_name_from_path(finfo->path, finfo->path_len,
397 &_exts[((long) match) - 1],
400 lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
403 lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
405 lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
407 lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
410 fprintf(stderr, "file %s info\n", finfo->path);
411 fprintf(stderr, "\ttitle='%s'\n", info.title.str);
412 fprintf(stderr, "\tartist='%s'\n", info.artist.str);
413 fprintf(stderr, "\talbum='%s'\n", info.album.str);
414 fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
417 if (stream_type == LMS_STREAM_TYPE_AUDIO) {
418 audio_info.id = finfo->id;
419 audio_info.title = info.title;
420 audio_info.artist = info.artist;
421 audio_info.album = info.album;
422 audio_info.genre = info.genre;
423 audio_info.trackno = info.trackno;
424 r = lms_db_audio_add(plugin->audio_db, &audio_info);
427 video_info.id = finfo->id;
428 video_info.title = info.title;
429 video_info.artist = info.artist;
430 r = lms_db_video_add(plugin->video_db, &video_info);
435 free(info.title.str);
436 free(info.artist.str);
437 free(info.album.str);
438 free(info.genre.str);
445 _setup(struct plugin *plugin, struct lms_context *ctxt)
447 plugin->audio_db = lms_db_audio_new(ctxt->db);
448 if (!plugin->audio_db)
450 plugin->video_db = lms_db_video_new(ctxt->db);
451 if (!plugin->video_db)
458 _start(struct plugin *plugin, struct lms_context *ctxt)
461 r = lms_db_audio_start(plugin->audio_db);
462 r |= lms_db_video_start(plugin->video_db);
467 _finish(struct plugin *plugin, struct lms_context *ctxt)
469 if (plugin->audio_db)
470 lms_db_audio_free(plugin->audio_db);
471 if (plugin->video_db)
472 lms_db_video_free(plugin->video_db);
478 _close(struct plugin *plugin)
484 API struct lms_plugin *
485 lms_plugin_open(void)
487 struct plugin *plugin;
489 plugin = (struct plugin *)malloc(sizeof(*plugin));
490 plugin->plugin.name = _name;
491 plugin->plugin.match = (lms_plugin_match_fn_t)_match;
492 plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
493 plugin->plugin.close = (lms_plugin_close_fn_t)_close;
494 plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
495 plugin->plugin.start = (lms_plugin_start_fn_t)_start;
496 plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
498 return (struct lms_plugin *)plugin;
501 API const struct lms_plugin_info *
502 lms_plugin_info(void)
504 static struct lms_plugin_info info = {
507 "MP4 files (MP4, M4A, MOV, QT, 3GP)",
510 "http://lms.garage.maemo.org"