refactor pcm reader framework
[fdkaac.git] / src / caf_reader.c
CommitLineData
1af8624b 1/*
2 * Copyright (C) 2013 nu774
3 * For conditions of distribution and use, see copyright notice in COPYING
4 */
5#if HAVE_CONFIG_H
6# include "config.h"
7#endif
8
9#if HAVE_STDINT_H
10# include <stdint.h>
11#endif
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <stdarg.h>
a7e00a42 17#include "pcm_reader.h"
1af8624b 18#include "m4af.h"
19
20typedef struct caf_reader_t {
21 pcm_reader_vtbl_t *vtbl;
22 pcm_sample_description_t sample_format;
23 int64_t length;
24 int64_t position;
25 int64_t data_offset;
26 pcm_io_context_t io;
27 aacenc_tag_callback_t tag_callback;
28 void *tag_ctx;
29 uint8_t chanmap[8];
30} caf_reader_t;
31
32static const pcm_sample_description_t *caf_get_format(pcm_reader_t *reader)
33{
34 return &((caf_reader_t *)reader)->sample_format;
35}
36
37static int64_t caf_get_length(pcm_reader_t *reader)
38{
39 return ((caf_reader_t *)reader)->length;
40}
41
42static int64_t caf_get_position(pcm_reader_t *reader)
43{
44 return ((caf_reader_t *)reader)->position;
45}
46
47static void caf_teardown(pcm_reader_t **reader)
48{
49 free(*reader);
50 *reader = 0;
51}
52
53static
54uint32_t caf_next_chunk(caf_reader_t *reader, int64_t *chunk_size)
55{
56 uint32_t fcc;
57 if (pcm_scanb(&reader->io, "LQ", &fcc, chunk_size) == 2)
58 return fcc;
59 return 0;
60}
61
62static
63int caf_desc(caf_reader_t *reader, int64_t chunk_size)
64{
65 double mSampleRate;
66 uint32_t mFormatID, mFormatFlags, mBytesPerPacket, mFramesPerPacket,
67 mChannelsPerFrame, mBitsPerChannel;
68 pcm_sample_description_t *desc = &reader->sample_format;
69
70 ENSURE(chunk_size >= 32);
71 TRY_IO(pcm_scanb(&reader->io, "QLLLLLL", &mSampleRate, &mFormatID,
72 &mFormatFlags, &mBytesPerPacket, &mFramesPerPacket,
73 &mChannelsPerFrame, &mBitsPerChannel) != 7);
74
75 ENSURE(mFormatID == M4AF_FOURCC('l','p','c','m'));
76 ENSURE(mSampleRate && mBytesPerPacket &&
77 mChannelsPerFrame >= 1 && mChannelsPerFrame <= 8 &&
78 mBitsPerChannel && mFramesPerPacket == 1 &&
79 mBytesPerPacket % mChannelsPerFrame == 0 &&
80 mBytesPerPacket >= mChannelsPerFrame * ((mBitsPerChannel + 7) / 8));
81
82 desc->sample_rate = mSampleRate;
83 desc->bits_per_channel = mBitsPerChannel;
84 desc->bytes_per_frame = mBytesPerPacket;
85 desc->channels_per_frame = mChannelsPerFrame;
86
87 switch (mFormatFlags) {
88 case 0: desc->sample_type = PCM_TYPE_SINT_BE; break;
89 case 1: desc->sample_type = PCM_TYPE_FLOAT_BE; break;
90 case 2: desc->sample_type = PCM_TYPE_SINT; break;
91 case 3: desc->sample_type = PCM_TYPE_FLOAT; break;
92 default: goto FAIL;
93 }
94
95 TRY_IO(pcm_skip(&reader->io, chunk_size - 32));
96 return 0;
97FAIL:
98 return -1;
99}
100
101static
102int caf_info(caf_reader_t *reader, int64_t chunk_size)
103{
104 char *buf, *key, *val, *end;
105 size_t len;
106
107 if (chunk_size < 4 || (buf = malloc(chunk_size)) == 0)
108 return -1;
109 pcm_read(&reader->io, buf, chunk_size);
110 key = buf + 4;
111 end = buf + chunk_size;
112 do {
113 if ((val = key + strlen(key) + 1) < end) {
114 len = strlen(val);
115 if (reader->tag_callback)
116 reader->tag_callback(reader->tag_ctx, key, val, len);
117 key = val + len + 1;
118 }
119 } while (key < end && val < end);
120
121 if (reader->tag_callback)
122 reader->tag_callback(reader->tag_ctx, 0, 0, 0);
123 free(buf);
124 return 0;
125}
126
127static
128int caf_read_frames(pcm_reader_t *preader, void *buffer, unsigned nframes)
129{
130 int rc;
131 unsigned i, j, nbytes;
132 caf_reader_t *reader = (caf_reader_t *)preader;
133 unsigned bpf = reader->sample_format.bytes_per_frame;
134 unsigned nchannels = reader->sample_format.channels_per_frame;
135 unsigned bpc = bpf / nchannels;
136 uint8_t tmp[64]; /* enough room for maximum bpf: 8ch float64 */
137 uint8_t *bp;
138 uint8_t *chanmap = reader->chanmap;
139
140 if (nframes > reader->length - reader->position)
141 nframes = reader->length - reader->position;
142 nbytes = nframes * bpf;
143 if (nbytes) {
144 if ((rc = pcm_read(&reader->io, buffer, nbytes)) < 0)
145 return -1;
146 nframes = rc / bpf;
147 for (bp = buffer, i = 0; i < nframes; ++i, bp += bpf) {
148 memcpy(tmp, bp, bpf);
149 for (j = 0; j < nchannels; ++j)
150 memcpy(bp + bpc * j, tmp + bpc * chanmap[j], bpc);
151 }
152 reader->position += nframes;
153 }
154 if (nframes == 0) {
155 /* fetch info after data chunk */
156 uint32_t fcc;
157 int64_t chunk_size;
158 while ((fcc = caf_next_chunk(reader, &chunk_size)) != 0) {
159 if (fcc == M4AF_FOURCC('i','n','f','o'))
160 TRY_IO(caf_info(reader, chunk_size));
161 else
162 TRY_IO(pcm_skip(&reader->io, chunk_size));
163 }
164 }
165 return nframes;
166FAIL:
167 return 0;
168}
169
170static
171int caf_parse(caf_reader_t *reader, int64_t *data_length)
172{
173 uint32_t fcc;
174 int64_t chunk_size;
175
176 *data_length = 0;
177
178 /* CAFFileHeader */
179 TRY_IO(pcm_read32be(&reader->io, &fcc));
180 ENSURE(fcc == M4AF_FOURCC('c','a','f','f'));
181 TRY_IO(pcm_skip(&reader->io, 4)); /* mFileVersion, mFileFlags */
182
183 while ((fcc = caf_next_chunk(reader, &chunk_size)) != 0) {
184 if (fcc == M4AF_FOURCC('d','e','s','c'))
185 TRY_IO(caf_desc(reader, chunk_size));
186 else if (fcc == M4AF_FOURCC('i','n','f','o'))
187 TRY_IO(caf_info(reader, chunk_size));
188 else if (fcc == M4AF_FOURCC('c','h','a','n')) {
189 ENSURE(reader->sample_format.channels_per_frame);
190 if (apple_chan_chunk(&reader->io, chunk_size,
191 &reader->sample_format, reader->chanmap) < 0)
192 goto FAIL;
193 } else if (fcc == M4AF_FOURCC('d','a','t','a')) {
194 TRY_IO(pcm_skip(&reader->io, 4)); /* mEditCount */
195 *data_length = (chunk_size == ~0ULL) ? chunk_size : chunk_size - 4;
3de0e22d 196 reader->data_offset = pcm_tell(&reader->io);
1af8624b 197 break;
198 } else
199 TRY_IO(pcm_skip(&reader->io, chunk_size));
1af8624b 200 }
201 ENSURE(reader->sample_format.channels_per_frame);
202 ENSURE(fcc == M4AF_FOURCC('d','a','t','a'));
203 return 0;
204FAIL:
205 return -1;
206}
207
208static pcm_reader_vtbl_t caf_vtable = {
209 caf_get_format,
210 caf_get_length,
211 caf_get_position,
212 caf_read_frames,
213 caf_teardown
214};
215
216pcm_reader_t *caf_open(pcm_io_context_t *io,
217 aacenc_tag_callback_t tag_callback, void *tag_ctx)
218{
219 caf_reader_t *reader = 0;
220 int64_t data_length;
221 unsigned bpf;
222
223 if ((reader = calloc(1, sizeof(caf_reader_t))) == 0)
224 return 0;
225 memcpy(&reader->io, io, sizeof(pcm_io_context_t));
226 reader->tag_callback = tag_callback;
227 reader->tag_ctx = tag_ctx;
209130e8 228 memcpy(reader->chanmap, "\000\001\002\003\004\005\006\007", 8);
1af8624b 229
230 if (caf_parse(reader, &data_length) < 0) {
231 free(reader);
232 return 0;
233 }
234 bpf = reader->sample_format.bytes_per_frame;
235
236 /* CAF uses -1 to indicate "unknown size" */
237 if (data_length < 0 || data_length % bpf)
238 reader->length = INT64_MAX;
239 else
240 reader->length = data_length / bpf;
241
242 if (reader->length == INT64_MAX) {
243 if (pcm_seek(&reader->io, 0, SEEK_END) >= 0) {
244 int64_t size = pcm_tell(&reader->io);
245 if (size > 0)
246 reader->length = (size - reader->data_offset) / bpf;
247 pcm_seek(&reader->io, reader->data_offset, SEEK_SET);
248 }
249 }
250 reader->vtbl = &caf_vtable;
251 return (pcm_reader_t *)reader;
252}
This page took 0.025049 seconds and 4 git commands to generate.