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 static const char _name[] = "mp4";
57 static const struct lms_string_size _exts[] = {
58 LMS_STATIC_STRING_SIZE(".mp4"),
59 LMS_STATIC_STRING_SIZE(".m4a"),
60 LMS_STATIC_STRING_SIZE(".m4v"),
61 LMS_STATIC_STRING_SIZE(".mov"),
62 LMS_STATIC_STRING_SIZE(".qt"),
63 LMS_STATIC_STRING_SIZE(".3gp")
65 static const char *_cats[] = {
71 static const char *_authors[] = {
72 "Andre Moreira Magalhaes",
77 _match(struct plugin *p, const char *path, int len, int base)
81 i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
85 return (void*)(i + 1);
88 #ifdef HAVE_MP4V2_2_0_API
90 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
92 struct mp4_info info = { };
93 struct lms_audio_info audio_info = { };
94 struct lms_video_info video_info = { };
95 int r, stream_type = LMS_STREAM_TYPE_AUDIO;
97 u_int32_t num_tracks, i;
100 mp4_fh = MP4Read(finfo->path);
101 if (mp4_fh == MP4_INVALID_FILE_HANDLE) {
102 fprintf(stderr, "ERROR: cannot read mp4 file %s\n", finfo->path);
106 tags = MP4TagsAlloc();
110 if (!MP4TagsFetch(tags, mp4_fh)) {
115 #define STR_FIELD_FROM_TAG(_tags_field, _field) \
118 _field.len = strlen(_tags_field); \
119 _field.str = malloc(_field.len); \
120 memcpy(_field.str, _tags_field, _field.len + 1); \
124 STR_FIELD_FROM_TAG(tags->name, info.title);
125 STR_FIELD_FROM_TAG(tags->artist, info.artist);
127 /* check if the file contains a video track */
128 num_tracks = MP4GetNumberOfTracks(mp4_fh, MP4_VIDEO_TRACK_TYPE, 0);
130 stream_type = LMS_STREAM_TYPE_VIDEO;
132 info.length = MP4GetDuration(mp4_fh) /
133 MP4GetTimeScale(mp4_fh) ?: 1;
135 if (stream_type == LMS_STREAM_TYPE_AUDIO) {
138 STR_FIELD_FROM_TAG(tags->album, info.album);
139 STR_FIELD_FROM_TAG(tags->genre, info.genre);
141 info.trackno = tags->track->index;
143 id = MP4FindTrackId(mp4_fh, 0, MP4_AUDIO_TRACK_TYPE, 0);
144 audio_info.bitrate = MP4GetTrackBitRate(mp4_fh, id);
145 audio_info.channels = MP4GetTrackAudioChannels(mp4_fh, id);
146 audio_info.sampling_rate = MP4GetTrackTimeScale(mp4_fh, id);
147 audio_info.length = info.length;
149 num_tracks = MP4GetNumberOfTracks(mp4_fh, NULL, 0);
150 for (i = 0; i < num_tracks; i++) {
151 MP4TrackId id = MP4FindTrackId(mp4_fh, i, NULL, 0);
152 const char *type = MP4GetTrackType(mp4_fh, id);
153 enum lms_stream_type lmstype;
154 struct lms_stream *s;
156 if (strcmp(type, MP4_AUDIO_TRACK_TYPE) == 0)
157 lmstype = LMS_STREAM_TYPE_AUDIO;
158 else if (strcmp(type, MP4_VIDEO_TRACK_TYPE) == 0)
159 lmstype = LMS_STREAM_TYPE_VIDEO;
163 s = calloc(1, sizeof(*s));
167 if (lmstype == LMS_STREAM_TYPE_AUDIO) {
168 s->audio.channels = MP4GetTrackAudioChannels(mp4_fh, id);
169 s->audio.bitrate = MP4GetTrackBitRate(mp4_fh, id);
170 } else if (lmstype == LMS_STREAM_TYPE_VIDEO) {
171 s->video.width = MP4GetTrackVideoWidth(mp4_fh, id);
172 s->video.height = MP4GetTrackVideoHeight(mp4_fh, id);
173 s->video.framerate = MP4GetTrackVideoFrameRate(mp4_fh, id);
176 s->next = video_info.streams;
177 video_info.streams = s;
179 video_info.length = info.length;
181 #undef STR_FIELD_FROM_TAG
183 lms_string_size_strip_and_free(&info.title);
184 lms_string_size_strip_and_free(&info.artist);
185 lms_string_size_strip_and_free(&info.album);
186 lms_string_size_strip_and_free(&info.genre);
189 info.title = str_extract_name_from_path(finfo->path, finfo->path_len,
191 &_exts[((long) match) - 1],
194 lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
196 lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
198 lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
200 lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
202 if (stream_type == LMS_STREAM_TYPE_AUDIO) {
203 audio_info.id = finfo->id;
204 audio_info.title = info.title;
205 audio_info.artist = info.artist;
206 audio_info.album = info.album;
207 audio_info.genre = info.genre;
208 audio_info.trackno = info.trackno;
209 r = lms_db_audio_add(plugin->audio_db, &audio_info);
211 video_info.id = finfo->id;
212 video_info.title = info.title;
213 video_info.artist = info.artist;
214 r = lms_db_video_add(plugin->video_db, &video_info);
220 free(info.title.str);
221 free(info.artist.str);
222 free(info.album.str);
223 free(info.genre.str);
225 while (video_info.streams) {
226 struct lms_stream *s = video_info.streams;
227 video_info.streams = s->next;
237 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
239 struct mp4_info info = { };
240 struct lms_audio_info audio_info = { };
241 struct lms_video_info video_info = { };
242 int r, stream_type = LMS_STREAM_TYPE_AUDIO;
243 MP4FileHandle mp4_fh;
244 u_int32_t num_tracks;
246 mp4_fh = MP4Read(finfo->path, 0);
247 if (mp4_fh == MP4_INVALID_FILE_HANDLE) {
248 fprintf(stderr, "ERROR: cannot read mp4 file %s\n", finfo->path);
252 /* check if the file contains a video track */
253 num_tracks = MP4GetNumberOfTracks(mp4_fh, MP4_VIDEO_TRACK_TYPE, 0);
255 stream_type = LMS_STREAM_TYPE_VIDEO;
257 MP4GetMetadataName(mp4_fh, &info.title.str);
259 info.title.len = strlen(info.title.str);
260 MP4GetMetadataArtist(mp4_fh, &info.artist.str);
262 info.artist.len = strlen(info.artist.str);
264 if (stream_type == LMS_STREAM_TYPE_AUDIO) {
265 u_int16_t total_tracks;
267 MP4GetMetadataAlbum(mp4_fh, &info.album.str);
269 info.album.len = strlen(info.album.str);
270 MP4GetMetadataGenre(mp4_fh, &info.genre.str);
272 info.genre.len = strlen(info.genre.str);
274 MP4GetMetadataTrack(mp4_fh, &info.trackno, &total_tracks);
277 lms_string_size_strip_and_free(&info.title);
278 lms_string_size_strip_and_free(&info.artist);
279 lms_string_size_strip_and_free(&info.album);
280 lms_string_size_strip_and_free(&info.genre);
283 info.title = str_extract_name_from_path(finfo->path, finfo->path_len,
285 &_exts[((long) match) - 1],
288 lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
291 lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
293 lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
295 lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
298 fprintf(stderr, "file %s info\n", finfo->path);
299 fprintf(stderr, "\ttitle='%s'\n", info.title.str);
300 fprintf(stderr, "\tartist='%s'\n", info.artist.str);
301 fprintf(stderr, "\talbum='%s'\n", info.album.str);
302 fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
305 if (stream_type == LMS_STREAM_TYPE_AUDIO) {
306 audio_info.id = finfo->id;
307 audio_info.title = info.title;
308 audio_info.artist = info.artist;
309 audio_info.album = info.album;
310 audio_info.genre = info.genre;
311 audio_info.trackno = info.trackno;
312 r = lms_db_audio_add(plugin->audio_db, &audio_info);
315 video_info.id = finfo->id;
316 video_info.title = info.title;
317 video_info.artist = info.artist;
318 r = lms_db_video_add(plugin->video_db, &video_info);
323 free(info.title.str);
324 free(info.artist.str);
325 free(info.album.str);
326 free(info.genre.str);
333 _setup(struct plugin *plugin, struct lms_context *ctxt)
335 plugin->audio_db = lms_db_audio_new(ctxt->db);
336 if (!plugin->audio_db)
338 plugin->video_db = lms_db_video_new(ctxt->db);
339 if (!plugin->video_db)
346 _start(struct plugin *plugin, struct lms_context *ctxt)
349 r = lms_db_audio_start(plugin->audio_db);
350 r |= lms_db_video_start(plugin->video_db);
355 _finish(struct plugin *plugin, struct lms_context *ctxt)
357 if (plugin->audio_db)
358 lms_db_audio_free(plugin->audio_db);
359 if (plugin->video_db)
360 lms_db_video_free(plugin->video_db);
366 _close(struct plugin *plugin)
372 API struct lms_plugin *
373 lms_plugin_open(void)
375 struct plugin *plugin;
377 plugin = (struct plugin *)malloc(sizeof(*plugin));
378 plugin->plugin.name = _name;
379 plugin->plugin.match = (lms_plugin_match_fn_t)_match;
380 plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
381 plugin->plugin.close = (lms_plugin_close_fn_t)_close;
382 plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
383 plugin->plugin.start = (lms_plugin_start_fn_t)_start;
384 plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
386 return (struct lms_plugin *)plugin;
389 API const struct lms_plugin_info *
390 lms_plugin_info(void)
392 static struct lms_plugin_info info = {
395 "MP4 files (MP4, M4A, MOV, QT, 3GP)",
398 "http://lms.garage.maemo.org"