summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/FMRadioSource.cpp
diff options
context:
space:
mode:
authorStefan Ekenberg <stefan.ekenberg@stericsson.com>2012-08-20 12:00:26 +0200
committerGerrit Code Review <gerrit@review.cyanogenmod.com>2013-01-30 00:16:57 -0800
commit69d8d46c5985ed76947f875ee3b7d32febfcf201 (patch)
tree8ccac17232e22005b25277ba9f9bb85a494e04cc /media/libstagefright/FMRadioSource.cpp
parent95dc7fae7a3d8ddbd020e588a33d9215e9105c50 (diff)
downloadframeworks_av-69d8d46c5985ed76947f875ee3b7d32febfcf201.zip
frameworks_av-69d8d46c5985ed76947f875ee3b7d32febfcf201.tar.gz
frameworks_av-69d8d46c5985ed76947f875ee3b7d32febfcf201.tar.bz2
FM Radio: Add support for FM Radio in Android
Creating interface and framework for using FM Radio RX and TX from different vendors. Change-Id: I1a71aed01bfffdddfabf1cdfbfa3707cb1ed016b Signed-off-by: Benn Porscke <benn.porscke@stericsson.com>
Diffstat (limited to 'media/libstagefright/FMRadioSource.cpp')
-rw-r--r--media/libstagefright/FMRadioSource.cpp201
1 files changed, 201 insertions, 0 deletions
diff --git a/media/libstagefright/FMRadioSource.cpp b/media/libstagefright/FMRadioSource.cpp
new file mode 100644
index 0000000..4229f23
--- /dev/null
+++ b/media/libstagefright/FMRadioSource.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Stefan Ekenberg (stefan.ekenberg@stericsson.com) for ST-Ericsson
+ */
+
+#define LOG_TAG "FMRadioSource"
+#include <utils/Log.h>
+
+#include <media/stagefright/FMRadioSource.h>
+#include <media/AudioSystem.h>
+#include <private/media/AudioTrackShared.h>
+#include <cutils/compiler.h>
+
+namespace android {
+
+static const int kSampleRate = 48000;
+static const audio_format_t kAudioFormat = AUDIO_FORMAT_PCM_16_BIT;
+static const audio_channel_mask_t kChannelMask = AUDIO_CHANNEL_IN_STEREO;
+static const int kBufferTimeoutMs = 3000;
+
+FMRadioSource::FMRadioSource()
+ : mInitCheck(NO_INIT),
+ mStarted(false),
+ mSessionId(AudioSystem::newAudioSessionId()) {
+
+ // get FM Radio RX input
+ audio_io_handle_t input = AudioSystem::getInput(AUDIO_SOURCE_FM_RADIO_RX,
+ kSampleRate,
+ kAudioFormat,
+ kChannelMask,
+ mSessionId);
+ if (input == 0) {
+ ALOGE("Could not get audio input for FM Radio source");
+ mInitCheck = UNKNOWN_ERROR;
+ return;
+ }
+
+ // get frame count
+ int frameCount = 0;
+ status_t status = AudioRecord::getMinFrameCount(&frameCount, kSampleRate,
+ kAudioFormat, popcount(kChannelMask));
+ if (status != NO_ERROR) {
+ mInitCheck = status;
+ return;
+ }
+
+ // create the IAudioRecord
+ status = openRecord(frameCount, input);
+ if (status != NO_ERROR) {
+ mInitCheck = status;
+ return;
+ }
+
+ AudioSystem::acquireAudioSessionId(mSessionId);
+
+ mInitCheck = OK;
+ return;
+}
+
+FMRadioSource::~FMRadioSource() {
+ AudioSystem::releaseAudioSessionId(mSessionId);
+}
+
+status_t FMRadioSource::initCheck() const {
+ return mInitCheck;
+}
+
+ssize_t FMRadioSource::readAt(off64_t offset, void *data, size_t size) {
+ Buffer audioBuffer;
+
+ if (!mStarted) {
+ status_t err = mAudioRecord->start(AudioSystem::SYNC_EVENT_NONE, 0);
+ if (err == OK) {
+ mStarted = true;
+ } else {
+ ALOGE("Failed to start audio source");
+ return 0;
+ }
+ }
+
+ // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
+ // while we are accessing the cblk
+ sp<IAudioRecord> audioRecord = mAudioRecord;
+ sp<IMemory> iMem = mCblkMemory;
+ audio_track_cblk_t* cblk = mCblk;
+
+ audioBuffer.frameCount = size / cblk->frameSize;
+
+ status_t err = obtainBuffer(&audioBuffer);
+ if (err != NO_ERROR) {
+ ALOGE("Error obtaining an audio buffer, giving up (err:%d).", err);
+ return 0;
+ }
+
+ memcpy(data, audioBuffer.data, audioBuffer.size);
+ mCblk->stepUser(audioBuffer.frameCount);
+
+ return audioBuffer.size;
+}
+
+status_t FMRadioSource::getSize(off64_t *size) {
+ *size = 0;
+ return OK;
+}
+
+// -------------------------------------------------------------------------
+
+status_t FMRadioSource::openRecord(int frameCount, audio_io_handle_t input)
+{
+ status_t status;
+ const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
+ if (audioFlinger == 0) {
+ return NO_INIT;
+ }
+
+ sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), input,
+ kSampleRate,
+ kAudioFormat,
+ kChannelMask,
+ frameCount,
+ IAudioFlinger::TRACK_DEFAULT,
+ gettid(),
+ &mSessionId,
+ &status);
+
+ if (record == 0) {
+ ALOGE("AudioFlinger could not create record track, status: %d", status);
+ return status;
+ }
+
+ sp<IMemory> cblk = record->getCblk();
+ if (cblk == 0) {
+ ALOGE("Could not get control block");
+ return NO_INIT;
+ }
+ mAudioRecord = record;
+ mCblkMemory = cblk;
+ mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
+ mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
+ android_atomic_and(~CBLK_DIRECTION_MSK, &mCblk->flags);
+ return NO_ERROR;
+}
+
+status_t FMRadioSource::obtainBuffer(Buffer* audioBuffer)
+{
+ status_t result = NO_ERROR;
+ uint32_t framesReq = audioBuffer->frameCount;
+
+ audioBuffer->frameCount = 0;
+ audioBuffer->size = 0;
+
+ mCblk->lock.lock();
+ uint32_t framesReady = mCblk->framesReady();
+ if (framesReady == 0) {
+ do {
+ result = mCblk->cv.waitRelative(mCblk->lock, milliseconds(kBufferTimeoutMs));
+ if (CC_UNLIKELY(result != NO_ERROR)) {
+ ALOGE("obtainBuffer timed out (is the CPU pegged?) "
+ "user=%08x, server=%08x", mCblk->user, mCblk->server);
+ mCblk->lock.unlock();
+ return TIMED_OUT;
+ }
+
+ framesReady = mCblk->framesReady();
+ } while (framesReady == 0);
+ }
+ mCblk->lock.unlock();
+
+ if (framesReq > framesReady) {
+ framesReq = framesReady;
+ }
+
+ uint32_t u = mCblk->user;
+ uint32_t bufferEnd = mCblk->userBase + mCblk->frameCount;
+
+ if (framesReq > bufferEnd - u) {
+ framesReq = bufferEnd - u;
+ }
+
+ audioBuffer->frameCount = framesReq;
+ audioBuffer->size = framesReq * mCblk->frameSize;
+ audioBuffer->data = (int8_t*)mCblk->buffer(u);
+
+ return NO_ERROR;
+}
+
+} // namespace android