blob: 783f2d0382f897d56a0a24b2c8be0f34b40c7f4e [file] [log] [blame]
/*
* 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