Parse m4a files using mp4 plugin.
[lms] / lightmediascanner / src / plugins / mp4 / mp4.c
1 /**
2  * Copyright (C) 2008 by INdT
3  *
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.
8  *
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.
13  *
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * @author Andre Moreira Magalhaes <andre.magalhaes@openbossa.org>
19  */
20
21 /**
22  * @brief
23  *
24  * mp4 file parser.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <lightmediascanner_plugin.h>
32 #include <lightmediascanner_db.h>
33 #include <mp4.h>
34
35 enum StreamTypes {
36     STREAM_TYPE_UNKNOWN = 0,
37     STREAM_TYPE_AUDIO,
38     STREAM_TYPE_VIDEO
39 };
40
41 struct mp4_info {
42     struct lms_string_size title;
43     struct lms_string_size artist;
44     struct lms_string_size album;
45     struct lms_string_size genre;
46 };
47
48 struct plugin {
49     struct lms_plugin plugin;
50     lms_db_audio_t *audio_db;
51     lms_db_video_t *video_db;
52 };
53
54 static const char _name[] = "mp4";
55 static const struct lms_string_size _exts[] = {
56     LMS_STATIC_STRING_SIZE(".mp4"),
57     LMS_STATIC_STRING_SIZE(".m4a")
58 };
59
60 static void *
61 _match(struct plugin *p, const char *path, int len, int base)
62 {
63     int i;
64
65     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
66     if (i < 0)
67       return NULL;
68     else
69       return (void*)(i + 1);
70 }
71
72 static int
73 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
74 {
75     struct mp4_info info = {{0}, {0}, {0}, {0}};
76     struct lms_audio_info audio_info = {0, {0}, {0}, {0}, {0}, 0, 0, 0};
77     struct lms_video_info video_info = {0, {0}, {0}};
78     int r, stream_type = STREAM_TYPE_AUDIO;
79     MP4FileHandle mp4_fh;
80     u_int32_t num_tracks;
81
82     mp4_fh = MP4Read(finfo->path, 0);
83     if (mp4_fh == MP4_INVALID_FILE_HANDLE) {
84         fprintf(stderr, "ERROR: cannot read mp4 file\n");
85         return -1;
86     }
87
88     MP4GetMetadataName(mp4_fh, &info.title.str);
89     if (info.title.str)
90         info.title.len = strlen(info.title.str);
91     MP4GetMetadataArtist(mp4_fh, &info.artist.str);
92     if (info.artist.str)
93         info.artist.len = strlen(info.artist.str);
94     MP4GetMetadataAlbum(mp4_fh, &info.album.str);
95     if (info.album.str)
96         info.album.len = strlen(info.album.str);
97     MP4GetMetadataGenre(mp4_fh, &info.genre.str);
98     if (info.genre.str)
99         info.genre.len = strlen(info.genre.str);
100
101     /* check if the file contains a video track */
102     num_tracks = MP4GetNumberOfTracks(mp4_fh, MP4_VIDEO_TRACK_TYPE, 0);
103     if (num_tracks > 0)
104         stream_type = STREAM_TYPE_VIDEO;
105
106     lms_string_size_strip_and_free(&info.title);
107     lms_string_size_strip_and_free(&info.artist);
108     lms_string_size_strip_and_free(&info.album);
109     lms_string_size_strip_and_free(&info.genre);
110
111     if (!info.title.str) {
112         int ext_idx;
113         ext_idx = ((int)match) - 1;
114         info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
115         info.title.str = malloc((info.title.len + 1) * sizeof(char));
116         memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
117         info.title.str[info.title.len] = '\0';
118     }
119     lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
120
121     if (info.artist.str)
122         lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
123     if (info.album.str)
124         lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
125     if (info.genre.str)
126         lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
127
128 #if 0
129     fprintf(stderr, "file %s info\n", finfo->path);
130     fprintf(stderr, "\ttitle='%s'\n", info.title.str);
131     fprintf(stderr, "\tartist='%s'\n", info.artist.str);
132     fprintf(stderr, "\talbum='%s'\n", info.album.str);
133     fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
134 #endif
135
136     if (stream_type == STREAM_TYPE_AUDIO) {
137         audio_info.id = finfo->id;
138         audio_info.title = info.title;
139         audio_info.artist = info.artist;
140         audio_info.album = info.album;
141         audio_info.genre = info.genre;
142         r = lms_db_audio_add(plugin->audio_db, &audio_info);
143     }
144     else {
145         video_info.id = finfo->id;
146         video_info.title = info.title;
147         video_info.artist = info.artist;
148         r = lms_db_video_add(plugin->video_db, &video_info);
149     }
150
151     MP4Close(mp4_fh);
152
153     if (info.title.str)
154         free(info.title.str);
155     if (info.artist.str)
156         free(info.artist.str);
157     if (info.album.str)
158         free(info.album.str);
159     if (info.genre.str)
160         free(info.genre.str);
161
162     return r;
163 }
164
165 static int
166 _setup(struct plugin *plugin, struct lms_context *ctxt)
167 {
168     plugin->audio_db = lms_db_audio_new(ctxt->db);
169     if (!plugin->audio_db)
170         return -1;
171     plugin->video_db = lms_db_video_new(ctxt->db);
172     if (!plugin->video_db)
173         return -1;
174
175     return 0;
176 }
177
178 static int
179 _start(struct plugin *plugin, struct lms_context *ctxt)
180 {
181     int r;
182     r = lms_db_audio_start(plugin->audio_db);
183     r |= lms_db_video_start(plugin->video_db);
184     return r;
185 }
186
187 static int
188 _finish(struct plugin *plugin, struct lms_context *ctxt)
189 {
190     if (plugin->audio_db)
191         lms_db_audio_free(plugin->audio_db);
192     if (plugin->video_db)
193         lms_db_video_free(plugin->video_db);
194
195     return 0;
196 }
197
198 static int
199 _close(struct plugin *plugin)
200 {
201     free(plugin);
202     return 0;
203 }
204
205 API struct lms_plugin *
206 lms_plugin_open(void)
207 {
208     struct plugin *plugin;
209
210     plugin = (struct plugin *)malloc(sizeof(*plugin));
211     plugin->plugin.name = _name;
212     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
213     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
214     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
215     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
216     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
217     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
218
219     return (struct lms_plugin *)plugin;
220 }