]>
Commit | Line | Data |
---|---|---|
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> | |
17 | #include "caf_reader.h" | |
18 | #include "m4af.h" | |
19 | ||
20 | typedef 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 | ||
32 | static const pcm_sample_description_t *caf_get_format(pcm_reader_t *reader) | |
33 | { | |
34 | return &((caf_reader_t *)reader)->sample_format; | |
35 | } | |
36 | ||
37 | static int64_t caf_get_length(pcm_reader_t *reader) | |
38 | { | |
39 | return ((caf_reader_t *)reader)->length; | |
40 | } | |
41 | ||
42 | static int64_t caf_get_position(pcm_reader_t *reader) | |
43 | { | |
44 | return ((caf_reader_t *)reader)->position; | |
45 | } | |
46 | ||
47 | static void caf_teardown(pcm_reader_t **reader) | |
48 | { | |
49 | free(*reader); | |
50 | *reader = 0; | |
51 | } | |
52 | ||
53 | static | |
54 | uint32_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 | ||
62 | static | |
63 | int 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; | |
97 | FAIL: | |
98 | return -1; | |
99 | } | |
100 | ||
101 | static | |
102 | int 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 | ||
127 | static | |
128 | int 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; | |
166 | FAIL: | |
167 | return 0; | |
168 | } | |
169 | ||
170 | static | |
171 | int 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; | |
196 | reader->data_offset += 12; | |
197 | break; | |
198 | } else | |
199 | TRY_IO(pcm_skip(&reader->io, chunk_size)); | |
200 | ||
201 | reader->data_offset += (chunk_size + 8); | |
202 | } | |
203 | ENSURE(reader->sample_format.channels_per_frame); | |
204 | ENSURE(fcc == M4AF_FOURCC('d','a','t','a')); | |
205 | return 0; | |
206 | FAIL: | |
207 | return -1; | |
208 | } | |
209 | ||
210 | static pcm_reader_vtbl_t caf_vtable = { | |
211 | caf_get_format, | |
212 | caf_get_length, | |
213 | caf_get_position, | |
214 | caf_read_frames, | |
215 | caf_teardown | |
216 | }; | |
217 | ||
218 | pcm_reader_t *caf_open(pcm_io_context_t *io, | |
219 | aacenc_tag_callback_t tag_callback, void *tag_ctx) | |
220 | { | |
221 | caf_reader_t *reader = 0; | |
222 | int64_t data_length; | |
223 | unsigned bpf; | |
224 | ||
225 | if ((reader = calloc(1, sizeof(caf_reader_t))) == 0) | |
226 | return 0; | |
227 | memcpy(&reader->io, io, sizeof(pcm_io_context_t)); | |
228 | reader->tag_callback = tag_callback; | |
229 | reader->tag_ctx = tag_ctx; | |
230 | ||
231 | if (caf_parse(reader, &data_length) < 0) { | |
232 | free(reader); | |
233 | return 0; | |
234 | } | |
235 | bpf = reader->sample_format.bytes_per_frame; | |
236 | ||
237 | /* CAF uses -1 to indicate "unknown size" */ | |
238 | if (data_length < 0 || data_length % bpf) | |
239 | reader->length = INT64_MAX; | |
240 | else | |
241 | reader->length = data_length / bpf; | |
242 | ||
243 | if (reader->length == INT64_MAX) { | |
244 | if (pcm_seek(&reader->io, 0, SEEK_END) >= 0) { | |
245 | int64_t size = pcm_tell(&reader->io); | |
246 | if (size > 0) | |
247 | reader->length = (size - reader->data_offset) / bpf; | |
248 | pcm_seek(&reader->io, reader->data_offset, SEEK_SET); | |
249 | } | |
250 | } | |
251 | reader->vtbl = &caf_vtable; | |
252 | return (pcm_reader_t *)reader; | |
253 | } |