File: mp4file.c

package info (click to toggle)
gtkpod 0.99.14-3
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 7,716 kB
  • ctags: 3,266
  • sloc: ansic: 43,120; sh: 4,837; xml: 1,316; lex: 637; perl: 264; makefile: 236; awk: 73; python: 35
file content (691 lines) | stat: -rw-r--r-- 21,241 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
/* Time-stamp: <2008-09-07 11:16:18 jcs>
|
|  Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
|  Part of the gtkpod project.
| 
|  URL: http://www.gtkpod.org/
|  URL: http://gtkpod.sourceforge.net/
|
|  This program is free software; you can redistribute it and/or modify
|  it under the terms of the GNU General Public License as published by
|  the Free Software Foundation; either version 2 of the License, or
|  (at your option) any later version.
|
|  This program 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 General Public License for more details.
|
|  You should have received a copy of the GNU General Public License
|  along with this program; if not, write to the Free Software
|  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|  iTunes and iPod are trademarks of Apple
|
|  This product is not supported/written/published by Apple!
|
|  $Id: mp4file.c 2206 2009-01-11 13:00:36Z jcsjcs $
*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "charset.h"
#include "itdb.h"
#include "misc.h"
#include "prefs.h"
#include "mp4file.h"



/* ------------------------------------------------------------

   Info on how to implement new file formats.

   You need to supply a function

   Track *xxx_get_file_info (gchar *filename)

   that returns a new Track structure with as many of the following
   fields filled in (in UTF8):

   gchar   *album;            /+ album (utf8)          +/
   gchar   *artist;           /+ artist (utf8)         +/
   gchar   *title;            /+ title (utf8)          +/
   gchar   *genre;            /+ genre (utf8)          +/
   gchar   *comment;          /+ comment (utf8)        +/
   gchar   *composer;         /+ Composer (utf8)       +/
   gchar   *filetype;         /+ Format description (utf8)   +/
   gchar   *charset;          /+ charset used for tags       +/
   gchar   *description;      /+ Description text (podcasts) +/
   gchar   *podcasturl;       /+ URL/Title (podcasts)        +/
   gchar   *podcastrss;       /+ Podcast RSS                 +/
   gchar   *subtitle;         /+ Subtitle (podcasts)         +/
   guint32 time_released;     /+ For podcasts: release date as
				 displayed next to the title in the
				 Podcast playlist  +/
   gint32  cd_nr;             /+ CD number             +/
   gint32  cds;               /+ number of CDs         +/
   gint32  track_nr;          /+ track number          +/
   gint32  tracks;            /+ number of tracks      +/
   gint32  year;              /+ year                  +/
   gint32  tracklen;          /+ Length of track in ms +/
   gint32  bitrate;           /+ bitrate in kbps       +/
   guint16 samplerate;        /+ e.g.: CD is 44100     +/
   guint32 peak_signal;	      /+ LAME Peak Signal * 0x800000         +/
   gboolean compilation;      /+ Track is part of a compilation CD   +/
   gboolean lyrics_flag;
   gint16 bpm;

   If prefs_get_int("readtags") returns FALSE you only should fill in
   tracklen, bitrate, samplerate, soundcheck and filetype

   If prefs_get_int("coverart_apic") returns TRUE you should try to
   read APIC coverart data from the tags and set it with
   gp_set_thumbnails_from_data().

   Please note that the iPod will only play as much of the track as
   specified in "tracklen".

   You don't have to fill in the value for charset if you use the
   default charset (i.e. you use charset_to_utf8() to convert to
   UTF8). Otherwise please specify the charset used.

   When an error occurs, the function returns NULL and logs an error
   message using gtkpod_warning().

   You need to add your handler to get_track_info_from_file() in
   file.c


   You also have to write a function to write TAGs back to the
   file. That function should be named

   gboolean xxx_write_file_info (gchar *filename, Track *track)

   and return TRUE on success or FALSE on error. In that case it
   should log an error message using gtkpod_warning().

   You need to add your handler to file_write_info() in file.c


   Finally, you may want to provide a function that can
   read and set the soundcheck field: 

   gboolean xxx_read_soundcheck (gchar *filename, Track *track)

   and return TRUE when the soundcheck value could be determined.

   You need to add your handler to read_soundcheck() in file.c.

   ------------------------------------------------------------ */



#ifdef HAVE_LIBMP4V2
/* Use mp4v2 from the mpeg4ip project to handle mp4 (m4a, m4p, m4b) files
   (http://mpeg4ip.sourceforge.net/) */
/* Copyright note: code for mp4_get_file_info() is based on
 * mp4info.cpp of the mpeg4ip project */

/* define metadata bug is present (see note at mp4_write_file_info()) */
#define MP4V2_HAS_METADATA_BUG TRUE

#include <sys/types.h>
#include <sys/param.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_LIBMP4V2_2
#include <mp4v2/mp4v2.h>
#else
#include <mp4.h>
#endif

#ifndef FREEFORM_ACCEPTS_EXTRA_ARG
/* Version 1.6 of libmp4v2 introduces an index argument for MP4GetMetadataFreeForm. For C++ sources it defaults
   to 0, but in C we have to specify it on our own. 
*/
#define MP4GetMetadataFreeForm(mp4File, name, pValue, pValueSize, owner)  MP4GetMetadataFreeForm(mp4File, name, pValue, pValueSize)
#endif

#ifndef COVERART_ACCEPTS_EXTRA_ARG
/* Version 1.6 of libmp4v2 introduces an index argument for MP4GetMetadataCoverart. For C++ sources it defaults
   to NULL, but in C we have to specify it on our own. 
*/
#define MP4GetMetadataCoverArt(hFile, coverArt, size, index) MP4GetMetadataCoverArt(hFile, coverArt, size)
#endif

static gboolean mp4_scan_soundcheck (MP4FileHandle mp4File, Track *track)
{
    gboolean success = FALSE;
    u_int8_t *ppValue;
    u_int32_t pValueSize;


    g_return_val_if_fail (mp4File != MP4_INVALID_FILE_HANDLE, FALSE);

    if (MP4GetMetadataFreeForm(mp4File, "iTunNORM",
			       &ppValue, &pValueSize, NULL))
    {
	gchar *str;
	guint sc1=0, sc2=0;
	str = g_malloc0((pValueSize+1)*sizeof(gchar));
	memcpy(str, ppValue, pValueSize*sizeof(gchar));
	/* This field consists of a number of hex numbers
	   represented in ASCII, e.g. " 00000FA7 00000B3F
	   000072CF 00006AB6 0001CF53 00016310 0000743A
	   00007C1F 00023DD5 000162E2". iTunes seems to
	   choose the larger one of the first two numbers
	   as the value for track->soundcheck */
	sscanf (str, "%x %x", &sc1, &sc2);
	g_free (str);
	if (sc1 > sc2)
	    track->soundcheck = sc1;
	else
	    track->soundcheck = sc2;
	success = TRUE;
    }

    if (MP4GetMetadataFreeForm(mp4File, "replaygain_track_gain",
			       &ppValue, &pValueSize, NULL))
    {
	gchar *str;
	gdouble rg;
	str = g_malloc0((pValueSize+1)*sizeof(gchar));
	memcpy(str, ppValue, pValueSize*sizeof(gchar));
	rg = g_strtod (str, NULL);
	track->soundcheck = replaygain_to_soundcheck (rg);
	g_free (str);

	success = TRUE;
    }

    return success;
}


gboolean mp4_read_soundcheck (gchar *mp4FileName, Track *track)
{
    gboolean success = FALSE;
    MP4FileHandle mp4File;

    g_return_val_if_fail (mp4FileName, FALSE);
    g_return_val_if_fail (track, FALSE);

    mp4File = MP4Read(mp4FileName, 0);

    if (mp4File != MP4_INVALID_FILE_HANDLE)
    {
	MP4TrackId trackId;
	const char *trackType;
	u_int32_t track_cur, tracks_num;
	gboolean audio_or_video_found = FALSE;

	tracks_num = MP4GetNumberOfTracks (mp4File, NULL,  0);

	for (track_cur=0; track_cur < tracks_num; ++track_cur)
	{
	    trackId = MP4FindTrackId(mp4File, track_cur, NULL, 0);
	    trackType = MP4GetTrackType(mp4File, trackId);

	    if (trackType &&
		((strcmp(trackType, MP4_AUDIO_TRACK_TYPE) == 0) ||
		 (strcmp(trackType, MP4_VIDEO_TRACK_TYPE) == 0) ||
		 (strcmp(trackType, MP4_OD_TRACK_TYPE) == 0)))
	    {
		audio_or_video_found = TRUE;
		success = mp4_scan_soundcheck (mp4File, track);
	    }
	    if (audio_or_video_found) break;
	}
	if (!audio_or_video_found)
	{
	    gchar *filename = charset_to_utf8 (mp4FileName);
	    gtkpod_warning (
		_("'%s' does not appear to be a mp4 audio or video file.\n"),
		filename);
	    g_free (filename);
	}
	MP4Close(mp4File);
    }
    else
    {
	gchar *filename = charset_to_utf8 (mp4FileName);
	gtkpod_warning (
	    _("Could not open '%s' for reading, or file is not an mp4 file.\n"),
	    filename);
	g_free (filename);
    }

    return success;
}



Track *mp4_get_file_info (gchar *mp4FileName)
{
    Track *track = NULL;
    MP4FileHandle mp4File;

    g_return_val_if_fail (mp4FileName, NULL);

    mp4File = MP4Read(mp4FileName, 0);

    if (mp4File != MP4_INVALID_FILE_HANDLE)
    {
	MP4TrackId trackId;
	const char *trackType;
	u_int32_t track_cur, tracks_num;
	gboolean audio_or_video_found = FALSE;
/*	gboolean artwork_found = FALSE; not used yet */

	tracks_num = MP4GetNumberOfTracks (mp4File, NULL,  0);

	for (track_cur=0; track_cur < tracks_num; ++track_cur)
	{
	    trackId = MP4FindTrackId(mp4File, track_cur, NULL, 0);
	    trackType = MP4GetTrackType(mp4File, trackId);
	    if (trackType && (strcmp(trackType, "text") == 0))
	    {
		u_int32_t m_max_frame_size;
		m_max_frame_size = MP4GetTrackMaxSampleSize(mp4File, trackId) + 4;
		MP4SampleId samples = MP4GetTrackNumberOfSamples(mp4File, trackId);
		MP4SampleId i;
		Itdb_Chapterdata *chapterdata = itdb_chapterdata_new();
		for (i=1; i<=samples; i++)
		{
		    u_int8_t *m_buffer;
		    m_buffer = (u_int8_t *) malloc(m_max_frame_size * sizeof(u_int8_t));
		    u_int32_t m_this_frame_size = m_max_frame_size;
		    u_int8_t *buffer;
		    buffer = m_buffer;
		    gchar *title;
		    if (!MP4ReadSample(mp4File, trackId, i, &buffer, &m_this_frame_size, NULL, NULL, NULL, NULL))
		    {
			/* chapter title couldn't be read; probably using
			 * an older version of libmp4v2.  We'll just make
			 * our own titles, since the ipod doesn't display
			 * them anyway
			 */
			free (m_buffer);
			m_buffer = (u_int8_t *) malloc(12 * sizeof(u_int8_t));
			sprintf(m_buffer, "Chapter %03i", i);
			m_buffer[11] = '\0';
			title = g_strdup(m_buffer);
		    }
		    else
		    {
			int titlelength = (buffer[0] << 8) + buffer[1];
			gchar *newtitle = (gchar *) malloc((titlelength+1) * sizeof(gchar));
			newtitle = g_strndup (&buffer[2], titlelength);
			newtitle[titlelength] = '\0';
			title = g_strdup (newtitle);
			free (newtitle);
		    }

		    MP4Timestamp sampletime = MP4GetSampleTime(mp4File, trackId, i);
		    u_int64_t convertedsampletime = MP4ConvertFromTrackTimestamp(mp4File,
			    trackId, sampletime, MP4_MILLISECONDS_TIME_SCALE);
		    itdb_chapterdata_add_chapter(chapterdata, convertedsampletime, title);
		}
		track->chapterdata = itdb_chapterdata_duplicate (chapterdata);

		itdb_chapterdata_free(chapterdata);
	    }
	    if (trackType &&
		(audio_or_video_found == FALSE) &&
		((strcmp(trackType, MP4_AUDIO_TRACK_TYPE) == 0) ||
		 (strcmp(trackType, MP4_VIDEO_TRACK_TYPE) == 0) ||
		 (strcmp(trackType, MP4_OD_TRACK_TYPE) == 0)))
	    {
		gchar *value;
		guint16 numvalue, numvalue2;
		MP4Duration trackDuration = MP4GetTrackDuration(mp4File, trackId);
		double msDuration = 
		    (double)MP4ConvertFromTrackDuration(mp4File, trackId,
							trackDuration,
							MP4_MSECS_TIME_SCALE);
		guint32 avgBitRate = MP4GetTrackBitRate(mp4File, trackId);
		guint32 samplerate = MP4GetTrackTimeScale(mp4File, trackId);
		
		track = gp_track_new ();
		
		track->tracklen = msDuration;
		track->bitrate = avgBitRate/1000;
		track->samplerate = samplerate;
		value = strrchr (mp4FileName, '.');
		if (value)
		{
		    if (g_strcasecmp (value, ".m4a") == 0)
			track->filetype = g_strdup ("AAC audio file");
		    if (g_strcasecmp (value, ".m4p") == 0)
			track->filetype = g_strdup ("Protected AAC audio file");
		    if (g_strcasecmp (value, ".m4b") == 0)
			track->filetype = g_strdup ("AAC audio book file");
		    if (g_strcasecmp (value, ".mp4") == 0)
			track->filetype = g_strdup ("MP4 video file");
		}
		if (prefs_get_int("readtags"))
		{
		    if (MP4GetMetadataName(mp4File, &value) && value != NULL)
		    {
			track->title = charset_to_utf8 (value);
			g_free(value);
		    }
		    if (MP4GetMetadataArtist(mp4File, &value) && value != NULL)
		    {
			track->artist = charset_to_utf8 (value);
			g_free(value);
		    }
#if MP4_ALBUMARTIST_EXISTS
		    if (!track->artist || !*track->artist)
		    {
			g_free (track->artist);
			track->artist = NULL;
			if (MP4GetMetadataAlbumArtist(mp4File, &value) && value != NULL)
			{
			    track->artist = charset_to_utf8 (value);
			}
		    }
		    else
		    {
			if (MP4GetMetadataAlbumArtist(mp4File, &value) && value != NULL)
			{
			    track->albumartist = charset_to_utf8 (value);
			}
		    }
#else
#warning "Album Artist field not supported with this version of libmp4v2. Album Artist support requires at least V1.6.0"
#endif
#if HAVE_LIBMP4V2_2
		    if (MP4GetMetadataComposer(mp4File, &value) && value != NULL)
#else
		    if (MP4GetMetadataWriter(mp4File, &value) && value != NULL)
#endif
		    {
			track->composer = charset_to_utf8 (value);
			g_free(value);
		    }
		    if (MP4GetMetadataComment(mp4File, &value) && value != NULL)
		    {
			track->comment = charset_to_utf8 (value);
			g_free(value);
		    }
#if HAVE_LIBMP4V2_2
		    if (MP4GetMetadataReleaseDate(mp4File, &value) && value != NULL)
#else
		    if (MP4GetMetadataYear(mp4File, &value) && value != NULL)
#endif
		    {
			track->year = atoi (value);
			g_free(value);
		    }
		    if (MP4GetMetadataAlbum(mp4File, &value) && value != NULL)
		    {
			track->album = charset_to_utf8 (value);
			g_free(value);
		    }
		    if (MP4GetMetadataTrack(mp4File, &numvalue, &numvalue2))
		    {
			track->track_nr = numvalue;
			track->tracks = numvalue2;
		    }
		    if (MP4GetMetadataDisk(mp4File, &numvalue, &numvalue2))
		    {
			track->cd_nr = numvalue;
			track->cds = numvalue2;
		    }
		    if (MP4GetMetadataGrouping(mp4File, &value) && value != NULL)
		    {
			track->grouping = charset_to_utf8 (value);
			g_free (value);
		    }
		    if (MP4GetMetadataGenre(mp4File, &value) && value != NULL)
		    {
			track->genre = charset_to_utf8 (value);
			g_free(value);
		    }
#if HAVE_LIBMP4V2_2
		    if (MP4GetMetadataBPM (mp4File, &numvalue))
#else
		    if (MP4GetMetadataTempo (mp4File, &numvalue))
#endif
		    {
			track->BPM = numvalue;
		    }
		    if (MP4HaveAtom (mp4File, "moov.udta.meta.ilst.\251lyr"))
		    {
			track->lyrics_flag = 0x01;
		    }
		}
		mp4_scan_soundcheck (mp4File, track);
		audio_or_video_found = TRUE;

		if (prefs_get_int("coverart_apic"))
		{
		    u_int8_t *image_data;
		    u_int32_t image_data_len;
		    if (MP4GetMetadataCoverArt (mp4File,
						&image_data, &image_data_len, 0))
		    {
			if (image_data)
			{
/*			    FILE *file = fopen ("/tmp/tttt", "w");
			    fwrite (image_data, 1, image_data_len, file);
			    fclose (file);*/
			    gp_track_set_thumbnails_from_data (track,
							       image_data,
							       image_data_len);
			    g_free (image_data);
			}
		    }
		}
	    }
	}
	if (!audio_or_video_found)
	{
	    gchar *filename = charset_to_utf8 (mp4FileName);
	    gtkpod_warning (
		_("'%s' does not appear to be a mp4 audio or video file.\n"),
		filename);
	    g_free (filename);
	}
	MP4Close(mp4File);
    }
    else
    {
	gchar *filename = charset_to_utf8 (mp4FileName);
	gtkpod_warning (
	    _("Could not open '%s' for reading, or file is not an mp4 file.\n"),
	    filename);
	g_free (filename);
    }

    return track;
}


gboolean mp4_write_file_info (gchar *mp4FileName, Track *track)
{
    gboolean result = TRUE;
    MP4FileHandle mp4File = MP4Modify(mp4FileName, 0, FALSE);

    if (mp4File != MP4_INVALID_FILE_HANDLE)
    {
	MP4TrackId trackId;
	const char *trackType;

	trackId = MP4FindTrackId(mp4File, 0, NULL, 0);
	trackType = MP4GetTrackType(mp4File, trackId);
	if (trackType && ((strcmp(trackType, MP4_AUDIO_TRACK_TYPE) == 0)||(strcmp(trackType, MP4_VIDEO_TRACK_TYPE) == 0)))
	{
	    gchar *value;

#if MP4V2_HAS_METADATA_BUG
	    /* It could have been so easy. But: due to a bug in mp4v2
	     * you have to delete all meta data before modifying
	     * it. Therefore we have to read it first to avoid data
	     * loss. (Bug present in mpeg4ip-1.0RC1.) */
/*	    gchar *m_name = NULL, *m_artist = NULL, *m_albumartist = NULL;
	    gchar *m_writer = NULL, *m_comment = NULL;
	    gchar *m_year = NULL;
	    gchar *m_album = NULL, *m_genre = NULL;*/
	    gchar *m_tool = NULL;
/*	    guint16 m_track, m_tracks, m_disk, m_disks; */
	    guint16 m_tempo;
	    guint8 *m_covert = NULL, m_cpl;
	    guint32 m_size;
/*	    gboolean has_track = MP4GetMetadataTrack (mp4File,
						      &m_track, &m_tracks);
	    gboolean has_disk = MP4GetMetadataDisk (mp4File,
	    &m_disk, &m_disks);*/
#if HAVE_LIBMP4V2_2
	    gboolean has_tempo = MP4GetMetadataBPM (mp4File,
						      &m_tempo);
#else
	    gboolean has_tempo = MP4GetMetadataTempo (mp4File,
						      &m_tempo);
#endif
	    gboolean has_compilation = MP4GetMetadataCompilation (mp4File,
								  &m_cpl);
/*	    MP4GetMetadataName (mp4File, &m_name);
	    MP4GetMetadataArtist (mp4File, &m_artist);
	    MP4GetMetadataAlbumArtist (mp4File, &m_albumartist);
#if HAVE_LIBMP4V2_2
	    MP4GetMetadataComposer (mp4File, &m_writer);
#else
	    MP4GetMetadataWriter (mp4File, &m_writer);
#endif
	    MP4GetMetadataComment (mp4File, &m_comment);
#if HAVE_LIBMP4V2_2
	    MP4GetMetadataReleaseDate (mp4File, &m_year);
#else
	    MP4GetMetadataYear (mp4File, &m_year);
#endif
	    MP4GetMetadataAlbum (mp4File, &m_album);
	    MP4GetMetadataGenre (mp4File, &m_genre);*/
	    MP4GetMetadataTool (mp4File, &m_tool);
	    MP4GetMetadataCoverArt (mp4File, &m_covert, &m_size, 0);
	    MP4MetadataDelete (mp4File);
#endif
	    value = charset_from_utf8 (track->title);
	    MP4SetMetadataName (mp4File, value);
	    g_free (value);

	    value = charset_from_utf8 (track->artist);
	    MP4SetMetadataArtist (mp4File, value);
	    g_free (value);

#if MP4_ALBUMARTIST_EXISTS
	    value = charset_from_utf8 (track->albumartist);
	    MP4SetMetadataAlbumArtist (mp4File, value);
	    g_free (value);
#endif
	    value = charset_from_utf8 (track->composer);
#if HAVE_LIBMP4V2_2
	    MP4SetMetadataComposer (mp4File, value);
#else
	    MP4SetMetadataWriter (mp4File, value);
#endif
	    g_free (value);

	    value = charset_from_utf8 (track->comment);
	    MP4SetMetadataComment (mp4File, value);
	    g_free (value);

	    value = g_strdup_printf ("%d", track->year);
#if HAVE_LIBMP4V2_2
	    MP4SetMetadataReleaseDate (mp4File, value);
#else
	    MP4SetMetadataYear (mp4File, value);
#endif
	    g_free (value);

	    value = charset_from_utf8 (track->album);
	    MP4SetMetadataAlbum (mp4File, value);
	    g_free (value);

	    MP4SetMetadataTrack (mp4File, track->track_nr, track->tracks);

	    MP4SetMetadataDisk (mp4File, track->cd_nr, track->cds);

#if HAVE_LIBMP4V2_2
	    MP4SetMetadataBPM (mp4File, track->BPM);
#else
	    MP4SetMetadataTempo (mp4File, track->BPM);
#endif

	    value = charset_from_utf8 (track->grouping);
	    MP4SetMetadataGrouping (mp4File, value);
	    g_free (value);

	    value = charset_from_utf8 (track->genre);
	    MP4SetMetadataGenre (mp4File, value);
	    g_free (value);

#if MP4V2_HAS_METADATA_BUG
#if HAVE_LIBMP4V2_2
	    if (has_tempo) MP4SetMetadataBPM (mp4File, m_tempo);
#else
	    if (has_tempo) MP4SetMetadataTempo (mp4File, m_tempo);
#endif
	    if (has_compilation) MP4SetMetadataCompilation (mp4File, m_cpl);
	    if (m_tool)     MP4SetMetadataTool (mp4File, m_tool);
	    if (m_covert)   MP4SetMetadataCoverArt (mp4File, m_covert, m_size);
/*	    g_free (m_name);
	    g_free (m_artist);
	    g_free (m_albumartist);
	    g_free (m_writer);
	    g_free (m_comment);
	    g_free (m_year);
	    g_free (m_album);
	    g_free (m_genre);*/
	    g_free (m_tool);
	    g_free (m_covert);
#endif
	}
	else
	{
	    gchar *filename = charset_to_utf8 (mp4FileName);
	    gtkpod_warning (_("'%s' does not appear to be a mp4 audio file.\n"),
			    filename);
	    g_free (filename);
	    result = FALSE;
	}
	MP4Close (mp4File);
    }
    else
    {
	gchar *filename = charset_to_utf8 (mp4FileName);
	gtkpod_warning (
	    _("Could not open '%s' for writing, or file is not an mp4 file.\n"),
	    filename);
	g_free (filename);
	result = FALSE;
    }

    return result;
}

#else
/* We don't support mp4 without the mp4v2 library */
Track *mp4_get_file_info (gchar *name)
{
    gtkpod_warning (_("Import of '%s' failed: m4a/m4p/m4b not supported without the mp4v2 library. You must compile the gtkpod source together with the mp4v2 library.\n"), name);
    return NULL;
}

gboolean mp4_write_file_info (gchar *filename, Track *track)
{
    gtkpod_warning (_("m4a/m4p/m4b metadata update for '%s' failed: m4a/m4p/m4b not supported without the mp4v2 library. You must compile the gtkpod source together with the mp4v2 library.\n"), filename);
    return FALSE;
}

gboolean mp4_read_soundcheck (gchar *filename, Track *track)
{
    gtkpod_warning (_("m4a/m4p/m4b soundcheck update for '%s' failed: m4a/m4p/m4b not supported without the mp4v2 library. You must compile the gtkpod source together with the mp4v2 library.\n"), filename);
    return FALSE;
}
#endif