SoftVorbis.cpp revision 98391d3e9c0188d477fab6044d5259eeb0014113
1/*
2 * Copyright (C) 2011 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 "SoftVorbis"
19#include <utils/Log.h>
20
21#include "SoftVorbis.h"
22
23#include <media/stagefright/foundation/ADebug.h>
24#include <media/stagefright/MediaDefs.h>
25
26extern "C" {
27    #include <Tremolo/codec_internal.h>
28
29    int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb);
30    int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb);
31    int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb);
32}
33
34namespace android {
35
36template<class T>
37static void InitOMXParams(T *params) {
38    params->nSize = sizeof(T);
39    params->nVersion.s.nVersionMajor = 1;
40    params->nVersion.s.nVersionMinor = 0;
41    params->nVersion.s.nRevision = 0;
42    params->nVersion.s.nStep = 0;
43}
44
45SoftVorbis::SoftVorbis(
46        const char *name,
47        const OMX_CALLBACKTYPE *callbacks,
48        OMX_PTR appData,
49        OMX_COMPONENTTYPE **component)
50    : SimpleSoftOMXComponent(name, callbacks, appData, component),
51      mInputBufferCount(0),
52      mState(NULL),
53      mVi(NULL),
54      mAnchorTimeUs(0),
55      mNumFramesOutput(0),
56      mNumFramesLeftOnPage(-1),
57      mOutputPortSettingsChange(NONE) {
58    initPorts();
59    CHECK_EQ(initDecoder(), (status_t)OK);
60}
61
62SoftVorbis::~SoftVorbis() {
63    if (mState != NULL) {
64        vorbis_dsp_clear(mState);
65        delete mState;
66        mState = NULL;
67    }
68
69    if (mVi != NULL) {
70        vorbis_info_clear(mVi);
71        delete mVi;
72        mVi = NULL;
73    }
74}
75
76void SoftVorbis::initPorts() {
77    OMX_PARAM_PORTDEFINITIONTYPE def;
78    InitOMXParams(&def);
79
80    def.nPortIndex = 0;
81    def.eDir = OMX_DirInput;
82    def.nBufferCountMin = kNumBuffers;
83    def.nBufferCountActual = def.nBufferCountMin;
84    def.nBufferSize = 8192;
85    def.bEnabled = OMX_TRUE;
86    def.bPopulated = OMX_FALSE;
87    def.eDomain = OMX_PortDomainAudio;
88    def.bBuffersContiguous = OMX_FALSE;
89    def.nBufferAlignment = 1;
90
91    def.format.audio.cMIMEType =
92        const_cast<char *>(MEDIA_MIMETYPE_AUDIO_VORBIS);
93
94    def.format.audio.pNativeRender = NULL;
95    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
96    def.format.audio.eEncoding = OMX_AUDIO_CodingVORBIS;
97
98    addPort(def);
99
100    def.nPortIndex = 1;
101    def.eDir = OMX_DirOutput;
102    def.nBufferCountMin = kNumBuffers;
103    def.nBufferCountActual = def.nBufferCountMin;
104    def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t);
105    def.bEnabled = OMX_TRUE;
106    def.bPopulated = OMX_FALSE;
107    def.eDomain = OMX_PortDomainAudio;
108    def.bBuffersContiguous = OMX_FALSE;
109    def.nBufferAlignment = 2;
110
111    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
112    def.format.audio.pNativeRender = NULL;
113    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
114    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
115
116    addPort(def);
117}
118
119status_t SoftVorbis::initDecoder() {
120    return OK;
121}
122
123OMX_ERRORTYPE SoftVorbis::internalGetParameter(
124        OMX_INDEXTYPE index, OMX_PTR params) {
125    switch (index) {
126        case OMX_IndexParamAudioVorbis:
127        {
128            OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams =
129                (OMX_AUDIO_PARAM_VORBISTYPE *)params;
130
131            if (vorbisParams->nPortIndex != 0) {
132                return OMX_ErrorUndefined;
133            }
134
135            vorbisParams->nBitRate = 0;
136            vorbisParams->nMinBitRate = 0;
137            vorbisParams->nMaxBitRate = 0;
138            vorbisParams->nAudioBandWidth = 0;
139            vorbisParams->nQuality = 3;
140            vorbisParams->bManaged = OMX_FALSE;
141            vorbisParams->bDownmix = OMX_FALSE;
142
143            if (!isConfigured()) {
144                vorbisParams->nChannels = 1;
145                vorbisParams->nSampleRate = 44100;
146            } else {
147                vorbisParams->nChannels = mVi->channels;
148                vorbisParams->nSampleRate = mVi->rate;
149                vorbisParams->nBitRate = mVi->bitrate_nominal;
150                vorbisParams->nMinBitRate = mVi->bitrate_lower;
151                vorbisParams->nMaxBitRate = mVi->bitrate_upper;
152            }
153
154            return OMX_ErrorNone;
155        }
156
157        case OMX_IndexParamAudioPcm:
158        {
159            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
160                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
161
162            if (pcmParams->nPortIndex != 1) {
163                return OMX_ErrorUndefined;
164            }
165
166            pcmParams->eNumData = OMX_NumericalDataSigned;
167            pcmParams->eEndian = OMX_EndianBig;
168            pcmParams->bInterleaved = OMX_TRUE;
169            pcmParams->nBitPerSample = 16;
170            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
171            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
172            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
173
174            if (!isConfigured()) {
175                pcmParams->nChannels = 1;
176                pcmParams->nSamplingRate = 44100;
177            } else {
178                pcmParams->nChannels = mVi->channels;
179                pcmParams->nSamplingRate = mVi->rate;
180            }
181
182            return OMX_ErrorNone;
183        }
184
185        default:
186            return SimpleSoftOMXComponent::internalGetParameter(index, params);
187    }
188}
189
190OMX_ERRORTYPE SoftVorbis::internalSetParameter(
191        OMX_INDEXTYPE index, const OMX_PTR params) {
192    switch (index) {
193        case OMX_IndexParamStandardComponentRole:
194        {
195            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
196                (const OMX_PARAM_COMPONENTROLETYPE *)params;
197
198            if (strncmp((const char *)roleParams->cRole,
199                        "audio_decoder.vorbis",
200                        OMX_MAX_STRINGNAME_SIZE - 1)) {
201                return OMX_ErrorUndefined;
202            }
203
204            return OMX_ErrorNone;
205        }
206
207        case OMX_IndexParamAudioVorbis:
208        {
209            const OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams =
210                (const OMX_AUDIO_PARAM_VORBISTYPE *)params;
211
212            if (vorbisParams->nPortIndex != 0) {
213                return OMX_ErrorUndefined;
214            }
215
216            return OMX_ErrorNone;
217        }
218
219        default:
220            return SimpleSoftOMXComponent::internalSetParameter(index, params);
221    }
222}
223
224bool SoftVorbis::isConfigured() const {
225    return mInputBufferCount >= 2;
226}
227
228static void makeBitReader(
229        const void *data, size_t size,
230        ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) {
231    buf->data = (uint8_t *)data;
232    buf->size = size;
233    buf->refcount = 1;
234    buf->ptr.owner = NULL;
235
236    ref->buffer = buf;
237    ref->begin = 0;
238    ref->length = size;
239    ref->next = NULL;
240
241    oggpack_readinit(bits, ref);
242}
243
244void SoftVorbis::onQueueFilled(OMX_U32 portIndex) {
245    List<BufferInfo *> &inQueue = getPortQueue(0);
246    List<BufferInfo *> &outQueue = getPortQueue(1);
247
248    if (mOutputPortSettingsChange != NONE) {
249        return;
250    }
251
252    if (portIndex == 0 && mInputBufferCount < 2) {
253        BufferInfo *info = *inQueue.begin();
254        OMX_BUFFERHEADERTYPE *header = info->mHeader;
255
256        const uint8_t *data = header->pBuffer + header->nOffset;
257        size_t size = header->nFilledLen;
258
259        ogg_buffer buf;
260        ogg_reference ref;
261        oggpack_buffer bits;
262
263        makeBitReader(
264                (const uint8_t *)data + 7, size - 7,
265                &buf, &ref, &bits);
266
267        if (mInputBufferCount == 0) {
268            CHECK(mVi == NULL);
269            mVi = new vorbis_info;
270            vorbis_info_init(mVi);
271
272            CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits));
273        } else {
274            CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits));
275
276            CHECK(mState == NULL);
277            mState = new vorbis_dsp_state;
278            CHECK_EQ(0, vorbis_dsp_init(mState, mVi));
279
280            notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
281            mOutputPortSettingsChange = AWAITING_DISABLED;
282        }
283
284        inQueue.erase(inQueue.begin());
285        info->mOwnedByUs = false;
286        notifyEmptyBufferDone(header);
287
288        ++mInputBufferCount;
289
290        return;
291    }
292
293    while (!inQueue.empty() && !outQueue.empty()) {
294        BufferInfo *inInfo = *inQueue.begin();
295        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
296
297        BufferInfo *outInfo = *outQueue.begin();
298        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
299
300        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
301            inQueue.erase(inQueue.begin());
302            inInfo->mOwnedByUs = false;
303            notifyEmptyBufferDone(inHeader);
304
305            outHeader->nFilledLen = 0;
306            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
307
308            outQueue.erase(outQueue.begin());
309            outInfo->mOwnedByUs = false;
310            notifyFillBufferDone(outHeader);
311            return;
312        }
313
314        int32_t numPageSamples;
315        CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples));
316        memcpy(&numPageSamples,
317               inHeader->pBuffer
318                + inHeader->nOffset + inHeader->nFilledLen - 4,
319               sizeof(numPageSamples));
320
321        if (numPageSamples >= 0) {
322            mNumFramesLeftOnPage = numPageSamples;
323        }
324
325        if (inHeader->nOffset == 0) {
326            mAnchorTimeUs = inHeader->nTimeStamp;
327            mNumFramesOutput = 0;
328        }
329
330        inHeader->nFilledLen -= sizeof(numPageSamples);;
331
332        ogg_buffer buf;
333        buf.data = inHeader->pBuffer + inHeader->nOffset;
334        buf.size = inHeader->nFilledLen;
335        buf.refcount = 1;
336        buf.ptr.owner = NULL;
337
338        ogg_reference ref;
339        ref.buffer = &buf;
340        ref.begin = 0;
341        ref.length = buf.size;
342        ref.next = NULL;
343
344        ogg_packet pack;
345        pack.packet = &ref;
346        pack.bytes = ref.length;
347        pack.b_o_s = 0;
348        pack.e_o_s = 0;
349        pack.granulepos = 0;
350        pack.packetno = 0;
351
352        int numFrames = 0;
353
354        int err = vorbis_dsp_synthesis(mState, &pack, 1);
355        if (err != 0) {
356            ALOGW("vorbis_dsp_synthesis returned %d", err);
357        } else {
358            numFrames = vorbis_dsp_pcmout(
359                    mState, (int16_t *)outHeader->pBuffer,
360                    kMaxNumSamplesPerBuffer);
361
362            if (numFrames < 0) {
363                ALOGE("vorbis_dsp_pcmout returned %d", numFrames);
364                numFrames = 0;
365            }
366        }
367
368        if (mNumFramesLeftOnPage >= 0) {
369            if (numFrames > mNumFramesLeftOnPage) {
370                ALOGV("discarding %d frames at end of page",
371                     numFrames - mNumFramesLeftOnPage);
372                numFrames = mNumFramesLeftOnPage;
373            }
374            mNumFramesLeftOnPage -= numFrames;
375        }
376
377        outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels;
378        outHeader->nOffset = 0;
379        outHeader->nFlags = 0;
380
381        outHeader->nTimeStamp =
382            mAnchorTimeUs
383                + (mNumFramesOutput * 1000000ll) / mVi->rate;
384
385        mNumFramesOutput += numFrames;
386
387        inInfo->mOwnedByUs = false;
388        inQueue.erase(inQueue.begin());
389        inInfo = NULL;
390        notifyEmptyBufferDone(inHeader);
391        inHeader = NULL;
392
393        outInfo->mOwnedByUs = false;
394        outQueue.erase(outQueue.begin());
395        outInfo = NULL;
396        notifyFillBufferDone(outHeader);
397        outHeader = NULL;
398
399        ++mInputBufferCount;
400    }
401}
402
403void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) {
404    if (portIndex == 0 && mState != NULL) {
405        // Make sure that the next buffer output does not still
406        // depend on fragments from the last one decoded.
407
408        mNumFramesOutput = 0;
409        vorbis_dsp_restart(mState);
410    }
411}
412
413void SoftVorbis::onReset() {
414    mInputBufferCount = 0;
415    mNumFramesOutput = 0;
416    if (mState != NULL) {
417        vorbis_dsp_clear(mState);
418        delete mState;
419        mState = NULL;
420    }
421
422    if (mVi != NULL) {
423        vorbis_info_clear(mVi);
424        delete mVi;
425        mVi = NULL;
426    }
427}
428
429void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
430    if (portIndex != 1) {
431        return;
432    }
433
434    switch (mOutputPortSettingsChange) {
435        case NONE:
436            break;
437
438        case AWAITING_DISABLED:
439        {
440            CHECK(!enabled);
441            mOutputPortSettingsChange = AWAITING_ENABLED;
442            break;
443        }
444
445        default:
446        {
447            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
448            CHECK(enabled);
449            mOutputPortSettingsChange = NONE;
450            break;
451        }
452    }
453}
454
455}  // namespace android
456
457android::SoftOMXComponent *createSoftOMXComponent(
458        const char *name, const OMX_CALLBACKTYPE *callbacks,
459        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
460    return new android::SoftVorbis(name, callbacks, appData, component);
461}
462