int aac_read_metadata (DB_playItem_t *it) { deadbeef->pl_lock (); DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); deadbeef->pl_unlock (); if (!fp) { return -1; } if (fp->vfs->is_streaming ()) { deadbeef->fclose (fp); return -1; } aac_info_t inf; memset (&inf, 0, sizeof (inf)); inf.file = fp; inf.junk = deadbeef->junk_get_leading_size (fp); if (inf.junk >= 0) { deadbeef->fseek (inf.file, inf.junk, SEEK_SET); } else { inf.junk = 0; } MP4FILE_CB cb = { .read = aac_fs_read, .write = NULL, .seek = aac_fs_seek, .truncate = NULL, .user_data = &inf }; deadbeef->pl_delete_all_meta (it); mp4ff_t *mp4 = mp4ff_open_read (&cb); if (mp4) { aac_load_tags (it, mp4); mp4ff_close (mp4); } /*int apeerr = */deadbeef->junk_apev2_read (it, fp); /*int v2err = */deadbeef->junk_id3v2_read (it, fp); /*int v1err = */deadbeef->junk_id3v1_read (it, fp); deadbeef->fclose (fp); return 0; }
int aac_read_metadata (DB_playItem_t *it) { #ifdef USE_MP4FF DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { return -1; } if (fp->vfs->is_streaming ()) { deadbeef->fclose (fp); return -1; } MP4FILE_CB cb = { .read = aac_fs_read, .write = NULL, .seek = aac_fs_seek, .truncate = NULL, .user_data = fp }; deadbeef->pl_delete_all_meta (it); mp4ff_t *mp4 = mp4ff_open_read (&cb); if (mp4) { aac_load_tags (it, mp4); mp4ff_close (mp4); deadbeef->pl_add_meta (it, "title", NULL); } else { /*int apeerr = */deadbeef->junk_apev2_read (it, fp); /*int v2err = */deadbeef->junk_id3v2_read (it, fp); /*int v1err = */deadbeef->junk_id3v1_read (it, fp); deadbeef->pl_add_meta (it, "title", NULL); } deadbeef->fclose (fp); #endif return 0; }
static DB_playItem_t * aac_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) { trace ("adding %s\n", fname); DB_FILE *fp = deadbeef->fopen (fname); if (!fp) { trace ("not found\n"); return NULL; } aac_info_t info = {0}; info.junk = deadbeef->junk_get_leading_size (fp); if (info.junk >= 0) { trace ("junk: %d\n", info.junk); deadbeef->fseek (fp, info.junk, SEEK_SET); } else { info.junk = 0; } const char *ftype = NULL; float duration = -1; int totalsamples = 0; int samplerate = 0; int channels = 0; int mp4track = -1; MP4FILE mp4 = NULL; if (fp->vfs->is_streaming ()) { trace ("streaming aac (%s)\n", fname); ftype = "RAW AAC"; } else { // slowwww! info.file = fp; MP4FILE_CB cb = { #ifdef USE_MP4FF .read = aac_fs_read, .write = NULL, .seek = aac_fs_seek, .truncate = NULL, .user_data = &info #else .open = aac_fs_open, .seek = aac_fs_seek, .read = aac_fs_read, .write = NULL, .close = aac_fs_close #endif }; int res = aac_probe (fp, fname, &cb, &duration, &samplerate, &channels, &totalsamples, &mp4track, &mp4); if (res == -1) { deadbeef->fclose (fp); return NULL; } else if (res == 0) { ftype = "MP4 AAC"; } else if (res == 1) { ftype = "RAW AAC"; } } DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->pl_add_meta (it, ":FILETYPE", ftype); deadbeef->plt_set_item_duration (plt, it, duration); trace ("duration: %f sec\n", duration); // read tags if (mp4) { #ifdef USE_MP4FF aac_load_tags (it, mp4); mp4ff_close (mp4); #else const MP4Tags *tags = MP4TagsAlloc (); MP4TagsFetch (tags, mp4); deadbeef->pl_add_meta (it, "title", tags->name); deadbeef->pl_add_meta (it, "artist", tags->artist); deadbeef->pl_add_meta (it, "albumArtist", tags->albumArtist); deadbeef->pl_add_meta (it, "album", tags->album); deadbeef->pl_add_meta (it, "composer", tags->composer); deadbeef->pl_add_meta (it, "comments", tags->comments); deadbeef->pl_add_meta (it, "genre", tags->genre); deadbeef->pl_add_meta (it, "year", tags->releaseDate); char s[10]; if (tags->track) { snprintf (s, sizeof (s), "%d", tags->track->index); deadbeef->pl_add_meta (it, "track", s); snprintf (s, sizeof (s), "%d", tags->track->total); deadbeef->pl_add_meta (it, "numtracks", s); } if (tags->disk) { snprintf (s, sizeof (s), "%d", tags->disk->index); deadbeef->pl_add_meta (it, "disc", s); snprintf (s, sizeof (s), "%d", tags->disk->total); deadbeef->pl_add_meta (it, "numdiscs", s); } deadbeef->pl_add_meta (it, "copyright", tags->copyright); deadbeef->pl_add_meta (it, "vendor", tags->encodedBy); deadbeef->pl_add_meta (it, "title", NULL); MP4TagsFree (tags); MP4Close (mp4); #endif } int apeerr = deadbeef->junk_apev2_read (it, fp); int v2err = deadbeef->junk_id3v2_read (it, fp); int v1err = deadbeef->junk_id3v1_read (it, fp); deadbeef->pl_add_meta (it, "title", NULL); int64_t fsize = deadbeef->fgetlength (fp); deadbeef->fclose (fp); if (duration > 0) { char s[100]; snprintf (s, sizeof (s), "%lld", fsize); deadbeef->pl_add_meta (it, ":FILE_SIZE", s); deadbeef->pl_add_meta (it, ":BPS", "16"); snprintf (s, sizeof (s), "%d", channels); deadbeef->pl_add_meta (it, ":CHANNELS", s); snprintf (s, sizeof (s), "%d", samplerate); deadbeef->pl_add_meta (it, ":SAMPLERATE", s); int br = (int)roundf(fsize / duration * 8 / 1000); snprintf (s, sizeof (s), "%d", br); deadbeef->pl_add_meta (it, ":BITRATE", s); // embedded cue deadbeef->pl_lock (); const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); DB_playItem_t *cue = NULL; if (cuesheet) { cue = deadbeef->plt_insert_cue_from_buffer (plt, after, it, cuesheet, strlen (cuesheet), totalsamples, samplerate); if (cue) { deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue); deadbeef->pl_unlock (); return cue; } } deadbeef->pl_unlock (); cue = deadbeef->plt_insert_cue (plt, after, it, totalsamples, samplerate); if (cue) { deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue); return cue; } } deadbeef->pl_add_meta (it, "title", NULL); after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); return after; } static const char * exts[] = { "aac", "mp4", "m4a", NULL }; // define plugin interface static DB_decoder_t plugin = { .plugin.api_vmajor = 1, .plugin.api_vminor = 0, .plugin.version_major = 1, .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "aac", .plugin.name = "AAC player", .plugin.descr = "plays aac files, supports raw aac files, as well as mp4 container", .plugin.copyright = "Copyright (C) 2009-2012 Alexey Yakovenko <*****@*****.**>\n" "\n" "Uses modified libmp4ff (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com\n" "\n" "This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU General Public License\n" "as published by the Free Software Foundation; either version 2\n" "of the License, or (at your option) any later version.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" , .plugin.website = "http://deadbeef.sf.net", .open = aac_open, .init = aac_init, .free = aac_free, .read = aac_read, .seek = aac_seek, .seek_sample = aac_seek_sample, .insert = aac_insert, .read_metadata = aac_read_metadata, #ifdef USE_MP4FF // mp4ff metadata writer doesn't work // .write_metadata = aac_write_metadata, #else #endif .exts = exts, }; DB_plugin_t * aac_load (DB_functions_t *api) { deadbeef = api; return DB_PLUGIN (&plugin); }
static DB_playItem_t * aac_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) { trace ("adding %s\n", fname); DB_FILE *fp = deadbeef->fopen (fname); if (!fp) { trace ("not found\n"); return NULL; } aac_info_t info = {0}; info.junk = deadbeef->junk_get_leading_size (fp); if (info.junk >= 0) { trace ("junk: %d\n", info.junk); deadbeef->fseek (fp, info.junk, SEEK_SET); } else { info.junk = 0; } const char *ftype = NULL; float duration = -1; int totalsamples = 0; int samplerate = 0; int channels = 0; int mp4track = -1; MP4FILE mp4 = NULL; if (fp->vfs->is_streaming ()) { trace ("streaming aac (%s)\n", fname); ftype = "RAW AAC"; } else { // slowwww! info.file = fp; MP4FILE_CB cb = { .read = aac_fs_read, .write = NULL, .seek = aac_fs_seek, .truncate = NULL, .user_data = &info }; mp4ff_t *mp4 = mp4ff_open_read (&cb); if (mp4) { int ntracks = mp4ff_total_tracks (mp4); trace ("aac: numtracks=%d\n", ntracks); int i; for (i = 0; i < ntracks; i++) { if (mp4ff_get_track_type (mp4, i) != TRACK_AUDIO) { trace ("aac: track %d is not audio\n", i); continue; } int mp4framesize; int res = mp4_track_get_info (mp4, i, &duration, &samplerate, &channels, &totalsamples, &mp4framesize); if (res >= 0 && duration > 0) { trace ("aac: found audio track %d (duration=%f, totalsamples=%d)\n", i, duration, totalsamples); int num_chapters; aac_chapter_t *chapters = NULL; if (mp4ff_chap_get_num_tracks(mp4) > 0) { chapters = aac_load_itunes_chapters (mp4, &num_chapters, samplerate); } DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); ftype = "MP4 AAC"; deadbeef->pl_add_meta (it, ":FILETYPE", ftype); deadbeef->pl_set_meta_int (it, ":TRACKNUM", i); deadbeef->plt_set_item_duration (plt, it, duration); aac_load_tags (it, mp4); int apeerr = deadbeef->junk_apev2_read (it, fp); int v2err = deadbeef->junk_id3v2_read (it, fp); int v1err = deadbeef->junk_id3v1_read (it, fp); int64_t fsize = deadbeef->fgetlength (fp); char s[100]; snprintf (s, sizeof (s), "%lld", fsize); deadbeef->pl_add_meta (it, ":FILE_SIZE", s); deadbeef->pl_add_meta (it, ":BPS", "16"); snprintf (s, sizeof (s), "%d", channels); deadbeef->pl_add_meta (it, ":CHANNELS", s); snprintf (s, sizeof (s), "%d", samplerate); deadbeef->pl_add_meta (it, ":SAMPLERATE", s); int br = (int)roundf(fsize / duration * 8 / 1000); snprintf (s, sizeof (s), "%d", br); deadbeef->pl_add_meta (it, ":BITRATE", s); // embedded chapters deadbeef->pl_lock (); // FIXME: is it needed? if (chapters && num_chapters > 0) { DB_playItem_t *cue = aac_insert_with_chapters (plt, after, it, chapters, num_chapters, totalsamples, samplerate); for (int n = 0; n < num_chapters; n++) { if (chapters[n].title) { free (chapters[n].title); } } free (chapters); if (cue) { deadbeef->fclose (fp); mp4ff_close (mp4); deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue); deadbeef->pl_unlock (); return cue; } } // embedded cue const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); DB_playItem_t *cue = NULL; if (cuesheet) { cue = deadbeef->plt_insert_cue_from_buffer (plt, after, it, cuesheet, strlen (cuesheet), totalsamples, samplerate); if (cue) { deadbeef->fclose (fp); mp4ff_close (mp4); deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue); deadbeef->pl_unlock (); return cue; } } deadbeef->pl_unlock (); cue = deadbeef->plt_insert_cue (plt, after, it, totalsamples, samplerate); if (cue) { deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue); return cue; } after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); break; } } mp4ff_close (mp4); if (i < ntracks) { deadbeef->fclose (fp); return after; } if (ntracks > 0) { // mp4 container found, but no valid aac tracks in it deadbeef->fclose (fp); return NULL; } } } trace ("aac: mp4 container failed, trying raw aac\n"); int res = aac_probe (fp, &duration, &samplerate, &channels, &totalsamples); if (res == -1) { deadbeef->fclose (fp); return NULL; } ftype = "RAW AAC"; DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->pl_add_meta (it, ":FILETYPE", ftype); deadbeef->plt_set_item_duration (plt, it, duration); trace ("duration: %f sec\n", duration); // read tags int apeerr = deadbeef->junk_apev2_read (it, fp); int v2err = deadbeef->junk_id3v2_read (it, fp); int v1err = deadbeef->junk_id3v1_read (it, fp); int64_t fsize = deadbeef->fgetlength (fp); deadbeef->fclose (fp); if (duration > 0) { char s[100]; snprintf (s, sizeof (s), "%lld", fsize); deadbeef->pl_add_meta (it, ":FILE_SIZE", s); deadbeef->pl_add_meta (it, ":BPS", "16"); snprintf (s, sizeof (s), "%d", channels); deadbeef->pl_add_meta (it, ":CHANNELS", s); snprintf (s, sizeof (s), "%d", samplerate); deadbeef->pl_add_meta (it, ":SAMPLERATE", s); int br = (int)roundf(fsize / duration * 8 / 1000); snprintf (s, sizeof (s), "%d", br); deadbeef->pl_add_meta (it, ":BITRATE", s); // embedded cue deadbeef->pl_lock (); const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); DB_playItem_t *cue = NULL; if (cuesheet) { cue = deadbeef->plt_insert_cue_from_buffer (plt, after, it, cuesheet, strlen (cuesheet), totalsamples, samplerate); if (cue) { deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue); deadbeef->pl_unlock (); return cue; } } deadbeef->pl_unlock (); cue = deadbeef->plt_insert_cue (plt, after, it, totalsamples, samplerate); if (cue) { deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue); return cue; } } after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); return after; }