1 /*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "WAVExtractor"
19 #include <utils/Log.h>
20
21 #include "WAVExtractor.h"
22
23 #include <android-base/properties.h>
24 #include <android/binder_ibinder.h> // for AIBinder_getCallingUid
25 #include <audio_utils/primitives.h>
26 #include <media/stagefright/foundation/ADebug.h>
27 #include <media/stagefright/MediaDefs.h>
28 #include <media/stagefright/MediaErrors.h>
29 #include <media/stagefright/MetaData.h>
30 #include <private/android_filesystem_config.h> // for AID_MEDIA
31 #include <system/audio.h>
32 #include <utils/String8.h>
33 #include <cutils/bitops.h>
34
35 #define CHANNEL_MASK_USE_CHANNEL_ORDER 0
36
37 // NOTE: This code assumes the device processor is little endian.
38
39 namespace android {
40
41 // MediaServer is capable of handling float extractor output, but general processes
42 // may not be able to do so.
43 // TODO: Improve API to set extractor float output.
44 // (Note: duplicated with FLACExtractor.cpp)
shouldExtractorOutputFloat(int bitsPerSample)45 static inline bool shouldExtractorOutputFloat(int bitsPerSample)
46 {
47 return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA
48 && android::base::GetBoolProperty("media.extractor.float", true);
49 }
50
51 enum {
52 WAVE_FORMAT_PCM = 0x0001,
53 WAVE_FORMAT_IEEE_FLOAT = 0x0003,
54 WAVE_FORMAT_ALAW = 0x0006,
55 WAVE_FORMAT_MULAW = 0x0007,
56 WAVE_FORMAT_MSGSM = 0x0031,
57 WAVE_FORMAT_EXTENSIBLE = 0xFFFE
58 };
59
60 static const char* WAVEEXT_SUBFORMAT = "\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71";
61 static const char* AMBISONIC_SUBFORMAT = "\x00\x00\x21\x07\xD3\x11\x86\x44\xC8\xC1\xCA\x00\x00\x00";
62
U32_LE_AT(const uint8_t * ptr)63 static uint32_t U32_LE_AT(const uint8_t *ptr) {
64 return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0];
65 }
66
U16_LE_AT(const uint8_t * ptr)67 static uint16_t U16_LE_AT(const uint8_t *ptr) {
68 return ptr[1] << 8 | ptr[0];
69 }
70
71 struct WAVSource : public MediaTrackHelper {
72 WAVSource(
73 DataSourceHelper *dataSource,
74 AMediaFormat *meta,
75 uint16_t waveFormat,
76 bool outputFloat,
77 off64_t offset, size_t size);
78
79 virtual media_status_t start();
80 virtual media_status_t stop();
81 virtual media_status_t getFormat(AMediaFormat *meta);
82
83 virtual media_status_t read(
84 MediaBufferHelper **buffer, const ReadOptions *options = NULL);
85
supportsNonBlockingReadandroid::WAVSource86 bool supportsNonBlockingRead() override { return false; }
87
88 protected:
89 virtual ~WAVSource();
90
91 private:
92 static const size_t kMaxFrameSize;
93
94 DataSourceHelper *mDataSource;
95 AMediaFormat *mMeta;
96 uint16_t mWaveFormat;
97 const bool mOutputFloat;
98 int32_t mSampleRate;
99 int32_t mNumChannels;
100 int32_t mBitsPerSample;
101 off64_t mOffset;
102 size_t mSize;
103 bool mStarted;
104 off64_t mCurrentPos;
105
106 WAVSource(const WAVSource &);
107 WAVSource &operator=(const WAVSource &);
108 };
109
WAVExtractor(DataSourceHelper * source)110 WAVExtractor::WAVExtractor(DataSourceHelper *source)
111 : mDataSource(source),
112 mValidFormat(false),
113 mChannelMask(CHANNEL_MASK_USE_CHANNEL_ORDER) {
114 mTrackMeta = AMediaFormat_new();
115 mInitCheck = init();
116 }
117
~WAVExtractor()118 WAVExtractor::~WAVExtractor() {
119 delete mDataSource;
120 AMediaFormat_delete(mTrackMeta);
121 }
122
getMetaData(AMediaFormat * meta)123 media_status_t WAVExtractor::getMetaData(AMediaFormat *meta) {
124 AMediaFormat_clear(meta);
125 if (mInitCheck == OK) {
126 AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_WAV);
127 }
128
129 return AMEDIA_OK;
130 }
131
countTracks()132 size_t WAVExtractor::countTracks() {
133 return mInitCheck == OK ? 1 : 0;
134 }
135
getTrack(size_t index)136 MediaTrackHelper *WAVExtractor::getTrack(size_t index) {
137 if (mInitCheck != OK || index > 0) {
138 return NULL;
139 }
140
141 return new WAVSource(
142 mDataSource, mTrackMeta,
143 mWaveFormat, shouldExtractorOutputFloat(mBitsPerSample), mDataOffset, mDataSize);
144 }
145
getTrackMetaData(AMediaFormat * meta,size_t index,uint32_t)146 media_status_t WAVExtractor::getTrackMetaData(
147 AMediaFormat *meta,
148 size_t index, uint32_t /* flags */) {
149 if (mInitCheck != OK || index > 0) {
150 return AMEDIA_ERROR_UNKNOWN;
151 }
152
153 const media_status_t status = AMediaFormat_copy(meta, mTrackMeta);
154 if (status == OK) {
155 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
156 shouldExtractorOutputFloat(mBitsPerSample)
157 ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
158 }
159 return status;
160 }
161
init()162 status_t WAVExtractor::init() {
163 uint8_t header[12];
164 if (mDataSource->readAt(
165 0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
166 return NO_INIT;
167 }
168
169 if (memcmp(header, "RIFF", 4) || memcmp(&header[8], "WAVE", 4)) {
170 return NO_INIT;
171 }
172
173 size_t totalSize = U32_LE_AT(&header[4]);
174
175 off64_t offset = 12;
176 size_t remainingSize = totalSize;
177 while (remainingSize >= 8) {
178 uint8_t chunkHeader[8];
179 if (mDataSource->readAt(offset, chunkHeader, 8) < 8) {
180 return NO_INIT;
181 }
182
183 remainingSize -= 8;
184 offset += 8;
185
186 uint32_t chunkSize = U32_LE_AT(&chunkHeader[4]);
187
188 if (chunkSize > remainingSize) {
189 return NO_INIT;
190 }
191
192 if (!memcmp(chunkHeader, "fmt ", 4)) {
193 if (chunkSize < 16) {
194 return NO_INIT;
195 }
196
197 uint8_t formatSpec[40];
198 if (mDataSource->readAt(offset, formatSpec, 2) < 2) {
199 return NO_INIT;
200 }
201
202 mWaveFormat = U16_LE_AT(formatSpec);
203 if (mWaveFormat != WAVE_FORMAT_PCM
204 && mWaveFormat != WAVE_FORMAT_IEEE_FLOAT
205 && mWaveFormat != WAVE_FORMAT_ALAW
206 && mWaveFormat != WAVE_FORMAT_MULAW
207 && mWaveFormat != WAVE_FORMAT_MSGSM
208 && mWaveFormat != WAVE_FORMAT_EXTENSIBLE) {
209 return AMEDIA_ERROR_UNSUPPORTED;
210 }
211
212 uint8_t fmtSize = 16;
213 if (mWaveFormat == WAVE_FORMAT_EXTENSIBLE) {
214 fmtSize = 40;
215 }
216 if (mDataSource->readAt(offset, formatSpec, fmtSize) < fmtSize) {
217 return NO_INIT;
218 }
219
220 mNumChannels = U16_LE_AT(&formatSpec[2]);
221
222 if (mNumChannels < 1 || mNumChannels > FCC_8) {
223 ALOGE("Unsupported number of channels (%d)", mNumChannels);
224 return AMEDIA_ERROR_UNSUPPORTED;
225 }
226
227 if (mWaveFormat != WAVE_FORMAT_EXTENSIBLE) {
228 if (mNumChannels != 1 && mNumChannels != FCC_2) {
229 ALOGW("More than 2 channels (%d) in non-WAVE_EXT, unknown channel mask",
230 mNumChannels);
231 }
232 }
233
234 mSampleRate = U32_LE_AT(&formatSpec[4]);
235
236 if (mSampleRate == 0) {
237 return ERROR_MALFORMED;
238 }
239
240 mBitsPerSample = U16_LE_AT(&formatSpec[14]);
241
242 if (mWaveFormat == WAVE_FORMAT_EXTENSIBLE) {
243 uint16_t validBitsPerSample = U16_LE_AT(&formatSpec[18]);
244 if (validBitsPerSample != mBitsPerSample) {
245 if (validBitsPerSample != 0) {
246 ALOGE("validBits(%d) != bitsPerSample(%d) are not supported",
247 validBitsPerSample, mBitsPerSample);
248 return AMEDIA_ERROR_UNSUPPORTED;
249 } else {
250 // we only support valitBitsPerSample == bitsPerSample but some WAV_EXT
251 // writers don't correctly set the valid bits value, and leave it at 0.
252 ALOGW("WAVE_EXT has 0 valid bits per sample, ignoring");
253 }
254 }
255
256 mChannelMask = U32_LE_AT(&formatSpec[20]);
257 ALOGV("numChannels=%d channelMask=0x%x", mNumChannels, mChannelMask);
258 if ((mChannelMask >> 18) != 0) {
259 ALOGE("invalid channel mask 0x%x", mChannelMask);
260 return ERROR_MALFORMED;
261 }
262
263 if ((mChannelMask != CHANNEL_MASK_USE_CHANNEL_ORDER)
264 && (popcount(mChannelMask) != mNumChannels)) {
265 ALOGE("invalid number of channels (%d) in channel mask (0x%x)",
266 popcount(mChannelMask), mChannelMask);
267 return ERROR_MALFORMED;
268 }
269
270 // In a WAVE_EXT header, the first two bytes of the GUID stored at byte 24 contain
271 // the sample format, using the same definitions as a regular WAV header
272 mWaveFormat = U16_LE_AT(&formatSpec[24]);
273 if (memcmp(&formatSpec[26], WAVEEXT_SUBFORMAT, 14) &&
274 memcmp(&formatSpec[26], AMBISONIC_SUBFORMAT, 14)) {
275 ALOGE("unsupported GUID");
276 return ERROR_UNSUPPORTED;
277 }
278 }
279
280 if (mWaveFormat == WAVE_FORMAT_PCM) {
281 if (mBitsPerSample != 8 && mBitsPerSample != 16
282 && mBitsPerSample != 24 && mBitsPerSample != 32) {
283 return ERROR_UNSUPPORTED;
284 }
285 } else if (mWaveFormat == WAVE_FORMAT_IEEE_FLOAT) {
286 if (mBitsPerSample != 32) { // TODO we don't support double
287 return ERROR_UNSUPPORTED;
288 }
289 }
290 else if (mWaveFormat == WAVE_FORMAT_MSGSM) {
291 if (mBitsPerSample != 0) {
292 return ERROR_UNSUPPORTED;
293 }
294 } else if (mWaveFormat == WAVE_FORMAT_MULAW || mWaveFormat == WAVE_FORMAT_ALAW) {
295 if (mBitsPerSample != 8) {
296 return ERROR_UNSUPPORTED;
297 }
298 } else {
299 return ERROR_UNSUPPORTED;
300 }
301
302 mValidFormat = true;
303 } else if (!memcmp(chunkHeader, "data", 4)) {
304 if (mValidFormat) {
305 mDataOffset = offset;
306 mDataSize = chunkSize;
307
308 AMediaFormat_clear(mTrackMeta);
309
310 switch (mWaveFormat) {
311 case WAVE_FORMAT_PCM:
312 case WAVE_FORMAT_IEEE_FLOAT:
313 AMediaFormat_setString(mTrackMeta,
314 AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_RAW);
315 break;
316 case WAVE_FORMAT_ALAW:
317 AMediaFormat_setString(mTrackMeta,
318 AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
319 break;
320 case WAVE_FORMAT_MSGSM:
321 AMediaFormat_setString(mTrackMeta,
322 AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MSGSM);
323 break;
324 default:
325 CHECK_EQ(mWaveFormat, (uint16_t)WAVE_FORMAT_MULAW);
326 AMediaFormat_setString(mTrackMeta,
327 AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
328 break;
329 }
330
331 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, mNumChannels);
332 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, mChannelMask);
333 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, mSampleRate);
334 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, mBitsPerSample);
335 int64_t durationUs = 0;
336 if (mWaveFormat == WAVE_FORMAT_MSGSM) {
337 // 65 bytes decode to 320 8kHz samples
338 durationUs =
339 1000000LL * (mDataSize / 65 * 320) / 8000;
340 } else {
341 size_t bytesPerSample = mBitsPerSample >> 3;
342
343 if (!bytesPerSample || !mNumChannels)
344 return AMEDIA_ERROR_MALFORMED;
345
346 size_t num_samples = mDataSize / (mNumChannels * bytesPerSample);
347
348 if (!mSampleRate)
349 return AMEDIA_ERROR_MALFORMED;
350
351 durationUs =
352 1000000LL * num_samples / mSampleRate;
353 }
354
355 AMediaFormat_setInt64(mTrackMeta, AMEDIAFORMAT_KEY_DURATION, durationUs);
356
357 return OK;
358 }
359 }
360
361 offset += chunkSize;
362 }
363
364 return NO_INIT;
365 }
366
367 const size_t WAVSource::kMaxFrameSize = 32768;
368
WAVSource(DataSourceHelper * dataSource,AMediaFormat * meta,uint16_t waveFormat,bool outputFloat,off64_t offset,size_t size)369 WAVSource::WAVSource(
370 DataSourceHelper *dataSource,
371 AMediaFormat *meta,
372 uint16_t waveFormat,
373 bool outputFloat,
374 off64_t offset, size_t size)
375 : mDataSource(dataSource),
376 mMeta(meta),
377 mWaveFormat(waveFormat),
378 mOutputFloat(outputFloat),
379 mOffset(offset),
380 mSize(size),
381 mStarted(false) {
382 CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate));
383 CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mNumChannels));
384 CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &mBitsPerSample));
385 }
386
~WAVSource()387 WAVSource::~WAVSource() {
388 if (mStarted) {
389 stop();
390 }
391 }
392
start()393 media_status_t WAVSource::start() {
394 ALOGV("WAVSource::start");
395
396 CHECK(!mStarted);
397
398 // some WAV files may have large audio buffers that use shared memory transfer.
399 if (!mBufferGroup->init(4 /* buffers */, kMaxFrameSize)) {
400 return AMEDIA_ERROR_UNKNOWN;
401 }
402
403 mCurrentPos = mOffset;
404
405 mStarted = true;
406
407 return AMEDIA_OK;
408 }
409
stop()410 media_status_t WAVSource::stop() {
411 ALOGV("WAVSource::stop");
412
413 CHECK(mStarted);
414
415 mStarted = false;
416
417 return AMEDIA_OK;
418 }
419
getFormat(AMediaFormat * meta)420 media_status_t WAVSource::getFormat(AMediaFormat *meta) {
421 ALOGV("WAVSource::getFormat");
422
423 const media_status_t status = AMediaFormat_copy(meta, mMeta);
424 if (status == OK) {
425 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, kMaxFrameSize);
426 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
427 mOutputFloat ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
428 }
429 return status;
430 }
431
read(MediaBufferHelper ** out,const ReadOptions * options)432 media_status_t WAVSource::read(
433 MediaBufferHelper **out, const ReadOptions *options) {
434 *out = NULL;
435
436 if (options != nullptr && options->getNonBlocking() && !mBufferGroup->has_buffers()) {
437 return AMEDIA_ERROR_WOULD_BLOCK;
438 }
439
440 int64_t seekTimeUs;
441 ReadOptions::SeekMode mode;
442 if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
443 int64_t pos = 0;
444
445 if (mWaveFormat == WAVE_FORMAT_MSGSM) {
446 // 65 bytes decode to 320 8kHz samples
447 int64_t samplenumber = (seekTimeUs * mSampleRate) / 1000000;
448 int64_t framenumber = samplenumber / 320;
449 pos = framenumber * 65;
450 } else {
451 pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * (mBitsPerSample >> 3);
452 }
453 if (pos > (off64_t)mSize) {
454 pos = mSize;
455 }
456 mCurrentPos = pos + mOffset;
457 }
458
459 MediaBufferHelper *buffer;
460 media_status_t err = mBufferGroup->acquire_buffer(&buffer);
461 if (err != OK) {
462 return err;
463 }
464
465 // maxBytesToRead may be reduced so that in-place data conversion will fit in buffer size.
466 const size_t bufferSize = std::min(buffer->size(), kMaxFrameSize);
467 size_t maxBytesToRead;
468 if (mOutputFloat) { // destination is float at 4 bytes per sample, source may be less.
469 maxBytesToRead = (mBitsPerSample / 8) * (bufferSize / 4);
470 } else { // destination is int16_t at 2 bytes per sample, only source of 8 bits is less.
471 maxBytesToRead = mBitsPerSample == 8 ? bufferSize / 2 : bufferSize;
472 }
473
474 const size_t maxBytesAvailable =
475 (mCurrentPos - mOffset >= (off64_t)mSize)
476 ? 0 : mSize - (mCurrentPos - mOffset);
477
478 if (maxBytesToRead > maxBytesAvailable) {
479 maxBytesToRead = maxBytesAvailable;
480 }
481
482 if (mWaveFormat == WAVE_FORMAT_MSGSM) {
483 // Microsoft packs 2 frames into 65 bytes, rather than using separate 33-byte frames,
484 // so read multiples of 65, and use smaller buffers to account for ~10:1 expansion ratio
485 if (maxBytesToRead > 1024) {
486 maxBytesToRead = 1024;
487 }
488 maxBytesToRead = (maxBytesToRead / 65) * 65;
489 } else {
490 // read only integral amounts of audio unit frames.
491 const size_t inputUnitFrameSize = mNumChannels * mBitsPerSample / 8;
492 maxBytesToRead -= maxBytesToRead % inputUnitFrameSize;
493 }
494
495 ssize_t n = mDataSource->readAt(
496 mCurrentPos, buffer->data(),
497 maxBytesToRead);
498
499 if (n <= 0) {
500 buffer->release();
501 buffer = NULL;
502
503 return AMEDIA_ERROR_END_OF_STREAM;
504 }
505
506 buffer->set_range(0, n);
507
508 // TODO: add capability to return data as float PCM instead of 16 bit PCM.
509 if (mWaveFormat == WAVE_FORMAT_PCM) {
510 const size_t bytesPerFrame = (mBitsPerSample >> 3) * mNumChannels;
511 const size_t numFrames = n / bytesPerFrame;
512 const size_t numSamples = numFrames * mNumChannels;
513 if (mOutputFloat) {
514 float *fdest = (float *)buffer->data();
515 buffer->set_range(0, 4 * numSamples);
516 switch (mBitsPerSample) {
517 case 8: {
518 memcpy_to_float_from_u8(fdest, (const uint8_t *)buffer->data(), numSamples);
519 } break;
520 case 16: {
521 memcpy_to_float_from_i16(fdest, (const int16_t *)buffer->data(), numSamples);
522 } break;
523 case 24: {
524 memcpy_to_float_from_p24(fdest, (const uint8_t *)buffer->data(), numSamples);
525 } break;
526 case 32: { // buffer range is correct
527 memcpy_to_float_from_i32(fdest, (const int32_t *)buffer->data(), numSamples);
528 } break;
529 }
530 } else {
531 int16_t *idest = (int16_t *)buffer->data();
532 buffer->set_range(0, 2 * numSamples);
533 switch (mBitsPerSample) {
534 case 8: {
535 memcpy_to_i16_from_u8(idest, (const uint8_t *)buffer->data(), numSamples);
536 } break;
537 case 16:
538 // no conversion needed
539 break;
540 case 24: {
541 memcpy_to_i16_from_p24(idest, (const uint8_t *)buffer->data(), numSamples);
542 } break;
543 case 32: {
544 memcpy_to_i16_from_i32(idest, (const int32_t *)buffer->data(), numSamples);
545 } break;
546 }
547 }
548 } else if (mWaveFormat == WAVE_FORMAT_IEEE_FLOAT) {
549 if (!mOutputFloat) { // mBitsPerSample == 32
550 int16_t *idest = (int16_t *)buffer->data();
551 const size_t numSamples = n / 4;
552 memcpy_to_i16_from_float(idest, (const float *)buffer->data(), numSamples);
553 buffer->set_range(0, 2 * numSamples);
554 }
555 // Note: if output encoding is float, no need to convert if source is float.
556 }
557
558 int64_t timeStampUs = 0;
559
560 if (mWaveFormat == WAVE_FORMAT_MSGSM) {
561 timeStampUs = 1000000LL * (mCurrentPos - mOffset) * 320 / 65 / mSampleRate;
562 } else {
563 size_t bytesPerSample = mBitsPerSample >> 3;
564 timeStampUs = 1000000LL * (mCurrentPos - mOffset)
565 / (mNumChannels * bytesPerSample) / mSampleRate;
566 }
567
568 AMediaFormat *meta = buffer->meta_data();
569 AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeStampUs);
570 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
571
572 mCurrentPos += n;
573
574 *out = buffer;
575
576 return AMEDIA_OK;
577 }
578
579 ////////////////////////////////////////////////////////////////////////////////
580
CreateExtractor(CDataSource * source,void *)581 static CMediaExtractor* CreateExtractor(
582 CDataSource *source,
583 void *) {
584 return wrap(new WAVExtractor(new DataSourceHelper(source)));
585 }
586
Sniff(CDataSource * source,float * confidence,void **,FreeMetaFunc *)587 static CreatorFunc Sniff(
588 CDataSource *source,
589 float *confidence,
590 void **,
591 FreeMetaFunc *) {
592 DataSourceHelper *helper = new DataSourceHelper(source);
593 char header[12];
594 if (helper->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
595 delete helper;
596 return NULL;
597 }
598
599 if (memcmp(header, "RIFF", 4) || memcmp(&header[8], "WAVE", 4)) {
600 delete helper;
601 return NULL;
602 }
603
604 WAVExtractor *extractor = new WAVExtractor(helper); // extractor owns the helper
605 int numTracks = extractor->countTracks();
606 delete extractor;
607 if (numTracks == 0) {
608 return NULL;
609 }
610
611 *confidence = 0.3f;
612
613 return CreateExtractor;
614 }
615
616 static const char *extensions[] = {
617 "wav",
618 NULL
619 };
620
621 extern "C" {
622 // This is the only symbol that needs to be exported
623 __attribute__ ((visibility ("default")))
GETEXTRACTORDEF()624 ExtractorDef GETEXTRACTORDEF() {
625 return {
626 EXTRACTORDEF_VERSION,
627 UUID("7d613858-5837-4a38-84c5-332d1cddee27"),
628 1, // version
629 "WAV Extractor",
630 { .v3 = {Sniff, extensions} },
631 };
632 }
633
634 } // extern "C"
635
636 } // namespace android
637