| /* |
| * Copyright (C) 2009 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. |
| */ |
| |
| #include "include/HTTPStream.h" |
| |
| #include <stdlib.h> |
| |
| #include <media/stagefright/MediaBuffer.h> |
| #include <media/stagefright/MediaBufferGroup.h> |
| #include <media/stagefright/MediaDebug.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MetaData.h> |
| #include <media/stagefright/ShoutcastSource.h> |
| |
| namespace android { |
| |
| ShoutcastSource::ShoutcastSource(HTTPStream *http) |
| : mHttp(http), |
| mMetaDataOffset(0), |
| mBytesUntilMetaData(0), |
| mGroup(NULL), |
| mStarted(false) { |
| AString metaint; |
| if (mHttp->find_header_value("icy-metaint", &metaint)) { |
| char *end; |
| const char *start = metaint.c_str(); |
| mMetaDataOffset = strtol(start, &end, 10); |
| CHECK(end > start && *end == '\0'); |
| CHECK(mMetaDataOffset > 0); |
| |
| mBytesUntilMetaData = mMetaDataOffset; |
| } |
| } |
| |
| ShoutcastSource::~ShoutcastSource() { |
| if (mStarted) { |
| stop(); |
| } |
| |
| delete mHttp; |
| mHttp = NULL; |
| } |
| |
| status_t ShoutcastSource::start(MetaData *) { |
| CHECK(!mStarted); |
| |
| mGroup = new MediaBufferGroup; |
| mGroup->add_buffer(new MediaBuffer(4096)); // XXX |
| |
| mStarted = true; |
| |
| return OK; |
| } |
| |
| status_t ShoutcastSource::stop() { |
| CHECK(mStarted); |
| |
| delete mGroup; |
| mGroup = NULL; |
| |
| mStarted = false; |
| |
| return OK; |
| } |
| |
| sp<MetaData> ShoutcastSource::getFormat() { |
| sp<MetaData> meta = new MetaData; |
| meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); |
| meta->setInt32(kKeySampleRate, 44100); |
| meta->setInt32(kKeyChannelCount, 2); // XXX |
| |
| return meta; |
| } |
| |
| status_t ShoutcastSource::read( |
| MediaBuffer **out, const ReadOptions *options) { |
| CHECK(mStarted); |
| |
| *out = NULL; |
| |
| int64_t seekTimeUs; |
| ReadOptions::SeekMode mode; |
| if (options && options->getSeekTo(&seekTimeUs, &mode)) { |
| return ERROR_UNSUPPORTED; |
| } |
| |
| MediaBuffer *buffer; |
| status_t err = mGroup->acquire_buffer(&buffer); |
| if (err != OK) { |
| return err; |
| } |
| |
| *out = buffer; |
| |
| size_t num_bytes = buffer->size(); |
| if (mMetaDataOffset > 0 && num_bytes > mBytesUntilMetaData) { |
| num_bytes = mBytesUntilMetaData; |
| } |
| |
| ssize_t n = mHttp->receive(buffer->data(), num_bytes); |
| |
| if (n <= 0) { |
| return (status_t)n; |
| } |
| |
| buffer->set_range(0, n); |
| |
| mBytesUntilMetaData -= (size_t)n; |
| |
| if (mBytesUntilMetaData == 0) { |
| unsigned char num_16_byte_blocks = 0; |
| n = mHttp->receive((char *)&num_16_byte_blocks, 1); |
| CHECK_EQ(n, 1); |
| |
| char meta[255 * 16]; |
| size_t meta_size = num_16_byte_blocks * 16; |
| size_t meta_length = 0; |
| while (meta_length < meta_size) { |
| n = mHttp->receive(&meta[meta_length], meta_size - meta_length); |
| if (n <= 0) { |
| return (status_t)n; |
| } |
| |
| meta_length += (size_t) n; |
| } |
| |
| while (meta_length > 0 && meta[meta_length - 1] == '\0') { |
| --meta_length; |
| } |
| |
| if (meta_length > 0) { |
| // Technically we should probably attach this meta data to the |
| // next buffer. XXX |
| buffer->meta_data()->setData('shou', 'shou', meta, meta_length); |
| } |
| |
| mBytesUntilMetaData = mMetaDataOffset; |
| } |
| |
| return OK; |
| } |
| |
| } // namespace android |
| |