SmartAudio/package/allwinner/tina_multimedia/libcedarx/android_adapter/output/soundControl.cpp

820 lines
22 KiB
C++
Raw Normal View History

2018-07-13 01:31:50 +00:00
/*
* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd.
* All rights reserved.
*
* File : soundControl.cpp
* Description : soundControl for android
* History :
*
*/
#include <utils/Errors.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "soundControl.h"
#include "cdx_log.h"
#include <media/AudioTrack.h>
#include <media/MediaPlayerInterface.h>
#include <inttypes.h>
#include <pthread.h>
#include "outputCtrl.h"
using namespace android;
#define SAVE_PCM (0)
#if SAVE_PCM
const char* binpath = "/data/camera/test.bin";
#endif
enum ESOUNDSTATUS
{
SOUND_STATUS_STOPPED = 0,
SOUND_STATUS_STARTED,
SOUND_STATUS_PAUSED
};
typedef struct AudioDirectOutPutSink
{
sp<AudioTrack> pAudioTrackHandle;
audio_output_flags_t flag;
audio_format_t format;
audio_channel_mask_t chanmask;
unsigned int samplerate;
}AudioDirectOutPutSink;
typedef struct SoundCtrlContext
{
SoundCtrl base;
MediaPlayerBase::AudioSink* pAudioSink;
sp<AudioTrack> pAudioTrack;//* new an audio track if pAudioSink not set.
unsigned int nSampleRate;
unsigned int nChannelNum;
unsigned int nBitPerSample;
unsigned int needDirectOutPut;
enum AUIDO_RAW_DATA_TYPE data_type;
AudioDirectOutPutSink directSink;
unsigned int nRoutine;
#if (CONF_ANDROID_MAJOR_VER > 5)
AudioPlaybackRate nPlayRate;
#endif
int64_t nDataSizePlayed;
int64_t nFramePosOffset;
unsigned int nFrameSize;
unsigned int nLastFramePos;
enum ESOUNDSTATUS eStatus;
pthread_mutex_t mutex;
float volume;
#if SAVE_PCM
FILE* testpcm;
#endif
#if defined(CONF_SC_WRITE_ASYNC_MODE)
size_t fillBuffer(void *data, size_t size);
void *pBufAudioSink;
size_t nSizeAudioSink;
int bReachedEOS;
pthread_cond_t cbCond;
pthread_mutex_t cbMutex;
#endif
}SoundCtrlContext;
static int SoundDeviceStop_l(SoundCtrlContext* sc);
static void SoundClearDircectOutSink_l(SoundCtrlContext* sc)
{
if(sc->directSink.pAudioTrackHandle != NULL)
{
sc->directSink.pAudioTrackHandle->stop();
sc->directSink.pAudioTrackHandle.clear();
sc->directSink.pAudioTrackHandle = NULL;
sc->directSink.flag = AUDIO_OUTPUT_FLAG_NONE;
sc->directSink.format = AUDIO_FORMAT_DEFAULT;
sc->directSink.chanmask = AUDIO_CHANNEL_NONE;
sc->directSink.samplerate = 0;
}
}
static void __SdDestroy(SoundCtrl* s)
{
SoundCtrlContext* sc;
logd("<SoundCtl>: destroy");
sc = (SoundCtrlContext*)s;
pthread_mutex_lock(&sc->mutex);
if(sc->eStatus != SOUND_STATUS_STOPPED)
SoundDeviceStop_l(sc);
SoundClearDircectOutSink_l(sc);
pthread_mutex_unlock(&sc->mutex);
pthread_mutex_destroy(&sc->mutex);
#if defined(CONF_SC_WRITE_ASYNC_MODE)
if(sc->pAudioSink != NULL)
{
pthread_mutex_destroy(&sc->cbMutex);
pthread_cond_destroy(&sc->cbCond);
}
#endif
#if SAVE_PCM
fclose(sc->testpcm);
sc->testpcm = NULL;
#endif
free(sc);
return;
}
static void __SdSetFormat(SoundCtrl* s, CdxPlaybkCfg* cfg)
{
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
pthread_mutex_lock(&sc->mutex);
if(sc->eStatus != SOUND_STATUS_STOPPED)
{
loge("Sound device not int stop status, can not set audio params.");
abort();
}
if(cfg == NULL)
{
loge("invalid operation in __SdSetFormat");
pthread_mutex_unlock(&sc->mutex);
return;
}
else
{
sc->nBitPerSample = cfg->nBitpersample;
sc->nSampleRate = cfg->nSamplerate;
sc->nChannelNum = cfg->nChannels;
sc->needDirectOutPut = cfg->nNeedDirect;
sc->data_type = cfg->nDataType;
sc->nRoutine = cfg->nRoutine;
pthread_mutex_unlock(&sc->mutex);
return;
}
}
#if defined(CONF_SC_WRITE_ASYNC_MODE)
size_t SoundCtrlContext::fillBuffer(void *data, size_t size)
{
size_t doneSize = 0;
if(bReachedEOS == 1)
{
return 0;
}
pthread_mutex_lock(&cbMutex);
pBufAudioSink = data;
nSizeAudioSink = size;
while(nSizeAudioSink > 0)
{
if(bReachedEOS == 1)
break;
pthread_cond_wait(&cbCond, &cbMutex);
}
doneSize = size - nSizeAudioSink;
if(doneSize == size)
{
pBufAudioSink = NULL;
}
pthread_mutex_unlock(&cbMutex);
return doneSize;
}
// static
size_t SoundAudioSinkCallback(
MediaPlayerBase::AudioSink * /* audioSink */,
void *buffer, size_t size, void *cookie,
MediaPlayerBase::AudioSink::cb_event_t event)
{
SoundCtrlContext* sc = (SoundCtrlContext *)cookie;
switch(event)
{
case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
return sc->fillBuffer(buffer, size);
case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END:
ALOGV("SoundAudioSinkCallback: stream end");
sc->bReachedEOS = 1;
//sc->notifyAudioEOS();
break;
case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN:
ALOGV("SoundAudioSinkCallback: Tear down event");
//sc->mObserver->postAudioTearDown();
break;
}
return 0;
}
#endif
static int __SdStart(SoundCtrl* s)
{
SoundCtrlContext* sc;
status_t err;
unsigned int nFramePos;
audio_output_flags_t flag = AUDIO_OUTPUT_FLAG_NONE;
audio_format_t format = AUDIO_FORMAT_PCM_16_BIT;
audio_channel_mask_t chanmask = AUDIO_CHANNEL_OUT_STEREO;
sc = (SoundCtrlContext*)s;
logd("<SoundCtl>: start");
pthread_mutex_lock(&sc->mutex);
if(sc->eStatus == SOUND_STATUS_STARTED)
{
logw("Sound device already started.");
pthread_mutex_unlock(&sc->mutex);
return -1;
}
if(sc->eStatus == SOUND_STATUS_STOPPED)
{
if(sc->needDirectOutPut)
{
flag = AUDIO_OUTPUT_FLAG_DIRECT;
switch(sc->data_type)
{
case AUDIO_RAW_DATA_AC3:
logd("cedarx passthrough data ac3");
format = AUDIO_FORMAT_AC3;
break;
case AUDIO_RAW_DATA_DTS:
logd("cedarx passthrough data dts");
format = AUDIO_FORMAT_DTS;
break;
case AUDIO_RAW_DATA_DOLBY_DIGITAL_PLUS:
logd("cedarx passthrough data ddp");
format = AUDIO_FORMAT_E_AC3;
break;
default:
break;
}
if(sc->data_type == AUDIO_RAW_DATA_PCM)
{
format = AUDIO_FORMAT_PCM_16_BIT;
switch(sc->nBitPerSample)
{
case 32:
format = AUDIO_FORMAT_PCM_32_BIT;
break;
default:
break;
}
switch(sc->nChannelNum)
{
case 6:
chanmask = AUDIO_CHANNEL_OUT_5POINT1;
break;
case 8:
chanmask = AUDIO_CHANNEL_OUT_7POINT1;
break;
default:
break;
}
}
}
if(sc->pAudioSink != NULL && !sc->needDirectOutPut)
{
if(!sc->pAudioSink->ready()){
logd("Cedarx use AudioSink open spr : %d, ch : %d, fmt : %p",
sc->nSampleRate, sc->nChannelNum, format);
#if defined(CONF_SC_WRITE_ASYNC_MODE)
err = sc->pAudioSink->open(sc->nSampleRate,
sc->nChannelNum,
CHANNEL_MASK_USE_CHANNEL_ORDER,
format,
DEFAULT_AUDIOSINK_BUFFERCOUNT*5,
&SoundAudioSinkCallback,
sc,
flag);
#else //sync mode
err = sc->pAudioSink->open(sc->nSampleRate,
sc->nChannelNum,
CHANNEL_MASK_USE_CHANNEL_ORDER,
format,
DEFAULT_AUDIOSINK_BUFFERCOUNT*5,
NULL, //* no callback mode.
NULL,
flag);
#endif
if(err != OK)
{
pthread_mutex_unlock(&sc->mutex);
return -1;
}
}
else{
logd("Audiosink has opened");
}
unsigned int tmp = 0;
sc->pAudioSink->getPosition(&tmp);
if(tmp != 0)
{
sc->pAudioSink->pause();
sc->pAudioSink->flush();
}
sc->nFrameSize = sc->pAudioSink->frameSize();
SoundClearDircectOutSink_l(sc);
}
else if(sc->needDirectOutPut)
{
do{
logd("CedarX request Direct output to transfer iec61937 stream(or muti ch pcm)");
if(sc->pAudioTrack == NULL)
{
if(sc->directSink.pAudioTrackHandle != NULL)
{
if(sc->directSink.chanmask == chanmask &&
sc->directSink.flag == flag &&
sc->directSink.format == format &&
sc->directSink.samplerate == sc->nSampleRate)
{
logd("Direct out put reuse audiotrack from recycle");
sc->pAudioTrack = sc->directSink.pAudioTrackHandle;
break;
}
else
{
SoundClearDircectOutSink_l(sc);
}
}
logd("Direct out put new a Audiotrack");
sc->pAudioTrack = new AudioTrack();
if(sc->pAudioTrack == NULL)
{
loge("create audio track fail.");
pthread_mutex_unlock(&sc->mutex);
return -1;
}
sc->pAudioTrack->set(AUDIO_STREAM_MUSIC,
sc->nSampleRate,
format,
chanmask,
DEFAULT_AUDIOSINK_BUFFERCOUNT*5,
flag);
if(sc->directSink.pAudioTrackHandle != NULL)
loge("damn it! maybe memory leak!!!");
sc->directSink.flag = flag;
sc->directSink.format = format;
sc->directSink.chanmask = chanmask;
sc->directSink.samplerate = sc->nSampleRate;
sc->directSink.pAudioTrackHandle = sc->pAudioTrack;
}
else
{
logd("Direct out put reuse audiotrack from self");
}
}
while(0);
if(sc->pAudioTrack->initCheck() != OK)
{
loge("audio track initCheck() return fail.");
sc->pAudioTrack.clear();
sc->pAudioTrack = NULL;
pthread_mutex_unlock(&sc->mutex);
return -1;
}
sc->nFrameSize = sc->pAudioTrack->frameSize();
logd("framesize infomation : nFrameSize : %d", sc->nFrameSize);
#if (CONF_ANDROID_MAJOR_VER > 5)
if (sc->nPlayRate.mSpeed != 1.0f)
sc->pAudioTrack->setPlaybackRate(sc->nPlayRate);
#endif
}
else
{
sc->pAudioTrack = new AudioTrack();
if(sc->pAudioTrack == NULL)
{
loge("create audio track fail.");
pthread_mutex_unlock(&sc->mutex);
return -1;
}
sc->pAudioTrack->set(AUDIO_STREAM_DEFAULT,
sc->nSampleRate,
format,
(sc->nChannelNum == 2) ? AUDIO_CHANNEL_OUT_STEREO :
AUDIO_CHANNEL_OUT_MONO,20*1024);
if(sc->pAudioTrack->initCheck() != OK)
{
loge("audio track initCheck() return fail.");
sc->pAudioTrack.clear();
sc->pAudioTrack = NULL;
pthread_mutex_unlock(&sc->mutex);
return -1;
}
sc->nFrameSize = sc->pAudioTrack->frameSize();
#if (CONF_ANDROID_MAJOR_VER > 5)
if (sc->nPlayRate.mSpeed != 1.0f)
sc->pAudioTrack->setPlaybackRate(sc->nPlayRate);
#endif
}
sc->nDataSizePlayed = 0;
sc->nFramePosOffset = 0;
sc->nLastFramePos = 0;
}
if(sc->pAudioSink != NULL && !sc->needDirectOutPut)
sc->pAudioSink->start();
else
sc->pAudioTrack->start();
sc->eStatus = SOUND_STATUS_STARTED;
pthread_mutex_unlock(&sc->mutex);
return 0;
}
static int __SdStop(SoundCtrl* s)
{
int ret;
SoundCtrlContext* sc;
logd("<SoundCtl>: stop");
sc = (SoundCtrlContext*)s;
pthread_mutex_lock(&sc->mutex);
ret = SoundDeviceStop_l(sc);
pthread_mutex_unlock(&sc->mutex);
return ret;
}
static int SoundDeviceStop_l(SoundCtrlContext* sc)
{
if(sc->eStatus == SOUND_STATUS_STOPPED)
{
logw("Sound device already stopped.");
return 0;
}
if(sc->pAudioSink != NULL && !sc->needDirectOutPut)
{
#if defined(CONF_SC_WRITE_ASYNC_MODE)
pthread_mutex_lock(&sc->cbMutex);
sc->bReachedEOS = 1;
pthread_cond_signal(&sc->cbCond);
pthread_mutex_unlock(&sc->cbMutex);
#endif
sc->pAudioSink->pause();
sc->pAudioSink->flush();
sc->pAudioSink->stop();
sc->pAudioSink->close();
}
else
{
if (sc->pAudioTrack.get() != NULL)
{
sc->pAudioTrack->pause();
sc->pAudioTrack->flush();
sc->pAudioTrack->stop();
sc->pAudioTrack.clear();
sc->pAudioTrack = NULL;
}
}
sc->nDataSizePlayed = 0;
sc->nFramePosOffset = 0;
sc->nLastFramePos = 0;
sc->nFrameSize = 0;
sc->eStatus = SOUND_STATUS_STOPPED;
return 0;
}
static int __SdPause(SoundCtrl* s)
{
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
logd("<SoundCtl>: pause");
pthread_mutex_lock(&sc->mutex);
if(sc->eStatus != SOUND_STATUS_STARTED)
{
logw("Invalid pause operation, sound device not in start status.");
pthread_mutex_unlock(&sc->mutex);
return -1;
}
if(sc->pAudioSink != NULL && !sc->needDirectOutPut)
sc->pAudioSink->pause();
else
{
if (sc->pAudioTrack.get() != NULL)
{
sc->pAudioTrack->pause();
}
}
sc->eStatus = SOUND_STATUS_PAUSED;
pthread_mutex_unlock(&sc->mutex);
return 0;
}
static int __SdWrite(SoundCtrl* s, void* pData, int nDataSize)
{
int nWritten = 0;
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
pthread_mutex_lock(&sc->mutex);
if(sc->eStatus == SOUND_STATUS_STOPPED || sc->eStatus == SOUND_STATUS_PAUSED)
{
pthread_mutex_unlock(&sc->mutex);
return 0;
}
#if SAVE_PCM
fwrite(pData, 1, nDataSize,sc->testpcm);
#endif
#if defined(CONF_SC_WRITE_ASYNC_MODE)
if(sc->pAudioSink != NULL && !sc->needDirectOutPut)
{
//memcpy to the buf from audioSink
pthread_mutex_lock(&sc->cbMutex);
if(sc->nSizeAudioSink > 0 && sc->pBufAudioSink != NULL)
{
nWritten = sc->nSizeAudioSink < (size_t)nDataSize ? sc->nSizeAudioSink : nDataSize;
memcpy(sc->pBufAudioSink, pData, nWritten);
sc->nSizeAudioSink -= (size_t)nWritten;
sc->pBufAudioSink = (char *)sc->pBufAudioSink + nWritten;
}
else
{
nWritten = -1;//buf is not ready
}
pthread_cond_signal(&sc->cbCond);
pthread_mutex_unlock(&sc->cbMutex);
}
#else
if(sc->pAudioSink != NULL && !sc->needDirectOutPut)
nWritten = sc->pAudioSink->write(pData, nDataSize);
#endif
else
{
if (sc->pAudioTrack.get() != NULL)
{
nWritten = sc->pAudioTrack->write(pData, nDataSize);
}
}
if(nWritten < 0)
nWritten = 0;
else
sc->nDataSizePlayed += nWritten;
pthread_mutex_unlock(&sc->mutex);
return nWritten;
}
static int SoundDeviceReset_l(SoundCtrlContext* sc)
{
if(sc->eStatus == SOUND_STATUS_STOPPED)
{
logd("Sound device already in Idle");
return -1;
}
if(sc->pAudioSink != NULL && !sc->needDirectOutPut)
{
#if defined(CONF_SC_WRITE_ASYNC_MODE)
pthread_mutex_lock(&sc->cbMutex);
pthread_cond_signal(&sc->cbCond);
pthread_mutex_unlock(&sc->cbMutex);
#endif
sc->pAudioSink->pause();
sc->pAudioSink->flush();
sc->pAudioSink->stop();
}
else
{
if (sc->pAudioTrack.get() != NULL)
{
sc->pAudioTrack->pause();
sc->pAudioTrack->flush();
sc->pAudioTrack->stop();
}
}
sc->nDataSizePlayed = 0;
sc->nFramePosOffset = 0;
sc->nLastFramePos = 0;
sc->eStatus = SOUND_STATUS_STOPPED;
return 0;
}
//* called at player seek operation.
static int __SdReset(SoundCtrl* s)
{
SoundCtrlContext* sc;
int ret = 0;
logd("<SoundCtl>: reset");
sc = (SoundCtrlContext*)s;
pthread_mutex_lock(&sc->mutex);
ret = SoundDeviceReset_l(sc);
pthread_mutex_unlock(&sc->mutex);
return ret;
}
static int __SdGetCachedTime(SoundCtrl* s)
{
unsigned int nFramePos;
int64_t nCachedFrames;
int64_t nCachedTimeUs;
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
pthread_mutex_lock(&sc->mutex);
if(sc->eStatus == SOUND_STATUS_STOPPED)
{
pthread_mutex_unlock(&sc->mutex);
return 0;
}
if(sc->pAudioSink != NULL && !sc->needDirectOutPut)
sc->pAudioSink->getPosition(&nFramePos);
else
{
if (sc->pAudioTrack.get() != NULL)
{
sc->pAudioTrack->getPosition(&nFramePos);
}
}
if(sc->nFrameSize == 0)
{
loge("nFrameSize == 0.");
abort();
}
if(nFramePos < sc->nLastFramePos)
sc->nFramePosOffset += 0x100000000;
nCachedFrames = sc->nDataSizePlayed/sc->nFrameSize - nFramePos - sc->nFramePosOffset;
nCachedTimeUs = nCachedFrames*1000000/sc->nSampleRate;
logv("nDataSizePlayed = %lld, nFrameSize = %d, nFramePos = %u, \
nLastFramePos = %u, nFramePosOffset = %lld",
sc->nDataSizePlayed, sc->nFrameSize, nFramePos, sc->nLastFramePos, sc->nFramePosOffset);
logv("nCachedFrames = %lld, nCachedTimeUs = %lld, nSampleRate = %d",
nCachedFrames, nCachedTimeUs, sc->nSampleRate);
sc->nLastFramePos = nFramePos;
pthread_mutex_unlock(&sc->mutex);
return (int)(nCachedTimeUs);
}
static int __SdGetFrameCount(SoundCtrl* s)
{
SoundCtrlContext* sc = (SoundCtrlContext*)s;
int framecount = 0;
if (sc->pAudioSink != NULL && !sc->needDirectOutPut)
{
framecount = sc->pAudioSink->frameCount();
}
else
{
if (sc->pAudioTrack.get() != NULL)
{
framecount = sc->pAudioTrack->frameCount();
}
}
logv("framecount = %d",framecount);
return framecount;
}
#if (CONF_ANDROID_MAJOR_VER > 5)
int _SetPlaybackRate(SoundCtrl* s,const XAudioPlaybackRate *rate)
{
SoundCtrlContext* sc = (SoundCtrlContext*)s;
int ret =0;
sc->nPlayRate.mSpeed = rate->mSpeed;
sc->nPlayRate.mPitch = rate->mPitch;
sc->nPlayRate.mStretchMode = (AudioTimestretchStretchMode)rate->mStretchMode;
sc->nPlayRate.mFallbackMode = (AudioTimestretchFallbackMode)rate->mFallbackMode;
ALOGD("rate.mSpeed %f",sc->nPlayRate.mSpeed);
ALOGD("rate.mPitch %f",sc->nPlayRate.mPitch);
ALOGD("rate.mFallbackMode %d",sc->nPlayRate.mFallbackMode);
ALOGD("rate.mStretchMode %d",sc->nPlayRate.mStretchMode);
if (sc->pAudioSink != NULL && !sc->needDirectOutPut)
{
ret = sc->pAudioSink->setPlaybackRate(sc->nPlayRate);
}
else
{
if (sc->pAudioTrack == NULL)
__SdStart(s);
ret = sc->pAudioTrack->setPlaybackRate(sc->nPlayRate);
}
return ret;
}
#else
int _SetPlaybackRate(SoundCtrl* s,const XAudioPlaybackRate *rate)
{
CDX_UNUSE(s);
CDX_UNUSE(rate);
return 0;
}
#endif
static SoundControlOpsT mSoundControlOps =
{
.destroy = __SdDestroy,
.setFormat = __SdSetFormat,
.start = __SdStart,
.stop = __SdStop,
.pause = __SdPause,
.write = __SdWrite,
.reset = __SdReset,
.getCachedTime = __SdGetCachedTime,
.getFrameCount = __SdGetFrameCount,
.setPlaybackRate = _SetPlaybackRate,
};
SoundCtrl* SoundDeviceCreate(void* pAudioSink)
{
SoundCtrlContext* s;
logd("<SoundDeviceCreate>: init");
s = (SoundCtrlContext*)malloc(sizeof(SoundCtrlContext));
if(s == NULL)
{
loge("malloc memory fail.");
return NULL;
}
memset(s, 0, sizeof(SoundCtrlContext));
s->base.ops = &mSoundControlOps;
s->pAudioSink = (MediaPlayerBase::AudioSink*)pAudioSink;
s->pAudioTrack = NULL;
#if (CONF_ANDROID_MAJOR_VER > 5)
s->nPlayRate.mSpeed = 1.0f;
#endif
s->eStatus = SOUND_STATUS_STOPPED;
#if SAVE_PCM
s->testpcm = fopen(binpath,"wb");
if(!s->testpcm)
{
loge("open file failed, %s", strerror(errno));
}
#endif
pthread_mutex_init(&s->mutex, NULL);
#if defined(CONF_SC_WRITE_ASYNC_MODE)
if(s->pAudioSink != NULL)
{
pthread_mutex_init(&s->cbMutex, NULL);
pthread_cond_init(&s->cbCond, NULL);
}
#endif
return (SoundCtrl*)&s->base;
}