libmix: port WMV related patch
BZ:47670
original patch:
51112:libstagefright: Fix an issue that thumbnail for WMV file can not be generated
42039: libstagefright: Ignore seek in case of no index object within asf file
30078:libstagefright: Porting patches from R2 to R3
Signed-off-by: ywan171 <yi.a.wang@intel.com>
Change-Id: I13ccf6d9c4194bf74dc6f708a9a5a18f83e8d4bd
Reviewed-on: http://android.intel.com:8080/58297
Reviewed-by: Feng, Wei <wei.feng@intel.com>
Reviewed-by: Zhang, Xiaolin <xiaolin.zhang@intel.com>
Tested-by: Zhang, Xiaolin <xiaolin.zhang@intel.com>
diff --git a/Android.mk b/Android.mk
index 0ed1c8a..b73ddce 100644
--- a/Android.mk
+++ b/Android.mk
@@ -12,5 +12,5 @@
include $(VENDORS_INTEL_MRST_LIBMIX_ROOT)/asfparser/Android.mk
include $(VENDORS_INTEL_MRST_LIBMIX_ROOT)/videodecoder/Android.mk
include $(VENDORS_INTEL_MRST_LIBMIX_ROOT)/videoencoder/Android.mk
-
+include $(VENDORS_INTEL_MRST_LIBMIX_ROOT)/frameworks/asf_extractor/Android.mk
endif
diff --git a/frameworks/asf_extractor/Android.mk b/frameworks/asf_extractor/Android.mk
new file mode 100644
index 0000000..78afb17
--- /dev/null
+++ b/frameworks/asf_extractor/Android.mk
@@ -0,0 +1,31 @@
+ifeq ($(strip $(USE_INTEL_ASF_EXTRACTOR)),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+
+LOCAL_SRC_FILES := \
+ AsfExtractor.cpp \
+ MediaBufferPool.cpp
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH) \
+ $(TARGET_OUT_HEADERS)/libmix_asfparser \
+ $(TOP)/frameworks/av/media/libstagefright/include \
+ $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_COPY_HEADERS_TO := libmix_asf_extractor
+
+LOCAL_COPY_HEADERS := \
+ AsfExtractor.h \
+ MetaDataExt.h \
+ MediaBufferPool.h
+
+LOCAL_CPPFLAGS += -DUSE_INTEL_ASF_EXTRACTOR
+LOCAL_MODULE := libasfextractor
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+endif
diff --git a/frameworks/asf_extractor/AsfExtractor.cpp b/frameworks/asf_extractor/AsfExtractor.cpp
new file mode 100644
index 0000000..9a10581
--- /dev/null
+++ b/frameworks/asf_extractor/AsfExtractor.cpp
@@ -0,0 +1,718 @@
+/*
+* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
+*
+* 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.
+*/
+
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AsfExtractor"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+#include "MetaDataExt.h"
+#include "MediaBufferPool.h"
+#include "AsfStreamParser.h"
+#include "AsfExtractor.h"
+
+
+namespace android {
+
+class ASFSource : public MediaSource {
+public:
+ ASFSource(const sp<AsfExtractor> &extractor, int trackIndex)
+ : mExtractor(extractor),
+ mTrackIndex(trackIndex) {
+ }
+
+ virtual status_t start(MetaData *params = NULL) {
+ return OK;
+ }
+
+ virtual status_t stop() {
+ return OK;
+ }
+
+ virtual sp<MetaData> getFormat() {
+ return mExtractor->getTrackMetaData(mTrackIndex, 0);
+ }
+
+ virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL) {
+ return mExtractor->read(mTrackIndex, buffer, options);
+ }
+
+protected:
+ virtual ~ASFSource() {
+ mExtractor = NULL;
+ }
+
+private:
+ sp<AsfExtractor> mExtractor;
+ int mTrackIndex;
+
+ ASFSource(const ASFSource &);
+ ASFSource &operator=(const ASFSource &);
+};
+
+
+AsfExtractor::AsfExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mInitialized(false),
+ mHasIndexObject(false),
+ mFirstTrack(NULL),
+ mLastTrack(NULL),
+ mReadLock(),
+ mFileMetaData(new MetaData),
+ mParser(NULL),
+ mHeaderObjectSize(0),
+ mDataObjectSize(0),
+ mDataPacketBeginOffset(0),
+ mDataPacketEndOffset(0),
+ mDataPacketCurrentOffset(0),
+ mDataPacketSize(0),
+ mDataPacketData(NULL) {
+ mParser = new AsfStreamParser;
+}
+
+AsfExtractor::~AsfExtractor() {
+ uninitialize();
+ mDataSource = NULL;
+ mFileMetaData = NULL;
+ delete mParser;
+ mParser = NULL;
+}
+
+sp<MetaData> AsfExtractor::getMetaData() {
+ status_t err = initialize();
+ if (err != OK) {
+ return new MetaData;
+ }
+
+ return mFileMetaData;
+}
+
+size_t AsfExtractor::countTracks() {
+ status_t err = initialize();
+ if (err != OK) {
+ return 0;
+ }
+
+ size_t n = 0;
+ Track *track = mFirstTrack;
+ while (track) {
+ ++n;
+ track = track->next;
+ }
+
+ ALOGV("track count is %d", n);
+ return n;
+}
+
+sp<MetaData> AsfExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+ status_t err = initialize();
+ if (err != OK) {
+ return NULL;
+ }
+
+ Track *track = getTrackByTrackIndex(index);
+ if (track == NULL) {
+ return NULL;
+ }
+
+ // There is no thumbnail data so ignore flags: kIncludeExtensiveMetaData
+ return track->meta;
+}
+
+sp<MediaSource> AsfExtractor::getTrack(size_t index) {
+ status_t err;
+ if ((err = initialize()) != OK) {
+ return NULL;
+ }
+
+ Track *track = getTrackByTrackIndex(index);
+ if (track == NULL) {
+ return NULL;
+ }
+
+ // Assume this track is active
+ track->skipTrack = false;
+ return new ASFSource(this, index);
+}
+
+status_t AsfExtractor::read(
+ int trackIndex,
+ MediaBuffer **buffer,
+ const MediaSource::ReadOptions *options) {
+ Track *track = getTrackByTrackIndex(trackIndex);
+ if (track == NULL) {
+ return BAD_VALUE;
+ }
+
+ if (!mParser->hasVideo() || (mParser->hasVideo() && mHasIndexObject)) {
+ if (options != NULL) {
+ status_t err = seek_l(track, options);
+ if (err != OK) {
+ return err;
+ }
+ }
+ } else {
+ ALOGW("No index object. Seek may not be supported!!!");
+ }
+
+ return read_l(track, buffer);
+}
+
+status_t AsfExtractor::initialize() {
+ if (mInitialized) {
+ return OK;
+ }
+
+ status_t status = OK;
+ // header object is the first mandatory object. The first 16 bytes
+ // is GUID of object, the following 8 bytes is size of object
+ if (mDataSource->readAt(16, &mHeaderObjectSize, 8) != 8) {
+ return ERROR_IO;
+ }
+
+ uint8_t* headerObjectData = new uint8_t [mHeaderObjectSize];
+ if (headerObjectData == NULL) {
+ return NO_MEMORY;
+ }
+
+ if (mDataSource->readAt(0, headerObjectData, mHeaderObjectSize) != mHeaderObjectSize) {
+ return ERROR_IO;
+ }
+ status = mParser->parseHeaderObject(headerObjectData, mHeaderObjectSize);
+ if (status != ASF_PARSER_SUCCESS) {
+ ALOGE("Failed to parse header object.");
+ return ERROR_MALFORMED;
+ }
+
+ delete [] headerObjectData;
+ headerObjectData = NULL;
+
+ uint8_t dataObjectHeaderData[ASF_DATA_OBJECT_HEADER_SIZE];
+ if (mDataSource->readAt(mHeaderObjectSize, dataObjectHeaderData, ASF_DATA_OBJECT_HEADER_SIZE)
+ != ASF_DATA_OBJECT_HEADER_SIZE) {
+ return ERROR_IO;
+ }
+ status = mParser->parseDataObjectHeader(dataObjectHeaderData, ASF_DATA_OBJECT_HEADER_SIZE);
+ if (status != ASF_PARSER_SUCCESS) {
+ ALOGE("Failed to parse data object header.");
+ return ERROR_MALFORMED;
+ }
+
+ // first 16 bytes is GUID of data object
+ mDataObjectSize = *(uint64_t*)(dataObjectHeaderData + 16);
+ mDataPacketBeginOffset = mHeaderObjectSize + ASF_DATA_OBJECT_HEADER_SIZE;
+ mDataPacketEndOffset = mHeaderObjectSize + mDataObjectSize;
+ mDataPacketCurrentOffset = mDataPacketBeginOffset;
+
+ // allocate memory for data packet
+ mDataPacketSize = mParser->getDataPacketSize();
+ mDataPacketData = new uint8_t [mDataPacketSize];
+ if (mDataPacketData == NULL) {
+ return NO_MEMORY;
+ }
+
+ const AsfFileMediaInfo *fileMediaInfo = mParser->getFileInfo();
+ if (fileMediaInfo && fileMediaInfo->seekable) {
+ uint64_t offset = mDataPacketEndOffset;
+
+ // Find simple index object for time seeking.
+ // object header include 16 bytes of object GUID and 8 bytes of object size.
+ uint8_t objectHeader[24];
+ int64_t objectSize;
+ for (;;) {
+ if (mDataSource->readAt(offset, objectHeader, 24) != 24) {
+ break;
+ }
+
+ objectSize = *(int64_t *)(objectHeader + 16);
+ if (!AsfStreamParser::isSimpleIndexObject(objectHeader)) {
+ offset += objectSize;
+ continue;
+ }
+ mHasIndexObject = true;
+ uint8_t* indexObjectData = new uint8_t [objectSize];
+ if (indexObjectData == NULL) {
+ // don't report as error, we just lose time seeking capability.
+ break;
+ }
+ if (mDataSource->readAt(offset, indexObjectData, objectSize) == objectSize) {
+ // Ignore return value
+ mParser->parseSimpleIndexObject(indexObjectData, objectSize);
+ }
+ delete [] indexObjectData;
+ break;
+ }
+ }
+
+ if (mParser->hasVideo() || mParser->hasAudio()) {
+ ALOGV("MEDIA_MIMETYPE_CONTAINER_ASF");
+ mFileMetaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_ASF);
+ } else {
+ ALOGE("Content does not have neither audio nor video.");
+ return ERROR_UNSUPPORTED;
+ }
+
+ // duration returned from parser is in 100-nanosecond unit, converted it to microseconds (us)
+ ALOGV("Duration is %.2f (sec)", mParser->getDuration()/1E7);
+ mFileMetaData->setInt64(kKeyDuration, mParser->getDuration() / SCALE_100_NANOSEC_TO_USEC);
+
+ setupTracks();
+ mInitialized = true;
+ return OK;
+}
+
+void AsfExtractor::uninitialize() {
+ if (mDataPacketData) {
+ delete [] mDataPacketData;
+ mDataPacketData = NULL;
+ }
+ mDataPacketSize = 0;
+
+ Track* track = mFirstTrack;
+ MediaBuffer* p;
+ while (track != NULL) {
+ track->meta = NULL;
+ if (track->bufferActive) {
+ track->bufferActive->release();
+ track->bufferActive = NULL;
+ }
+
+ int size = track->bufferQueue.size();
+ for (int i = 0; i < size; i++) {
+ p = track->bufferQueue.editItemAt(i);
+ p->release();
+ }
+
+ track->bufferQueue.clear();
+ delete track->bufferPool;
+
+ track->meta = NULL;
+ mFirstTrack = track->next;
+ delete track;
+ track = mFirstTrack;
+ }
+ mFirstTrack = NULL;
+ mLastTrack = NULL;
+}
+
+static const char* FourCC2MIME(uint32_t fourcc) {
+ // The first charater of FOURCC characters appears in the least-significant byte
+ // WVC1 => 0x31435657
+ switch (fourcc) {
+ //case FOURCC('W', 'M', 'V', '1'):
+ //case FOURCC('W', 'M', 'V', '2'):
+ //case FOURCC('W', 'M', 'V', 'A'):
+ case FOURCC('1', 'V', 'M', 'W'):
+ ALOGW("WMV1 format is not supported.");
+ return "video/wmv1";
+ case FOURCC('2', 'V', 'M', 'W'):
+ ALOGW("WMV2 format is not supported.");
+ return "video/wmv2";
+ case FOURCC('A', 'V', 'M', 'W'):
+ ALOGW("WMV Advanced profile, assuming as WVC1 for now");
+ return MEDIA_MIMETYPE_VIDEO_WMV;
+ //case FOURCC('W', 'M', 'V', '3'):
+ //case FOURCC('W', 'V', 'C', '1'):
+ case FOURCC('3', 'V', 'M', 'W'):
+ case FOURCC('1', 'C', 'V', 'W'):
+ return MEDIA_MIMETYPE_VIDEO_WMV;
+ default:
+ ALOGE("Unknown video format.");
+ return "video/unknown-type";
+ }
+}
+
+static const char* CodecID2MIME(uint32_t codecID) {
+ switch (codecID) {
+ // WMA version 1
+ case 0x0160:
+ // WMA version 2 (7, 8, 9 series)
+ case 0x0161:
+ // WMA 9/10 profressional (WMA version 3)
+ case 0x0162:
+ return MEDIA_MIMETYPE_AUDIO_WMA;
+ // WMA 9 lossless
+ case 0x0163:
+ //return MEDIA_MIMETYPE_AUDIO_WMA_LOSSLESS;
+ return MEDIA_MIMETYPE_AUDIO_WMA;
+ // WMA voice 9
+ case 0x000A:
+ // WMA voice 10
+ case 0x000B:
+ ALOGW("WMA voice 9/10 is not supported.");
+ return "audio/wma-voice";
+ default:
+ ALOGE("Unsupported Audio codec ID: %#x", codecID);
+ return "audio/unknown-type";
+ }
+}
+
+
+status_t AsfExtractor::setupTracks() {
+ ALOGW("Audio is temporarily disabled!!!!!!!!!!!!!!");
+ AsfAudioStreamInfo* audioInfo = NULL;//mParser->getAudioInfo();
+ AsfVideoStreamInfo* videoInfo = mParser->getVideoInfo();
+ Track* track;
+ while (audioInfo || videoInfo) {
+ track = new Track;
+ if (mLastTrack == NULL) {
+ mFirstTrack = track;
+ mLastTrack = track;
+ } else {
+ mLastTrack->next = track;
+ mLastTrack = track;
+ }
+
+ // this flag will be set to false within getTrack
+ track->skipTrack = true;
+ track->seekCompleted = false;
+ track->next = NULL;
+ track->meta = new MetaData;
+ track->bufferActive = NULL;
+ track->bufferPool = new MediaBufferPool;
+
+ if (audioInfo) {
+ track->streamNumber = audioInfo->streamNumber;
+ track->encrypted = audioInfo->encryptedContentFlag;
+ track->meta->setInt32(kKeyChannelCount, audioInfo->numChannels);
+ track->meta->setInt32(kKeySampleRate, audioInfo->sampleRate);
+
+ if (audioInfo->codecDataSize) {
+ track->meta->setData(
+ kKeyConfigData,
+ kTypeConfigData,
+ audioInfo->codecData,
+ audioInfo->codecDataSize);
+ }
+ // duration returned is in 100-nanosecond unit
+ track->meta->setInt64(kKeyDuration, mParser->getDuration() / SCALE_100_NANOSEC_TO_USEC);
+ track->meta->setCString(kKeyMIMEType, CodecID2MIME(audioInfo->codecID));
+ track->meta->setInt32(kKeySuggestedBufferSize, mParser->getDataPacketSize());
+ audioInfo = audioInfo->next;
+ } else {
+ track->streamNumber = videoInfo->streamNumber;
+ track->encrypted = videoInfo->encryptedContentFlag;
+ track->meta->setInt32(kKeyWidth, videoInfo->width);
+ track->meta->setInt32(kKeyHeight, videoInfo->height);
+ if (videoInfo->codecDataSize) {
+ track->meta->setData(
+ kKeyConfigData,
+ kTypeConfigData,
+ videoInfo->codecData,
+ videoInfo->codecDataSize);
+ }
+ // duration returned is in 100-nanosecond unit
+ track->meta->setInt64(kKeyDuration, mParser->getDuration() / SCALE_100_NANOSEC_TO_USEC);
+ track->meta->setCString(kKeyMIMEType, FourCC2MIME(videoInfo->fourCC));
+ int maxSize = mParser->getMaxObjectSize();
+ if (maxSize == 0) {
+ // estimated maximum packet size.
+ maxSize = 10 * mParser->getDataPacketSize();
+ }
+ track->meta->setInt32(kKeySuggestedBufferSize, maxSize);
+ if (mHasIndexObject) {
+ // set arbitary thumbnail time
+ track->meta->setInt64(kKeyThumbnailTime, mParser->getDuration() / (SCALE_100_NANOSEC_TO_USEC * 2));
+ } else {
+ track->meta->setInt64(kKeyThumbnailTime, 0);
+ }
+ videoInfo = videoInfo->next;
+ }
+ }
+
+ return OK;
+}
+
+status_t AsfExtractor::seek_l(Track* track, const MediaSource::ReadOptions *options) {
+ Mutex::Autolock lockSeek(mReadLock);
+
+ // It is expected seeking will happen on all the tracks with the same seeking options.
+ // Only the first track receiving the seeking command will perform seeking and all other
+ // tracks just siliently ignore it.
+
+ // TODO: potential problems in the following case:
+ // audio seek
+ // video read
+ // video seek
+ // video read
+
+ if (track->seekCompleted) {
+ // seeking is completed through a different track
+ track->seekCompleted = false;
+ return OK;
+ }
+ int64_t seekTimeUs;
+ MediaSource::ReadOptions::SeekMode mode;
+ if(!options->getSeekTo(&seekTimeUs,&mode)) {
+ return OK;
+ }
+
+ uint64_t targetSampleTimeUs = 0;
+
+ // seek to next sync sample or previous sync sample
+ bool nextSync = false;
+ switch (mode) {
+ case MediaSource::ReadOptions::SEEK_NEXT_SYNC:
+ nextSync = true;
+ break;
+ // Always seek to the closest previous sync frame
+ case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC:
+ case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC:
+
+ // Not supported, already seek to sync frame, so will not set kKeyTargetTime on bufferActive.
+ case MediaSource::ReadOptions::SEEK_CLOSEST:
+ default:
+ break;
+ }
+
+ uint32_t packetNumber;
+ uint64_t targetTime;
+ // parser takes seek time in 100-nanosecond unit and returns target time in 100-nanosecond as well.
+ if (!mParser->seek(seekTimeUs * SCALE_100_NANOSEC_TO_USEC, nextSync, packetNumber, targetTime)) {
+ ALOGV("Seeking failed.");
+ return ERROR_END_OF_STREAM;
+ }
+ ALOGV("seek time = %.2f secs, actual time = %.2f secs", seekTimeUs/1E6, targetTime / 1E7);
+
+ // convert to microseconds
+ targetSampleTimeUs = targetTime / SCALE_100_NANOSEC_TO_USEC;
+ mDataPacketCurrentOffset = mDataPacketBeginOffset + packetNumber * mDataPacketSize;
+ ALOGV("data packet offset = %lld", mDataPacketCurrentOffset);
+
+ // flush all pending buffers on all the tracks
+ Track* temp = mFirstTrack;
+ while (temp != NULL) {
+ Mutex::Autolock lockTrack(temp->lock);
+ if (temp->bufferActive) {
+ temp->bufferActive->release();
+ temp->bufferActive = NULL;
+ }
+
+ int size = temp->bufferQueue.size();
+ for (int i = 0; i < size; i++) {
+ MediaBuffer* buffer = temp->bufferQueue.editItemAt(i);
+ buffer->release();
+ }
+ temp->bufferQueue.clear();
+
+ if (temp != track) {
+ // notify all other tracks seeking is completed.
+ // this flag is reset when seeking request is made on each track.
+ // don't set this flag on the driving track so a new seek can be made.
+ temp->seekCompleted = true;
+ }
+ temp = temp->next;
+ }
+
+ return OK;
+}
+
+status_t AsfExtractor::read_l(Track *track, MediaBuffer **buffer) {
+ status_t err = OK;
+ while (err == OK) {
+ Mutex::Autolock lock(track->lock);
+ if (track->bufferQueue.size() != 0) {
+ *buffer = track->bufferQueue[0];
+ track->bufferQueue.removeAt(0);
+ return OK;
+ }
+ track->lock.unlock();
+
+ err = readPacket();
+ }
+ ALOGE("read_l failed.");
+ return err;
+}
+
+status_t AsfExtractor::readPacket() {
+ Mutex::Autolock lock(mReadLock);
+ if (mDataPacketCurrentOffset + mDataPacketSize > mDataPacketEndOffset) {
+ ALOGI("readPacket hits end of stream.");
+ return ERROR_END_OF_STREAM;
+ }
+
+ if (mDataSource->readAt(mDataPacketCurrentOffset, mDataPacketData, mDataPacketSize) !=
+ mDataPacketSize) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ // update next read position
+ mDataPacketCurrentOffset += mDataPacketSize;
+ AsfPayloadDataInfo *payloads = NULL;
+ int status = mParser->parseDataPacket(mDataPacketData, mDataPacketSize, &payloads);
+ if (status != ASF_PARSER_SUCCESS || payloads == NULL) {
+ ALOGE("Failed to parse data packet. status = %d", status);
+ return ERROR_END_OF_STREAM;
+ }
+
+ AsfPayloadDataInfo* payload = payloads;
+ while (payload) {
+ Track* track = getTrackByStreamNumber(payload->streamNumber);
+ if (track == NULL || track->skipTrack) {
+ payload = payload->next;
+ continue;
+ }
+ if (payload->mediaObjectLength == payload->payloadSize ||
+ payload->offsetIntoMediaObject == 0) {
+ // a comple object or the first payload of fragmented object
+ MediaBuffer *buffer = NULL;
+ status = track->bufferPool->acquire_buffer(
+ payload->mediaObjectLength, &buffer);
+ if (status != OK) {
+ ALOGE("Failed to acquire buffer.");
+ mParser->releasePayloadDataInfo(payloads);
+ return status;
+ }
+ memcpy(buffer->data(),
+ payload->payloadData,
+ payload->payloadSize);
+
+ buffer->set_range(0, payload->mediaObjectLength);
+ // kKeyTime is in microsecond unit (usecs)
+ // presentationTime is in mililsecond unit (ms)
+ buffer->meta_data()->setInt64(kKeyTime, payload->presentationTime * 1000);
+
+ if (payload->keyframe) {
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ }
+
+ if (payload->mediaObjectLength == payload->payloadSize) {
+ Mutex::Autolock lockTrack(track->lock);
+ // a complete object
+ track->bufferQueue.push(buffer);
+ } else {
+ // the first payload of a fragmented object
+ track->bufferActive = buffer;
+ if (track->encrypted) {
+ Mutex::Autolock lockTrack(track->lock);
+ MediaBuffer* copy = NULL;
+ track->bufferPool->acquire_buffer(payload->payloadSize, ©);
+ copy->meta_data()->setInt64(kKeyTime, payload->presentationTime * 1000);
+ memcpy(copy->data(), payload->payloadData, payload->payloadSize);
+ copy->set_range(0, payload->payloadSize);
+ track->bufferQueue.push(copy);
+ }
+ }
+ } else {
+ if (track->bufferActive == NULL) {
+ ALOGE("Receiving corrupt or discontinuous data packet.");
+ payload = payload->next;
+ continue;
+ }
+ // TODO: check object number and buffer size!!!!!!!!!!!!!!
+ // the last payload or the middle payload of a fragmented object
+ memcpy(
+ (uint8_t*)track->bufferActive->data() + payload->offsetIntoMediaObject,
+ payload->payloadData,
+ payload->payloadSize);
+
+ if (payload->offsetIntoMediaObject + payload->payloadSize ==
+ payload->mediaObjectLength) {
+ // the last payload of a fragmented object
+ // for encrypted content, push a cloned media buffer to vector instead.
+ if (!track->encrypted)
+ {
+ Mutex::Autolock lockTrack(track->lock);
+ track->bufferQueue.push(track->bufferActive);
+ track->bufferActive = NULL;
+ } else {
+ Mutex::Autolock lockTrack(track->lock);
+ track->bufferActive->set_range(payload->offsetIntoMediaObject, payload->payloadSize);
+ track->bufferQueue.push(track->bufferActive);
+ track->bufferActive = NULL;
+ }
+ } else {
+ // middle payload of a fragmented object
+ if (track->encrypted) {
+ Mutex::Autolock lockTrack(track->lock);
+ MediaBuffer* copy = NULL;
+ int64_t keytime;
+ track->bufferPool->acquire_buffer(payload->payloadSize, ©);
+ track->bufferActive->meta_data()->findInt64(kKeyTime, &keytime);
+ copy->meta_data()->setInt64(kKeyTime, keytime);
+ memcpy(copy->data(), payload->payloadData, payload->payloadSize);
+ copy->set_range(0, payload->payloadSize);
+ track->bufferQueue.push(copy);
+ }
+ }
+ }
+ payload = payload->next;
+ };
+
+ mParser->releasePayloadDataInfo(payloads);
+ return OK;
+}
+
+AsfExtractor::Track* AsfExtractor::getTrackByTrackIndex(int index) {
+ Track *track = mFirstTrack;
+ while (index > 0) {
+ if (track == NULL) {
+ return NULL;
+ }
+
+ track = track->next;
+ --index;
+ }
+ return track;
+}
+
+AsfExtractor::Track* AsfExtractor::getTrackByStreamNumber(int stream) {
+ Track *track = mFirstTrack;
+ while (track != NULL) {
+ if (track->streamNumber == stream) {
+ return track;
+ }
+ track = track->next;
+ }
+ return NULL;
+}
+
+bool SniffAsf(
+ const sp<DataSource> &source,
+ String8 *mimeType,
+ float *confidence,
+ sp<AMessage> *) {
+ uint8_t guid[16];
+ if (source->readAt(0, guid, 16) != 16) {
+ return false;
+ }
+ if (!AsfStreamParser::isHeaderObject(guid)) {
+ return false;
+ }
+
+ *mimeType = MEDIA_MIMETYPE_CONTAINER_ASF;
+ *confidence = 0.4f;
+ return true;
+}
+
+} // namespace android
+
diff --git a/frameworks/asf_extractor/AsfExtractor.h b/frameworks/asf_extractor/AsfExtractor.h
new file mode 100644
index 0000000..adb8e9d
--- /dev/null
+++ b/frameworks/asf_extractor/AsfExtractor.h
@@ -0,0 +1,122 @@
+/*
+* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
+*
+* 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.
+*/
+
+
+#ifndef ASF_EXTRACTOR_H_
+#define ASF_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/Vector.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+
+
+namespace android {
+
+struct AMessage;
+class DataSource;
+//class String8;
+
+
+class AsfExtractor : public MediaExtractor {
+public:
+ // Extractor assumes ownership of "source".
+ AsfExtractor(const sp<DataSource> &source);
+ virtual ~AsfExtractor();
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual sp<MetaData> getMetaData();
+
+private:
+ status_t read(
+ int trackIndex,
+ MediaBuffer **buffer,
+ const MediaSource::ReadOptions *options = NULL);
+
+ friend class ASFSource;
+
+private:
+ struct Track {
+ Track *next;
+ sp<MetaData> meta;
+ bool skipTrack;
+ bool seekCompleted;
+ bool encrypted;
+ uint8_t streamNumber;
+
+ // outgoing buffer queue (ready for decoding)
+ Vector<MediaBuffer*> bufferQueue;
+
+ // buffer pool
+ class MediaBufferPool *bufferPool;
+
+ // buffer currently being used to read payload data
+ MediaBuffer *bufferActive;
+ Mutex lock;
+ };
+
+ sp<DataSource> mDataSource;
+ bool mInitialized;
+ bool mHasIndexObject;
+ Track *mFirstTrack;
+ Track *mLastTrack;
+
+ Mutex mReadLock;
+ sp<MetaData> mFileMetaData;
+ class AsfStreamParser *mParser;
+
+ int64_t mHeaderObjectSize;
+ int64_t mDataObjectSize;
+
+ int64_t mDataPacketBeginOffset;
+ int64_t mDataPacketEndOffset;
+ int64_t mDataPacketCurrentOffset;
+
+ int64_t mDataPacketSize;
+ uint8_t *mDataPacketData;
+
+ enum {
+ // 100 nano seconds to micro second
+ SCALE_100_NANOSEC_TO_USEC = 10,
+ };
+
+ AsfExtractor(const AsfExtractor &);
+ AsfExtractor &operator=(const AsfExtractor &);
+
+private:
+ struct Track;
+ status_t initialize();
+ void uninitialize();
+ status_t setupTracks();
+ inline Track* getTrackByTrackIndex(int index);
+ inline Track* getTrackByStreamNumber(int stream);
+ status_t seek_l(Track* track, const MediaSource::ReadOptions *options);
+ status_t read_l(Track *track, MediaBuffer **buffer);
+ status_t readPacket();
+};
+
+
+bool SniffAsf(
+ const sp<DataSource> &source,
+ String8 *mimeType,
+ float *confidence,
+ sp<AMessage> *);
+
+} // namespace android
+
+#endif // ASF_EXTRACTOR_H_
diff --git a/frameworks/asf_extractor/MediaBufferPool.cpp b/frameworks/asf_extractor/MediaBufferPool.cpp
new file mode 100644
index 0000000..22b25e0
--- /dev/null
+++ b/frameworks/asf_extractor/MediaBufferPool.cpp
@@ -0,0 +1,105 @@
+/*
+* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
+*
+* 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.
+*/
+
+
+#define LOG_TAG "MediaBufferPool"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include "MediaBufferPool.h"
+
+#define DEFAULT_PAGE_SIZE 4096
+
+namespace android {
+
+MediaBufferPool::MediaBufferPool()
+ : mMaxBufferSize(0),
+ mFirstBuffer(NULL),
+ mLastBuffer(NULL) {
+}
+
+MediaBufferPool::~MediaBufferPool() {
+ MediaBuffer *next;
+ for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
+ buffer = next) {
+ next = buffer->nextBuffer();
+
+ CHECK_EQ(buffer->refcount(), 0);
+
+ buffer->setObserver(NULL);
+ buffer->release();
+ }
+}
+
+status_t MediaBufferPool::acquire_buffer(int size, MediaBuffer **out) {
+ Mutex::Autolock autoLock(mLock);
+
+ MediaBuffer *next = NULL;
+ while (mFirstBuffer) {
+ if ((int)mFirstBuffer->size() >= size) {
+ next = mFirstBuffer->nextBuffer();
+
+ // pop first buffer out of list
+ *out = mFirstBuffer;
+ mFirstBuffer->add_ref();
+ mFirstBuffer->reset();
+
+ mFirstBuffer = next;
+ if (mFirstBuffer == NULL) {
+ mLastBuffer = NULL;
+ }
+ return OK;
+ } else {
+ // delete the first buffer from the list
+ next = mFirstBuffer->nextBuffer();
+ mFirstBuffer->setObserver(NULL);
+ mFirstBuffer->release();
+ mFirstBuffer = next;
+ }
+ }
+
+ // not a single buffer matches the requirement. Allocating a new buffer.
+
+ mFirstBuffer = NULL;
+ mLastBuffer = NULL;
+
+ size = ((size + DEFAULT_PAGE_SIZE - 1)/DEFAULT_PAGE_SIZE) * DEFAULT_PAGE_SIZE;
+ if (size < mMaxBufferSize) {
+ size = mMaxBufferSize;
+ } else {
+ mMaxBufferSize = size;
+ }
+ MediaBuffer *p = new MediaBuffer(size);
+ *out = p;
+ return (p != NULL) ? OK : NO_MEMORY;
+}
+
+void MediaBufferPool::signalBufferReturned(MediaBuffer *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ buffer->setObserver(this);
+
+ if (mLastBuffer) {
+ mLastBuffer->setNextBuffer(buffer);
+ } else {
+ mFirstBuffer = buffer;
+ }
+
+ mLastBuffer = buffer;
+}
+
+} // namespace android
diff --git a/frameworks/asf_extractor/MediaBufferPool.h b/frameworks/asf_extractor/MediaBufferPool.h
new file mode 100644
index 0000000..2e35e0a
--- /dev/null
+++ b/frameworks/asf_extractor/MediaBufferPool.h
@@ -0,0 +1,55 @@
+/*
+* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
+*
+* 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.
+*/
+
+
+#ifndef MEDIA_BUFFER_POOL_H_
+
+#define MEDIA_BUFFER_POOL_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+class MediaBufferPool : public MediaBufferObserver {
+public:
+ MediaBufferPool();
+ ~MediaBufferPool();
+
+ status_t acquire_buffer(int size, MediaBuffer **buffer);
+
+protected:
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+ friend class MediaBuffer;
+
+ Mutex mLock;
+ int mMaxBufferSize;
+
+ MediaBuffer *mFirstBuffer, *mLastBuffer;
+
+ MediaBufferPool(const MediaBufferPool &);
+ MediaBufferPool &operator=(const MediaBufferPool &);
+};
+
+} // namespace android
+
+#endif // MEDIA_BUFFER_POOL_H_
diff --git a/frameworks/asf_extractor/MetaDataExt.h b/frameworks/asf_extractor/MetaDataExt.h
new file mode 100644
index 0000000..6476b9b
--- /dev/null
+++ b/frameworks/asf_extractor/MetaDataExt.h
@@ -0,0 +1,53 @@
+/*
+* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
+*
+* 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.
+*/
+
+
+#ifndef META_DATA_EXT_H_
+#define META_DATA_EXT_H_
+
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+#define MEDIA_MIMETYPE_AUDIO_WMA "audio/wma"
+#define MEDIA_MIMETYPE_AUDIO_AC3 "audio/ac3"
+#define MEDIA_MIMETYPE_VIDEO_WMV "video/wmv"
+#define MEDIA_MIMETYPE_CONTAINER_ASF "video/x-ms-asf"
+#define MEDIA_MIMETYPE_VIDEO_VA "video/x-va"
+#define MEDIA_MIMETYPE_AUDIO_WMA_VOICE "audio/wma-voice"
+
+
+enum
+{
+ // value by default takes int32_t unless specified
+ kKeyConfigData = 'kcfg', // raw data
+ kKeyProtected = 'prot', // int32_t (bool)
+ kKeyCropLeft = 'clft',
+ kKeyCropRight = 'crit',
+ kKeyCropTop = 'ctop',
+ kKeyCropBottom = 'cbtm',
+ kKeySuggestedBufferSize = 'sgbz',
+ kKeyWantRawOutput = 'rawo'
+};
+
+enum
+{
+ kTypeConfigData = 'tcfg',
+};
+
+} // namespace android
+
+#endif // META_DATA_EXT_H_