static int caf_read_frames(pcm_reader_t *preader, void *buffer, unsigned nframes) { int rc; unsigned i, j, nbytes; caf_reader_t *reader = (caf_reader_t *)preader; unsigned bpf = reader->sample_format.bytes_per_frame; unsigned nchannels = reader->sample_format.channels_per_frame; unsigned bpc = bpf / nchannels; uint8_t tmp[64]; /* enough room for maximum bpf: 8ch float64 */ uint8_t *bp; uint8_t *chanmap = reader->chanmap; if (nframes > reader->length - reader->position) nframes = reader->length - reader->position; nbytes = nframes * bpf; if (nbytes) { if ((rc = pcm_read(&reader->io, buffer, nbytes)) < 0) return -1; nframes = rc / bpf; for (bp = buffer, i = 0; i < nframes; ++i, bp += bpf) { memcpy(tmp, bp, bpf); for (j = 0; j < nchannels; ++j) memcpy(bp + bpc * j, tmp + bpc * chanmap[j], bpc); } reader->position += nframes; } if (nframes == 0) { /* fetch info after data chunk */ uint32_t fcc; int64_t chunk_size; while ((fcc = caf_next_chunk(reader, &chunk_size)) != 0) { if (fcc == M4AF_FOURCC('i','n','f','o')) TRY_IO(caf_info(reader, chunk_size)); else TRY_IO(pcm_skip(&reader->io, chunk_size)); } } return nframes; FAIL: return 0; }
static int caf_desc(caf_reader_t *reader, int64_t chunk_size) { double mSampleRate; uint32_t mFormatID, mFormatFlags, mBytesPerPacket, mFramesPerPacket, mChannelsPerFrame, mBitsPerChannel; pcm_sample_description_t *desc = &reader->sample_format; ENSURE(chunk_size >= 32); TRY_IO(pcm_scanb(&reader->io, "QLLLLLL", &mSampleRate, &mFormatID, &mFormatFlags, &mBytesPerPacket, &mFramesPerPacket, &mChannelsPerFrame, &mBitsPerChannel) != 7); ENSURE(mFormatID == M4AF_FOURCC('l','p','c','m')); ENSURE(mSampleRate && mBytesPerPacket && mChannelsPerFrame >= 1 && mChannelsPerFrame <= 8 && mBitsPerChannel && mFramesPerPacket == 1 && mBytesPerPacket % mChannelsPerFrame == 0 && mBytesPerPacket >= mChannelsPerFrame * ((mBitsPerChannel + 7) / 8)); desc->sample_rate = mSampleRate; desc->bits_per_channel = mBitsPerChannel; desc->bytes_per_frame = mBytesPerPacket; desc->channels_per_frame = mChannelsPerFrame; switch (mFormatFlags) { case 0: desc->sample_type = PCM_TYPE_SINT_BE; break; case 1: desc->sample_type = PCM_TYPE_FLOAT_BE; break; case 2: desc->sample_type = PCM_TYPE_SINT; break; case 3: desc->sample_type = PCM_TYPE_FLOAT; break; default: goto FAIL; } TRY_IO(pcm_skip(&reader->io, chunk_size - 32)); return 0; FAIL: return -1; }
static int caf_parse(caf_reader_t *reader, int64_t *data_length) { uint32_t fcc; int64_t chunk_size; *data_length = 0; /* CAFFileHeader */ TRY_IO(pcm_read32be(&reader->io, &fcc)); ENSURE(fcc == M4AF_FOURCC('c','a','f','f')); TRY_IO(pcm_skip(&reader->io, 4)); /* mFileVersion, mFileFlags */ while ((fcc = caf_next_chunk(reader, &chunk_size)) != 0) { if (fcc == M4AF_FOURCC('d','e','s','c')) TRY_IO(caf_desc(reader, chunk_size)); else if (fcc == M4AF_FOURCC('i','n','f','o')) TRY_IO(caf_info(reader, chunk_size)); else if (fcc == M4AF_FOURCC('c','h','a','n')) { ENSURE(reader->sample_format.channels_per_frame); if (apple_chan_chunk(&reader->io, chunk_size, &reader->sample_format, reader->chanmap) < 0) goto FAIL; } else if (fcc == M4AF_FOURCC('d','a','t','a')) { TRY_IO(pcm_skip(&reader->io, 4)); /* mEditCount */ *data_length = (chunk_size == ~0ULL) ? chunk_size : chunk_size - 4; reader->data_offset = pcm_tell(&reader->io); break; } else TRY_IO(pcm_skip(&reader->io, chunk_size)); } ENSURE(reader->sample_format.channels_per_frame); ENSURE(fcc == M4AF_FOURCC('d','a','t','a')); return 0; FAIL: return -1; }
static int parse_options(int argc, char **argv, aacenc_param_ex_t *params) { int ch; int n; #define OPT_INCLUDE_SBR_DELAY M4AF_FOURCC('s','d','l','y') #define OPT_MOOV_BEFORE_MDAT M4AF_FOURCC('m','o','o','v') #define OPT_RAW_CHANNELS M4AF_FOURCC('r','c','h','n') #define OPT_RAW_RATE M4AF_FOURCC('r','r','a','t') #define OPT_RAW_FORMAT M4AF_FOURCC('r','f','m','t') #define OPT_SHORT_TAG M4AF_FOURCC('s','t','a','g') #define OPT_SHORT_TAG_FILE M4AF_FOURCC('s','t','g','f') #define OPT_LONG_TAG M4AF_FOURCC('l','t','a','g') #define OPT_TAG_FROM_JSON M4AF_FOURCC('t','f','j','s') static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "profile", required_argument, 0, 'p' }, { "bitrate", required_argument, 0, 'b' }, { "bitrate-mode", required_argument, 0, 'm' }, { "bandwidth", required_argument, 0, 'w' }, { "afterburner", required_argument, 0, 'a' }, { "lowdelay-sbr", required_argument, 0, 'L' }, { "sbr-ratio", required_argument, 0, 's' }, { "transport-format", required_argument, 0, 'f' }, { "adts-crc-check", no_argument, 0, 'C' }, { "header-period", required_argument, 0, 'P' }, { "gapless-mode", required_argument, 0, 'G' }, { "include-sbr-delay", no_argument, 0, OPT_INCLUDE_SBR_DELAY }, { "ignorelength", no_argument, 0, 'I' }, { "silent", no_argument, 0, 'S' }, { "moov-before-mdat", no_argument, 0, OPT_MOOV_BEFORE_MDAT }, { "raw", no_argument, 0, 'R' }, { "raw-channels", required_argument, 0, OPT_RAW_CHANNELS }, { "raw-rate", required_argument, 0, OPT_RAW_RATE }, { "raw-format", required_argument, 0, OPT_RAW_FORMAT }, { "title", required_argument, 0, M4AF_TAG_TITLE }, { "artist", required_argument, 0, M4AF_TAG_ARTIST }, { "album", required_argument, 0, M4AF_TAG_ALBUM }, { "genre", required_argument, 0, M4AF_TAG_GENRE }, { "date", required_argument, 0, M4AF_TAG_DATE }, { "composer", required_argument, 0, M4AF_TAG_COMPOSER }, { "grouping", required_argument, 0, M4AF_TAG_GROUPING }, { "comment", required_argument, 0, M4AF_TAG_COMMENT }, { "album-artist", required_argument, 0, M4AF_TAG_ALBUM_ARTIST }, { "track", required_argument, 0, M4AF_TAG_TRACK }, { "disk", required_argument, 0, M4AF_TAG_DISK }, { "tempo", required_argument, 0, M4AF_TAG_TEMPO }, { "tag", required_argument, 0, OPT_SHORT_TAG }, { "tag-from-file", required_argument, 0, OPT_SHORT_TAG_FILE }, { "long-tag", required_argument, 0, OPT_LONG_TAG }, { "tag-from-json", required_argument, 0, OPT_TAG_FROM_JSON }, { 0, 0, 0, 0 }, }; params->afterburner = 1; aacenc_getmainargs(&argc, &argv); while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:CP:G:Io:SR", long_options, 0)) != EOF) { switch (ch) { case 'h': return usage(), -1; case 'p': if (sscanf(optarg, "%u", &n) != 1) { fprintf(stderr, "invalid arg for profile\n"); return -1; } params->profile = n; break; case 'b': if (sscanf(optarg, "%u", &n) != 1) { fprintf(stderr, "invalid arg for bitrate\n"); return -1; } params->bitrate = n; break; case 'm': if (sscanf(optarg, "%u", &n) != 1 || n > 5) { fprintf(stderr, "invalid arg for bitrate-mode\n"); return -1; } params->bitrate_mode = n; break; case 'w': if (sscanf(optarg, "%u", &n) != 1) { fprintf(stderr, "invalid arg for bandwidth\n"); return -1; } params->bandwidth = n; break; case 'a': if (sscanf(optarg, "%u", &n) != 1 || n > 1) { fprintf(stderr, "invalid arg for afterburner\n"); return -1; } params->afterburner = n; break; case 'L': if (sscanf(optarg, "%d", &n) != 1 || n < -1 || n > 1) { fprintf(stderr, "invalid arg for lowdelay-sbr\n"); return -1; } params->lowdelay_sbr = n; break; case 's': if (sscanf(optarg, "%u", &n) != 1 || n > 2) { fprintf(stderr, "invalid arg for sbr-ratio\n"); return -1; } params->sbr_ratio = n; break; case 'f': if (sscanf(optarg, "%u", &n) != 1) { fprintf(stderr, "invalid arg for transport-format\n"); return -1; } params->transport_format = n; break; case 'C': params->adts_crc_check = 1; break; case 'P': if (sscanf(optarg, "%u", &n) != 1) { fprintf(stderr, "invalid arg for header-period\n"); return -1; } params->header_period = n; break; case 'o': params->output_filename = optarg; break; case 'G': if (sscanf(optarg, "%u", &n) != 1 || n > 2) { fprintf(stderr, "invalid arg for gapless-mode\n"); return -1; } params->gapless_mode = n; break; case OPT_INCLUDE_SBR_DELAY: params->include_sbr_delay = 1; break; case 'I': params->ignore_length = 1; break; case 'S': params->silent = 1; break; case OPT_MOOV_BEFORE_MDAT: params->moov_before_mdat = 1; break; case 'R': params->is_raw = 1; break; case OPT_RAW_CHANNELS: if (sscanf(optarg, "%u", &n) != 1) { fprintf(stderr, "invalid arg for raw-channels\n"); return -1; } params->raw_channels = n; break; case OPT_RAW_RATE: if (sscanf(optarg, "%u", &n) != 1) { fprintf(stderr, "invalid arg for raw-rate\n"); return -1; } params->raw_rate = n; break; case OPT_RAW_FORMAT: params->raw_format = optarg; break; case M4AF_TAG_TITLE: case M4AF_TAG_ARTIST: case M4AF_TAG_ALBUM: case M4AF_TAG_GENRE: case M4AF_TAG_DATE: case M4AF_TAG_COMPOSER: case M4AF_TAG_GROUPING: case M4AF_TAG_COMMENT: case M4AF_TAG_ALBUM_ARTIST: case M4AF_TAG_TRACK: case M4AF_TAG_DISK: case M4AF_TAG_TEMPO: aacenc_add_tag_to_store(¶ms->tags, ch, 0, optarg, strlen(optarg), 0); break; case OPT_SHORT_TAG: case OPT_SHORT_TAG_FILE: case OPT_LONG_TAG: { char *val; size_t klen; unsigned fcc = M4AF_FOURCC('-','-','-','-'); if ((val = strchr(optarg, ':')) == 0) { fprintf(stderr, "invalid arg for tag\n"); return -1; } *val++ = '\0'; if (ch == OPT_SHORT_TAG || ch == OPT_SHORT_TAG_FILE) { /* * take care of U+00A9(COPYRIGHT SIGN). * 1) if length of fcc is 3, we prepend '\xa9'. * 2) U+00A9 becomes "\xc2\xa9" in UTF-8. Therefore * we remove first '\xc2'. */ if (optarg[0] == '\xc2') ++optarg; if ((klen = strlen(optarg))== 3) fcc = 0xa9; else if (klen != 4) { fprintf(stderr, "invalid arg for tag\n"); return -1; } for (; *optarg; ++optarg) fcc = ((fcc << 8) | (*optarg & 0xff)); } aacenc_add_tag_to_store(¶ms->tags, fcc, optarg, val, strlen(val), ch == OPT_SHORT_TAG_FILE); } break; case OPT_TAG_FROM_JSON: params->json_filename = optarg; break; default: return usage(), -1; } } if (argc == optind) return usage(), -1; if (!params->bitrate && !params->bitrate_mode) { fprintf(stderr, "bitrate or bitrate-mode is mandatory\n"); return -1; } if (params->output_filename && !strcmp(params->output_filename, "-") && !params->transport_format) { fprintf(stderr, "stdout streaming is not available on M4A output\n"); return -1; } if (params->bitrate && params->bitrate < 10000) params->bitrate *= 1000; if (params->is_raw) { if (!params->raw_channels) params->raw_channels = 2; if (!params->raw_rate) params->raw_rate = 44100; if (!params->raw_format) params->raw_format = "S16L"; } params->input_filename = argv[optind]; return 0; };