/** * Read any lyrics from the given file */ char *AP_read_lyrics(const char *filePath, GError **error) { APar_ScanAtoms(filePath, true); openSomeFile(filePath, true); char *value = find_atom_value(LYRICS); APar_FreeMemory(); return value; }
/** * Using the given track, set the metadata of the target * file */ void AP_write_metadata(Track *track, const char *filePath, GError **error) { ExtraTrackData *etr; gchar *atom; gchar *value; g_return_if_fail(track); APar_ScanAtoms(filePath); if (metadata_style != ITUNES_STYLE) { gchar *fbuf = charset_to_utf8(filePath); gtkpod_log_error(error, g_strdup_printf(_("ERROR %s is not itunes style."), fbuf)); g_free(fbuf); return; } // Title set_limited_text_atom_value(TITLE, track->title); // Artist set_limited_text_atom_value(ARTIST, track->artist); // Album Artist set_limited_text_atom_value(ALBUM_ARTIST, track->albumartist); // Album set_limited_text_atom_value(ALBUM, track->album); // Genre APar_MetaData_atomGenre_Set(track->genre); // Track Number and Total atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, TRACK_NUM_AND_TOTAL, DATA); if (track->track_nr == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { gchar *track_info = g_strdup_printf("%d / %d", track->track_nr, track->tracks); short tracknumData_atom = APar_MetaData_atom_Init(atom, track_info, AtomFlags_Data_Binary); APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->track_nr, 16); APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->tracks, 16); APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); g_free(track_info); } g_free(atom); // Disk Number and Total atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, DISK_NUM_AND_TOTAL, DATA); if (track->cd_nr == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { gchar *disk_info = g_strdup_printf("%d / %d", track->cd_nr, track->cds); short disknumData_atom = APar_MetaData_atom_Init(atom, disk_info, AtomFlags_Data_Binary); APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->cd_nr, 16); APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->cds, 16); g_free(disk_info); } g_free(atom); // Comment set_limited_text_atom_value(COMMENT, track->comment); // Year gchar *yr = NULL; if (track->year > 0) yr = g_strdup_printf("%d", track->year); set_limited_text_atom_value(YEAR, yr); if (yr) g_free(yr); // Lyrics etr = (ExtraTrackData *) track->userdata; if (etr) write_lyrics_internal(etr->lyrics, filePath, error); // Composer set_limited_text_atom_value(COMPOSER, track->composer); // Grouping set_limited_text_atom_value(GROUPING, track->grouping); // Description set_limited_text_atom_value(DESCRIPTION, track->description); // TV Network set_limited_text_atom_value(TV_NETWORK_NAME, track->tvnetwork); // TV Show Name set_limited_text_atom_value(TV_SHOW, track->tvshow); // TV Episode set_limited_text_atom_value(TV_EPISODE, track->tvepisode); // Compilation atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, COMPILATION, DATA); if (!track->compilation) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { //compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init value = g_strdup_printf("%d", track->compilation); short compilationData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(compilationData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is compilation g_free(value); } g_free(atom); // Tempo / BPM atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, TEMPO, DATA); if (!track->BPM) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { //bpm is [0, 0, 0, 0, 0, bpm_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init value = g_strdup_printf("%d", track->BPM); short bpmData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(bpmData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->BPM, 16); g_free(value); } g_free(atom); // Media Type atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, MEDIA_TYPE, DATA); guint8 mediaTypeTag = mediaTypeToMediaTypeTag(track->mediatype); value = g_strdup_printf("%d", track->season_nr); short stikData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(stikData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, mediaTypeTag, 8); g_free(value); g_free(atom); // TV Season No. atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, TV_SEASON_NO, DATA); if (track->season_nr == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { value = g_strdup_printf("%d", track->season_nr); short tvseasonData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); APar_Unified_atom_Put(tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->season_nr, 16); g_free(value); } g_free(atom); // TV Episode No. atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, TV_EPISODE_NO, DATA); if (track->episode_nr == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { value = g_strdup_printf("%d", track->episode_nr); short tvepisodenumData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); APar_Unified_atom_Put(tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->episode_nr, 16); g_free(value); } g_free(atom); // Keywords set_limited_text_atom_value(KEYWORD, track->keywords); // Podcast Category set_limited_text_atom_value(CATEGORY, track->category); // Podcast URL atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, PODCAST_URL, DATA); if (!track->podcasturl || strlen(track->podcasturl) == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { short podcasturlData_atom = APar_MetaData_atom_Init(atom, track->podcasturl, AtomFlags_Data_Binary); APar_Unified_atom_Put(podcasturlData_atom, track->podcasturl, UTF8_iTunesStyle_Binary, 0, 0); } g_free(atom); // Gapless Playback atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, GAPLESS_FLAG, DATA); if (!track->gapless_track_flag) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { value = g_strdup_printf("%d", track->gapless_track_flag); short gaplessData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(gaplessData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is gapl g_free(value); } g_free(atom); // Sort Title set_limited_text_atom_value(SORT_TITLE, track->sort_title); // Sort Artist set_limited_text_atom_value(SORT_ARTIST, track->sort_artist); // Sort Album Artist set_limited_text_atom_value(SORT_ALBUM_ARTIST, track->sort_albumartist); // Sort Composer set_limited_text_atom_value(SORT_COMPOSER, track->sort_composer); // Sort Album set_limited_text_atom_value(SORT_ALBUM, track->sort_album); // Sort TV Show set_limited_text_atom_value(SORT_TV_SHOW, track->sort_tvshow); if (prefs_get_int("coverart_apic")) { GdkPixbuf *pixbuf = (GdkPixbuf*) itdb_artwork_get_pixbuf(track->itdb->device, track->artwork, -1, -1); if (!pixbuf) { // Destroy any existing artwork if any APar_MetaData_atomArtwork_Set("REMOVE_ALL", NULL); } else { gchar *tmp_file = g_build_filename(g_get_tmp_dir(), "ttt.jpg", NULL); GError *pixbuf_err = NULL; gdk_pixbuf_save(pixbuf, tmp_file, "jpeg", &pixbuf_err, "quality", "100", NULL); if (!pixbuf_err) { APar_MetaData_atomArtwork_Set(tmp_file, NULL); g_remove(tmp_file); } else { gtkpod_log_error(error, g_strdup_printf(_("ERROR failed to change track file's artwork.") )); g_error_free(pixbuf_err); return; } g_free(tmp_file); g_object_unref(pixbuf); } } // after all the modifications are enacted on the tree in memory // then write out the changes APar_DetermineAtomLengths(); openSomeFile(filePath, true); APar_WriteFile(filePath, NULL, true); APar_FreeMemory(); }
/** * Open and scan the metadata of the m4a/mp4/... file * and populate the given track. */ void AP_read_metadata(const char *filePath, Track *track) { FILE *mp4File; Trackage *trackage; uint8_t track_cur; uint8_t txttrack_cur; gboolean audio_or_video_found = FALSE; gboolean has_quicktime_chaps = FALSE; uint32_t timescale = 0; APar_ScanAtoms(filePath, true); mp4File = openSomeFile(filePath, true); trackage = APar_ExtractDetails(mp4File, SHOW_TRACK_INFO); for (track_cur = 0; track_cur < trackage->total_tracks; ++track_cur) { TrackInfo *info = trackage->infos[track_cur]; if ((info->type_of_track & AUDIO_TRACK) || (info->type_of_track & VIDEO_TRACK) || (info->type_of_track & DRM_PROTECTED_TRACK)) { /* * the info->duration is in the track's timescale units so must be divided by that * value to get seconds, while track->tracklen in gtkpod is in ms */ float duration = ((float) info->duration / (float) info->parent->movie_info->timescale) * 1000; track->tracklen = (gint32) duration; track->bitrate = APar_calculate_bitrate(info); track->samplerate = info->media_sample_rate; audio_or_video_found = TRUE; break; } } for (txttrack_cur = 0; audio_or_video_found && txttrack_cur < trackage->total_tracks; ++txttrack_cur) { TrackInfo *txtinfo = trackage->infos[txttrack_cur]; char buf[128]; // search for chapter track if (!(txtinfo->type_of_track & TEXT_TRACK)) continue; // see if the AV track's chap refers to this text track // chap: 0: atom size 4: 'chap' 8,12,...,8+(N-1)*4: (0: referenced track ID) snprintf(buf, sizeof(buf), "moov.trak[%u].tref.chap", track_cur + 1); AtomicInfo* chapAtom = APar_FindAtom(buf, false, SIMPLE_ATOM, 0); if (!chapAtom) continue; int entry_count = (chapAtom->AtomicLength - 8) / 4; for (int i = 0; i < entry_count; ++i) { if (APar_read32(buf, mp4File, chapAtom->AtomicStart + 8 + i * 4) == txtinfo->track_id) { has_quicktime_chaps = TRUE; timescale = txtinfo->media_sample_rate; break; } } if (has_quicktime_chaps) break; } if (has_quicktime_chaps) { // found a chapter... now get the chapter data from the text track char buf[128]; // stts: 0: atom size 4: 'stts' 8: version 12: entry count 16,24,...,16+(N-1)*8: (0: frame count 4: duration) snprintf(buf, sizeof(buf), "moov.trak[%u].mdia.minf.stbl.stts", txttrack_cur + 1); AtomicInfo* sampleAtom = APar_FindAtom(buf, false, VERSIONED_ATOM, 0); // stsz: 0: atom size 4: 'stsz' 8: version 12: size of all (or 0) 16: entry count 20,24,...,20+(N-1)*4: (0: sample size) snprintf(buf, sizeof(buf), "moov.trak[%u].mdia.minf.stbl.stsz", txttrack_cur + 1); AtomicInfo* sampleSizeAtom = APar_FindAtom(buf, false, VERSIONED_ATOM, 0); // stco: 0: atom size 4: 'stco' 8: version 12: entry count 16,20,...,16+(N-1)*4: (0: sample byte offset) snprintf(buf, sizeof(buf), "moov.trak[%u].mdia.minf.stbl.stco", txttrack_cur + 1); AtomicInfo* sampleOffsetAtom = APar_FindAtom(buf, false, VERSIONED_ATOM, 0); // We must have a valid sampleAtom to know chapter times. If sampleSizeAtom or sampleOffsetAtom is invalid, // we can do without them (and instead create a default chapter name). if (sampleAtom && sampleAtom->AtomicLength >= 16) { Itdb_Chapterdata *chapterdata = itdb_chapterdata_new(); uint32_t stts_entry_count = APar_read32(buf, mp4File, sampleAtom->AtomicStart + 12); uint32_t stsz_entry_count = !sampleSizeAtom || sampleSizeAtom->AtomicLength < 20 ? 0 : APar_read32(buf, mp4File, sampleSizeAtom->AtomicStart + 16); uint32_t stco_entry_count = !sampleOffsetAtom || sampleOffsetAtom->AtomicLength < 16 ? 0 : APar_read32(buf, mp4File, sampleOffsetAtom->AtomicStart + 12); uint32_t stsz_all_size = !sampleSizeAtom || sampleSizeAtom->AtomicLength < 16 ? 0 : APar_read32(buf, mp4File, sampleSizeAtom->AtomicStart + 12); uint32_t start_time = 0; u_int32_t max_frame_size = stsz_all_size; // if stsz_all_size specified, use only that size for (int i = 0; !stsz_all_size && i < stsz_entry_count; ++i) { uint32_t chap_name_len = APar_read32(buf, mp4File, sampleSizeAtom->AtomicStart + 20 + i * 4); if (chap_name_len > max_frame_size) max_frame_size = chap_name_len; } max_frame_size += 1; // for trailing '\0' (unneeded?), and to make sure that malloc() gets passed at least 1 char * namebuf = (char *)malloc(max_frame_size * sizeof(char)); for (int i = 0; i < stts_entry_count; ++i) { gchar *title = NULL; uint32_t chap_name_len = stsz_all_size; uint32_t chap_offset = 0; if (stsz_all_size == 0 && i < stsz_entry_count) chap_name_len = APar_read32(buf, mp4File, sampleSizeAtom->AtomicStart + 20 + i * 4); if (i < stco_entry_count) chap_offset = APar_read32(buf, mp4File, sampleOffsetAtom->AtomicStart + 16 + i * 4); if (chap_offset != 0) APar_readX(namebuf, mp4File, chap_offset, chap_name_len); else // If the location of the chapter name is unknown, trigger default chapter naming chap_name_len = 0; if (chap_name_len > 2) { int titlelength = (namebuf[0] << 8) + namebuf[1]; // if the stsz atom and the title value disagree, use the smaller one for safety titlelength = (titlelength > chap_name_len) ? chap_name_len : titlelength; // If a title begins with 0xFFFE, it's a UTF-16 title if (titlelength >= 2 && namebuf[2] == 0xff && namebuf[3] == 0xfe) title = g_utf16_to_utf8((const gunichar2 *) &namebuf[4], titlelength - 2, NULL, NULL, NULL); else title = g_strndup(&namebuf[2], titlelength); } else { // chapter title couldn't be found; create our own titles (and some ipods don't display them anyway). // Translators: this string is used to create a chapter title when no chapter title could be found title = g_strdup_printf(_("Chapter %3d"), i); } if (!timescale) // assume 1000, also, don't divide by 0 timescale = 1000; double duration_ms = (double)start_time * 1000.0 / (double)timescale; itdb_chapterdata_add_chapter(chapterdata, duration_ms, title); g_free(title); if (i < (stts_entry_count - 1)) // skip this stage after the last chapter has been added { uint32_t frame_count = APar_read32(buf, mp4File, sampleAtom->AtomicStart + 16 + i * 8); uint32_t duration = APar_read32(buf, mp4File, sampleAtom->AtomicStart + 20 + i * 8); start_time += frame_count * duration; } } if (namebuf) free(namebuf); if (track->chapterdata) // if there was already chapter data, don't leak it itdb_chapterdata_free(track->chapterdata); track->chapterdata = itdb_chapterdata_duplicate(chapterdata); itdb_chapterdata_free(chapterdata); } } // TODO: add support for Nero-style mp4 chapters if (prefs_get_int("readtags")) { char* value = NULL; // MP4 Title value = find_atom_value(TITLE); if (value) { track->title = g_strdup(value); free(value); } // MP4 Artist value = find_atom_value(ARTIST); if (value) { track->artist = g_strdup(value); free(value); } // MP4 Album Artist value = find_atom_value(ALBUM_ARTIST); if (value) { track->albumartist = g_strdup(value); free(value); } // MP4 Composer value = find_atom_value(COMPOSER); if (value) { track->composer = g_strdup(value); free(value); } // MP4 Comment value = find_atom_value(COMMENT); if (value) { track->comment = g_strdup(value); free(value); } // MP4 Description value = find_atom_value(DESCRIPTION); if (value) { track->description = g_strdup(value); free(value); } // MP4 Keywords value = find_atom_value(KEYWORD); if (value) { track->keywords = g_strdup(value); free(value); } // MP4 Year value = find_atom_value(YEAR); if (value) { track->year = atoi(value); free(value); } // MP4 Album value = find_atom_value(ALBUM); if (value) { track->album = g_strdup(value); free(value); } // MP4 Track No. and Total value = find_atom_value(TRACK_NUM_AND_TOTAL); if (value) { const char* delimiter = " of "; char *result = NULL; result = strtok(value, delimiter); if (result) track->track_nr = atoi(result); result = strtok(NULL, delimiter); if (result) track->tracks = atoi(result); free(value); } // MP4 Disk No. and Total value = find_atom_value(DISK_NUM_AND_TOTAL); if (value) { const char* delimiter = " of "; char *result = NULL; result = strtok(value, delimiter); if (result) track->cd_nr = atoi(result); result = strtok(NULL, delimiter); if (result) track->cds = atoi(result); free(value); } // MP4 Grouping value = find_atom_value(GROUPING); if (value) { track->grouping = g_strdup(value); free(value); } // MP4 Genre - note: can be either a standard or custom genre // standard genre value = find_atom_value(STANDARD_GENRE); if (value) { track->genre = charset_to_utf8(value); // Should not free standard genres } else { // custom genre value = find_atom_value(CUSTOM_GENRE); if (value) { track->genre = g_strdup(value); free(value); } } // MP4 Tempo / BPM value = find_atom_value(TEMPO); if (value) { track->BPM = atoi(value); free(value); } // MP4 Lyrics value = find_atom_value(LYRICS); if (value) { track->lyrics_flag = 0x01; free(value); } // MP4 TV Show value = find_atom_value(TV_SHOW); if (value) { track->tvshow = g_strdup(value); free(value); } // MP4 TV Episode value = find_atom_value(TV_EPISODE); if (value) { track->tvepisode = g_strdup(value); free(value); } // MP4 TV Episode No. value = find_atom_value(TV_EPISODE_NO); if (value) { track->episode_nr = atoi(value); free(value); } // MP4 TV Network value = find_atom_value(TV_NETWORK_NAME); if (value) { track->tvnetwork = g_strdup(value); free(value); } // MP4 TV Season No. value = find_atom_value(TV_SEASON_NO); if (value) { track->season_nr = atoi(value); free(value); } // MP4 Media Type value = find_atom_value(MEDIA_TYPE); if (value) { stiks * stik = MatchStikString(value); if (stik) { track->mediatype = mediaTypeTagToMediaType(stik->stik_number); } // Should not free standard media types } // MP4 Compilation flag value = find_atom_value(COMPILATION); if (value) { track->compilation = !g_strcmp0("true", value); free(value); } // MP4 Category value = find_atom_value(CATEGORY); if (value) { track->category = g_strdup(value); free(value); } // MP4 Podcast URL value = find_atom_value(PODCAST_URL); if (value) { track->podcasturl = g_strdup(value); free(value); } value = find_atom_value(GAPLESS_FLAG); if (value) { track->gapless_track_flag = atoi(value); free(value); } // MP4 Sort Title value = find_atom_value(SORT_TITLE); if (value) { track->sort_title = g_strdup(value); free(value); } // MP4 Sort Artist value = find_atom_value(SORT_ARTIST); if (value) { track->sort_artist = g_strdup(value); free(value); } // MP4 Sort Album Artist value = find_atom_value(SORT_ALBUM_ARTIST); if (value) { track->sort_albumartist = g_strdup(value); free(value); } // MP4 Sort Composer value = find_atom_value(SORT_COMPOSER); if (value) { track->sort_composer = g_strdup(value); free(value); } // MP4 Sort Album value = find_atom_value(SORT_ALBUM); if (value) { track->sort_album = g_strdup(value); free(value); } // MP4 Sort TV Show value = find_atom_value(SORT_TV_SHOW); if (value) { track->sort_tvshow = g_strdup(value); free(value); } if (prefs_get_int("coverart_apic")) { gchar *tmp_file_prefix = g_build_filename(g_get_tmp_dir(), "ttt", NULL); gchar *tmp_file; AtomicInfo *info = find_atom("covr"); if (info) { // Extract the data to a temporary file tmp_file = APar_ExtractAAC_Artwork(info->AtomicNumber, tmp_file_prefix, 1); g_free(tmp_file_prefix); if (tmp_file && g_file_test(tmp_file, G_FILE_TEST_EXISTS)) { // Set the thumbnail using the tmp file GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(tmp_file, NULL); if (pixbuf) { itdb_track_set_thumbnails_from_pixbuf(track, pixbuf); g_object_unref(pixbuf); } g_remove(tmp_file); } if (tmp_file) g_free(tmp_file); } } } APar_FreeMemory(); }
int main (int argc, char **argv) { int c; char *m4afile = NULL; M4A_TAG_CFG cfg; cfg.mode = M4A_MODE_INVALID; cfg.md5sum = M4A_FALSE; cfg.sha1sum = M4A_FALSE; cfg.art = M4A_FALSE; cfg.pixpath[0] = '\0'; while (1) { static struct option long_options[] = { {"literal", no_argument, 0, 'l'}, {"verbose", no_argument, 0, 'v'}, {"with-md5sum", no_argument, 0, 'm'}, {"with-sha1sum", no_argument, 0, 's'}, {"extract-art", no_argument, 0, 'e'}, {"extract-art-to", required_argument, 0, 'p'}, {"output", required_argument, 0, 'o'}, {"help", no_argument, 0, 'h'}, {"test", no_argument, 0, 't'}, {0, 0, 0, 0} }; /* getopt_long stores the option index. */ int option_index = 0; c = getopt_long (argc, argv, "p:o:lvhtmse", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 0: /* Is mode set ? */ if (long_options[option_index].flag != 0) break; printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case 'l': cfg.mode = M4A_MODE_LITERAL; break; case 'v': cfg.mode = M4A_MODE_VERBOSE; break; case 't': cfg.mode = M4A_MODE_TESTING; break; case 'm': cfg.md5sum = M4A_TRUE; break; case 's': cfg.sha1sum = M4A_TRUE; break; case 'e': cfg.art = M4A_TRUE; break; case 'p': cfg.art = M4A_TRUE; strcpy(cfg.pixpath, optarg); break; case 'o': printf ("option -o with value `%s'\n", optarg); printf ("Not Yet Supported.....\n"); return 20; case 'h': printf ("\n%s", USAGE); return 0; case '?': /* getopt_long already printed an error message. */ return 20; default: fprintf (stderr, "Invalid Option\n%s\n", USAGE); return 30; } } /* Grab File names*/ if (optind < argc) { /* For Later Use only one for now while (optind < argc) { printf ("%s ", argv[optind++]); } */ m4afile = argv[optind]; } else { fprintf(stderr, "No Files specified\n%s", USAGE); return 1; } TestFileExistence(m4afile, true); xmlInitEndianDetection(); ExtractPaddingPrefs(NULL); tree_display_only=true; APar_ScanAtoms(m4afile); if (cfg.mode == M4A_MODE_LITERAL) { unsigned char *md5sum = NULL; unsigned char *sha1sum = NULL; int cnt = 0; M4A_ART *art = NULL; M4A_ART bfr[M4A_MAX_ART]; char *bfname = NULL; char path[512]; openSomeFile(m4afile, true); get_chksum(&cfg, m4afile, &md5sum, &sha1sum); if (cfg.art == M4A_TRUE) { int cvr; int idx; int ret; cvr = m4a_get_atomidx((const char *) "covr", 1, 0); idx = parsedAtoms[cvr].NextAtomNumber; while (parsedAtoms[idx].AtomicLevel > parsedAtoms[cvr].AtomicLevel) { ret = m4a_extract_art(idx, &bfr[cnt]); if (ret != 0) break; cnt++; idx = parsedAtoms[idx].NextAtomNumber; } if (cnt != 0) { char tmp[512]; strcpy(tmp, m4afile); if (cfg.pixpath[0] != '\0') { char *bname; strcpy(path, cfg.pixpath); strcat(path, "/"); bname = basename(tmp); strcat(path, bname); // printf ("Fname: %s\n", path); bfname = path; } art = bfr; } } redirect_io(&cfg); if (metadata_style >= THIRD_GEN_PARTNER) { APar_PrintUserDataAssests(); } else if (metadata_style == ITUNES_STYLE) { APar_PrintDataAtoms(m4afile, NULL, 0, PRINT_DATA); } reset_io(&cfg); m4a_display_json_tags( cfg.str, stdout, md5sum, sha1sum, art, cnt, bfname); openSomeFile(m4afile, false); } else if (cfg.mode == M4A_MODE_VERBOSE) { redirect_io(&cfg); APar_PrintAtomicTree(); reset_io(&cfg); m4a_display_json_tree(cfg.str, stdout); } else if (cfg.mode == M4A_MODE_TESTING) { int mda; unsigned char bfr[2][64]; int cvr; mda = APar_DetermineMediaData_AtomPosition(); printf ("Location of mdat: %d\n", mda); //APar_SimpleAtomPrintout(); m4a_stream_chksum(m4afile, bfr[0], bfr[1]); cvr = m4a_get_atomidx((const char *) "covr", 1, 0); printf("\n"); } else { unsigned char *md5sum = NULL, *sha1sum = NULL; if (get_chksum(&cfg, m4afile, &md5sum, &sha1sum) == 0) { if ((md5sum != NULL) || (sha1sum != NULL)) { char pfx[2]; pfx[0] = '\0'; printf("{\n \"stream\": {"); if (md5sum != NULL) { printf(" \"md5sum\": \"%s\"", md5sum); pfx[0] = ','; pfx[1] = '\0'; } if (sha1sum != NULL) { printf("%s \"sha1sum\": \"%s\"", pfx, sha1sum); } printf(" }\n}\n"); } } else if (cfg.art == M4A_TRUE) { M4A_ART art[M4A_MAX_ART]; int cvr; int idx; int ret; int cnt = 0; char path[512]; FILE *fp; FILE *out = stdout; if (cfg.pixpath[0] != '\0') { char tmp[256]; char *bname = NULL; strcpy(tmp, m4afile); strcpy(path, cfg.pixpath); strcat(path, "/"); bname = basename(tmp); strcat(path, bname); } fputs ("{\n \"@img\": [ ", out); openSomeFile(m4afile, true); cvr = m4a_get_atomidx((const char *) "covr", 1, 0); idx = parsedAtoms[cvr].NextAtomNumber; while (parsedAtoms[idx].AtomicLevel > parsedAtoms[cvr].AtomicLevel) { int err = M4A_FALSE; const char *extn = NULL; ret = m4a_extract_art(idx, &art[cnt]); if (ret != 0) break; if ( art[cnt].type == M4A_PNG) { extn = "png"; } else if ( art[cnt].type == M4A_JPG) { extn = "jpg"; } if (cfg.pixpath[0] != '\0') { char fname[512]; sprintf(fname, "%s.%d.%s", path, cnt+1, extn); if ((fp = fopen(fname, "wb")) != NULL) { if (fwrite(art[cnt].data, 1, art[cnt].size, fp) != art[cnt].size) { perror("img write:"); err = M4A_TRUE; } fclose(fp); } else { perror("img create:"); err = M4A_TRUE; } if (cnt != 0) fputs(", ", out); if (err == M4A_TRUE) fputs("null", out); else fprintf(out, "\"%s\"", fname); } else { base64_encodestate inst; char bfr[M4A_B64_BFR_SZ*2]; int clen; int blks; int j; base64_init_encodestate(&inst); blks = art[cnt].size/1024; if (cnt != 0) fputs(", ", out); fprintf (out, "{\"type\": \"%s\", \"data\": \"", extn); for (j = 0; j < blks; j++) { clen = base64_encode_block( (const char*) &art[cnt].data[j * M4A_B64_BFR_SZ], M4A_B64_BFR_SZ, bfr, &inst); //fwrite((void *)bfr, clen, 1, out); m4a_print_without_newlines(out, bfr, clen); } clen = base64_encode_block( (const char*) &art[cnt].data[j * M4A_B64_BFR_SZ], art[cnt].size % M4A_B64_BFR_SZ, bfr, &inst); m4a_print_without_newlines(out, bfr, clen); clen = base64_encode_blockend(bfr, &inst); m4a_print_without_newlines(out, bfr, clen); if (cnt != 0) fputs(", ", out); fputs ("\"}", out); } cnt++; idx = parsedAtoms[idx].NextAtomNumber; } openSomeFile(m4afile, false); fputs (" ]\n}\n", out); } } exit (0); }