summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice/nuplayer
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2011-08-16 09:34:26 -0700
committerMarco Nelissen <marcone@google.com>2012-08-03 13:57:32 -0700
commit840667883fd09d44015716d79bc3ac4d60edc0f0 (patch)
treecb55492dbe3ca64b2ab9ba2f5a3bb6d0a0bcf650 /media/libmediaplayerservice/nuplayer
parent9cb20d4a41eb84bfab7f9f0d0829393f378583f4 (diff)
downloadframeworks_av-840667883fd09d44015716d79bc3ac4d60edc0f0.zip
frameworks_av-840667883fd09d44015716d79bc3ac4d60edc0f0.tar.gz
frameworks_av-840667883fd09d44015716d79bc3ac4d60edc0f0.tar.bz2
experimental support for fragmented mp4 playback in nuplayer
cherry picked from change 170999 Change-Id: I407775f0290154ad4961134839a15c9f296424c0
Diffstat (limited to 'media/libmediaplayerservice/nuplayer')
-rw-r--r--media/libmediaplayerservice/nuplayer/Android.mk3
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp2
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp2
-rw-r--r--media/libmediaplayerservice/nuplayer/HTTPLiveSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp41
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h6
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp41
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h2
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerSource.h4
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.cpp2
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.cpp2
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp132
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/MP4Source.h52
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/Parser.cpp1643
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/Parser.h252
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp383
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h122
20 files changed, 2654 insertions, 47 deletions
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index f97ba57..f469054 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -11,6 +11,9 @@ LOCAL_SRC_FILES:= \
NuPlayerStreamListener.cpp \
RTSPSource.cpp \
StreamingSource.cpp \
+ mp4/MP4Source.cpp \
+ mp4/Parser.cpp \
+ mp4/TrackFragment.cpp \
LOCAL_C_INCLUDES := \
$(TOP)/frameworks/av/media/libstagefright/httplive \
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 99569c9..f0c3240 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -128,7 +128,7 @@ status_t NuPlayer::GenericSource::feedMoreTSData() {
return OK;
}
-sp<MetaData> NuPlayer::GenericSource::getFormat(bool audio) {
+sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) {
sp<MediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource;
if (source == NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index aaa5876..e50b855 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -43,7 +43,6 @@ struct NuPlayer::GenericSource : public NuPlayer::Source {
virtual status_t feedMoreTSData();
- virtual sp<MetaData> getFormat(bool audio);
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
virtual status_t getDuration(int64_t *durationUs);
@@ -53,6 +52,8 @@ struct NuPlayer::GenericSource : public NuPlayer::Source {
protected:
virtual ~GenericSource();
+ virtual sp<MetaData> getFormatMeta(bool audio);
+
private:
struct Track {
sp<MediaSource> mSource;
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 22b8847..1e98f35 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -81,7 +81,7 @@ void NuPlayer::HTTPLiveSource::start() {
mTSParser = new ATSParser;
}
-sp<MetaData> NuPlayer::HTTPLiveSource::getFormat(bool audio) {
+sp<MetaData> NuPlayer::HTTPLiveSource::getFormatMeta(bool audio) {
ATSParser::SourceType type =
audio ? ATSParser::AUDIO : ATSParser::VIDEO;
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index f22af5b..9950a9e 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -37,7 +37,6 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source {
virtual status_t feedMoreTSData();
- virtual sp<MetaData> getFormat(bool audio);
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
virtual status_t getDuration(int64_t *durationUs);
@@ -47,6 +46,8 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source {
protected:
virtual ~HTTPLiveSource();
+ virtual sp<MetaData> getFormatMeta(bool audio);
+
private:
enum Flags {
// Don't log any URLs.
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index daf60f6..a02732b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -28,9 +28,11 @@
#include "RTSPSource.h"
#include "StreamingSource.h"
#include "GenericSource.h"
+#include "mp4/MP4Source.h"
#include "ATSParser.h"
+#include <cutils/properties.h> // for property_get
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -43,6 +45,9 @@
#include "avc_utils.h"
+#include "ESDS.h"
+#include <media/stagefright/Utils.h>
+
namespace android {
////////////////////////////////////////////////////////////////////////////////
@@ -81,7 +86,14 @@ void NuPlayer::setDriver(const wp<NuPlayerDriver> &driver) {
void NuPlayer::setDataSource(const sp<IStreamSource> &source) {
sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
- msg->setObject("source", new StreamingSource(source));
+ char prop[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.use-mp4source", prop, NULL)
+ && (!strcmp(prop, "1") || !strcasecmp(prop, "true"))) {
+ msg->setObject("source", new MP4Source(source));
+ } else {
+ msg->setObject("source", new StreamingSource(source));
+ }
+
msg->post();
}
@@ -679,16 +691,16 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
return OK;
}
- sp<MetaData> meta = mSource->getFormat(audio);
+ sp<AMessage> format = mSource->getFormat(audio);
- if (meta == NULL) {
+ if (format == NULL) {
return -EWOULDBLOCK;
}
if (!audio) {
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
- mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime);
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+ mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str());
}
sp<AMessage> notify =
@@ -699,7 +711,7 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
new Decoder(notify, mNativeWindow);
looper()->registerHandler(*decoder);
- (*decoder)->configure(meta);
+ (*decoder)->configure(format);
int64_t durationUs;
if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
@@ -930,4 +942,19 @@ void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
}
}
+sp<AMessage> NuPlayer::Source::getFormat(bool audio) {
+ sp<MetaData> meta = getFormatMeta(audio);
+
+ if (meta == NULL) {
+ return NULL;
+ }
+
+ sp<AMessage> msg = new AMessage;
+
+ if(convertMetaDataToMessage(meta, &msg) == OK) {
+ return msg;
+ }
+ return NULL;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 25766e0..996806e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -60,14 +60,16 @@ protected:
virtual void onMessageReceived(const sp<AMessage> &msg);
+public:
+ struct NuPlayerStreamListener;
+ struct Source;
+
private:
struct Decoder;
struct GenericSource;
struct HTTPLiveSource;
- struct NuPlayerStreamListener;
struct Renderer;
struct RTSPSource;
- struct Source;
struct StreamingSource;
enum {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 0e53662..22f699e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -20,15 +20,11 @@
#include "NuPlayerDecoder.h"
-#include "ESDS.h"
-
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
namespace android {
@@ -42,16 +38,24 @@ NuPlayer::Decoder::Decoder(
NuPlayer::Decoder::~Decoder() {
}
-void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
+void NuPlayer::Decoder::configure(const sp<AMessage> &format) {
CHECK(mCodec == NULL);
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
+ AString mime;
+ CHECK(format->findString("mime", &mime));
sp<AMessage> notifyMsg =
new AMessage(kWhatCodecNotify, id());
- sp<AMessage> format = makeFormat(meta);
+ mCSDIndex = 0;
+ for (size_t i = 0;; ++i) {
+ sp<ABuffer> csd;
+ if (!format->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
+ break;
+ }
+
+ mCSD.push(csd);
+ }
if (mNativeWindow != NULL) {
format->setObject("native-window", mNativeWindow);
@@ -61,7 +65,7 @@ void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
// quickly, violating the OpenMAX specs, until that is remedied
// we need to invest in an extra looper to free the main event
// queue.
- bool needDedicatedLooper = !strncasecmp(mime, "video/", 6);
+ bool needDedicatedLooper = !strncasecmp(mime.c_str(), "video/", 6);
mCodec = new ACodec;
@@ -100,25 +104,6 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
}
}
-sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
- CHECK(mCSD.isEmpty());
-
- sp<AMessage> msg;
- CHECK_EQ(convertMetaDataToMessage(meta, &msg), (status_t)OK);
-
- mCSDIndex = 0;
- for (size_t i = 0;; ++i) {
- sp<ABuffer> csd;
- if (!msg->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
- break;
- }
-
- mCSD.push(csd);
- }
-
- return msg;
-}
-
void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) {
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 3ab1fcf..a876148 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -30,7 +30,7 @@ struct NuPlayer::Decoder : public AHandler {
Decoder(const sp<AMessage> &notify,
const sp<NativeWindowWrapper> &nativeWindow = NULL);
- void configure(const sp<MetaData> &meta);
+ void configure(const sp<AMessage> &format);
void signalFlush();
void signalResume();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 531b29f..66aeff3 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -34,7 +34,7 @@ struct NuPlayer::Source : public RefBase {
// an error or ERROR_END_OF_STREAM if not.
virtual status_t feedMoreTSData() = 0;
- virtual sp<MetaData> getFormat(bool audio) = 0;
+ virtual sp<AMessage> getFormat(bool audio);
virtual status_t dequeueAccessUnit(
bool audio, sp<ABuffer> *accessUnit) = 0;
@@ -54,6 +54,8 @@ struct NuPlayer::Source : public RefBase {
protected:
virtual ~Source() {}
+ virtual sp<MetaData> getFormatMeta(bool audio) { return NULL; }
+
private:
DISALLOW_EVIL_CONSTRUCTORS(Source);
};
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index c910488..4a704e3 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -95,7 +95,7 @@ status_t NuPlayer::RTSPSource::feedMoreTSData() {
return mFinalResult;
}
-sp<MetaData> NuPlayer::RTSPSource::getFormat(bool audio) {
+sp<MetaData> NuPlayer::RTSPSource::getFormatMeta(bool audio) {
sp<AnotherPacketSource> source = getSource(audio);
if (source == NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index e11e304..c8409e5 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -40,7 +40,6 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source {
virtual status_t feedMoreTSData();
- virtual sp<MetaData> getFormat(bool audio);
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
virtual status_t getDuration(int64_t *durationUs);
@@ -52,6 +51,8 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source {
protected:
virtual ~RTSPSource();
+ virtual sp<MetaData> getFormatMeta(bool audio);
+
private:
enum {
kWhatNotify = 'noti',
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index 7c9bc5e..b696aa4 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -107,7 +107,7 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() {
return OK;
}
-sp<MetaData> NuPlayer::StreamingSource::getFormat(bool audio) {
+sp<MetaData> NuPlayer::StreamingSource::getFormatMeta(bool audio) {
ATSParser::SourceType type =
audio ? ATSParser::AUDIO : ATSParser::VIDEO;
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h
index ca00ef9..3971e2a 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.h
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h
@@ -33,12 +33,13 @@ struct NuPlayer::StreamingSource : public NuPlayer::Source {
virtual status_t feedMoreTSData();
- virtual sp<MetaData> getFormat(bool audio);
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
protected:
virtual ~StreamingSource();
+ virtual sp<MetaData> getFormatMeta(bool audio);
+
private:
sp<IStreamSource> mSource;
status_t mFinalResult;
diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
new file mode 100644
index 0000000..25c91e9
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#include "MP4Source.h"
+
+#include "Parser.h"
+#include "../NuPlayerStreamListener.h"
+
+#include <media/IStreamSource.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct StreamSource : public Parser::Source {
+ StreamSource(const sp<IStreamSource> &source)
+ : mListener(new NuPlayer::NuPlayerStreamListener(source, 0)),
+ mPosition(0) {
+ mListener->start();
+ }
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
+ if (offset < mPosition) {
+ return -EPIPE;
+ }
+
+ while (offset > mPosition) {
+ char buffer[1024];
+ off64_t skipBytes = offset - mPosition;
+ if (skipBytes > sizeof(buffer)) {
+ skipBytes = sizeof(buffer);
+ }
+
+ sp<AMessage> extra;
+ ssize_t n;
+ for (;;) {
+ n = mListener->read(buffer, skipBytes, &extra);
+
+ if (n == -EWOULDBLOCK) {
+ usleep(10000);
+ continue;
+ }
+
+ break;
+ }
+
+ ALOGV("skipped %ld bytes at offset %lld", n, mPosition);
+
+ if (n < 0) {
+ return n;
+ }
+
+ mPosition += n;
+ }
+
+ sp<AMessage> extra;
+ size_t total = 0;
+ while (total < size) {
+ ssize_t n = mListener->read(
+ (uint8_t *)data + total, size - total, &extra);
+
+ if (n == -EWOULDBLOCK) {
+ usleep(10000);
+ continue;
+ } else if (n == 0) {
+ break;
+ } else if (n < 0) {
+ mPosition += total;
+ return n;
+ }
+
+ total += n;
+ }
+
+ ALOGV("read %ld bytes at offset %lld", n, mPosition);
+
+ mPosition += total;
+
+ return total;
+ }
+
+private:
+ sp<NuPlayer::NuPlayerStreamListener> mListener;
+ off64_t mPosition;
+
+ DISALLOW_EVIL_CONSTRUCTORS(StreamSource);
+};
+
+MP4Source::MP4Source(const sp<IStreamSource> &source)
+ : mSource(source),
+ mLooper(new ALooper),
+ mParser(new Parser),
+ mEOS(false) {
+ mLooper->registerHandler(mParser);
+}
+
+MP4Source::~MP4Source() {
+}
+
+void MP4Source::start() {
+ mLooper->start(false /* runOnCallingThread */);
+ mParser->start(new StreamSource(mSource));
+}
+
+status_t MP4Source::feedMoreTSData() {
+ return mEOS ? ERROR_END_OF_STREAM : (status_t)OK;
+}
+
+sp<AMessage> MP4Source::getFormat(bool audio) {
+ return mParser->getFormat(audio);
+}
+
+status_t MP4Source::dequeueAccessUnit(
+ bool audio, sp<ABuffer> *accessUnit) {
+ return mParser->dequeueAccessUnit(audio, accessUnit);
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
new file mode 100644
index 0000000..57430aa
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef MP4_SOURCE_H
+#define MP4_SOURCE_H
+
+#include "NuPlayerSource.h"
+
+namespace android {
+
+struct Parser;
+
+struct MP4Source : public NuPlayer::Source {
+ MP4Source(const sp<IStreamSource> &source);
+
+ virtual void start();
+
+ virtual status_t feedMoreTSData();
+
+ virtual sp<AMessage> getFormat(bool audio);
+
+ virtual status_t dequeueAccessUnit(
+ bool audio, sp<ABuffer> *accessUnit);
+
+protected:
+ virtual ~MP4Source();
+
+private:
+ sp<IStreamSource> mSource;
+ sp<ALooper> mLooper;
+ sp<Parser> mParser;
+ bool mEOS;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MP4Source);
+};
+
+} // namespace android
+
+#endif // MP4_SOURCE_H
diff --git a/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp b/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp
new file mode 100644
index 0000000..b9fe819
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp
@@ -0,0 +1,1643 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Parser"
+#include <utils/Log.h>
+
+#include "Parser.h"
+#include "TrackFragment.h"
+
+#include "ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+#include "../NuPlayerStreamListener.h"
+
+namespace android {
+
+static const char *Fourcc2String(uint32_t fourcc) {
+ static char buffer[5];
+ buffer[4] = '\0';
+ buffer[0] = fourcc >> 24;
+ buffer[1] = (fourcc >> 16) & 0xff;
+ buffer[2] = (fourcc >> 8) & 0xff;
+ buffer[3] = fourcc & 0xff;
+
+ return buffer;
+}
+
+static const char *IndentString(size_t n) {
+ static const char kSpace[] = " ";
+ return kSpace + sizeof(kSpace) - 2 * n - 1;
+}
+
+// static
+const Parser::DispatchEntry Parser::kDispatchTable[] = {
+ { FOURCC('m', 'o', 'o', 'v'), 0, NULL },
+ { FOURCC('t', 'r', 'a', 'k'), FOURCC('m', 'o', 'o', 'v'), NULL },
+ { FOURCC('u', 'd', 't', 'a'), FOURCC('t', 'r', 'a', 'k'), NULL },
+ { FOURCC('u', 'd', 't', 'a'), FOURCC('m', 'o', 'o', 'v'), NULL },
+ { FOURCC('m', 'e', 't', 'a'), FOURCC('u', 'd', 't', 'a'), NULL },
+ { FOURCC('i', 'l', 's', 't'), FOURCC('m', 'e', 't', 'a'), NULL },
+
+ { FOURCC('t', 'k', 'h', 'd'), FOURCC('t', 'r', 'a', 'k'),
+ &Parser::parseTrackHeader
+ },
+
+ { FOURCC('m', 'v', 'e', 'x'), FOURCC('m', 'o', 'o', 'v'), NULL },
+
+ { FOURCC('t', 'r', 'e', 'x'), FOURCC('m', 'v', 'e', 'x'),
+ &Parser::parseTrackExtends
+ },
+
+ { FOURCC('e', 'd', 't', 's'), FOURCC('t', 'r', 'a', 'k'), NULL },
+ { FOURCC('m', 'd', 'i', 'a'), FOURCC('t', 'r', 'a', 'k'), NULL },
+
+ { FOURCC('m', 'd', 'h', 'd'), FOURCC('m', 'd', 'i', 'a'),
+ &Parser::parseMediaHeader
+ },
+
+ { FOURCC('h', 'd', 'l', 'r'), FOURCC('m', 'd', 'i', 'a'),
+ &Parser::parseMediaHandler
+ },
+
+ { FOURCC('m', 'i', 'n', 'f'), FOURCC('m', 'd', 'i', 'a'), NULL },
+ { FOURCC('d', 'i', 'n', 'f'), FOURCC('m', 'i', 'n', 'f'), NULL },
+ { FOURCC('s', 't', 'b', 'l'), FOURCC('m', 'i', 'n', 'f'), NULL },
+ { FOURCC('s', 't', 's', 'd'), FOURCC('s', 't', 'b', 'l'), NULL },
+
+ { FOURCC('s', 't', 's', 'z'), FOURCC('s', 't', 'b', 'l'),
+ &Parser::parseSampleSizes },
+
+ { FOURCC('s', 't', 'z', '2'), FOURCC('s', 't', 'b', 'l'),
+ &Parser::parseCompactSampleSizes },
+
+ { FOURCC('s', 't', 's', 'c'), FOURCC('s', 't', 'b', 'l'),
+ &Parser::parseSampleToChunk },
+
+ { FOURCC('s', 't', 'c', 'o'), FOURCC('s', 't', 'b', 'l'),
+ &Parser::parseChunkOffsets },
+
+ { FOURCC('c', 'o', '6', '4'), FOURCC('s', 't', 'b', 'l'),
+ &Parser::parseChunkOffsets64 },
+
+ { FOURCC('a', 'v', 'c', 'C'), FOURCC('a', 'v', 'c', '1'),
+ &Parser::parseAVCCodecSpecificData },
+
+ { FOURCC('e', 's', 'd', 's'), FOURCC('m', 'p', '4', 'a'),
+ &Parser::parseESDSCodecSpecificData },
+
+ { FOURCC('e', 's', 'd', 's'), FOURCC('m', 'p', '4', 'v'),
+ &Parser::parseESDSCodecSpecificData },
+
+ { FOURCC('m', 'd', 'a', 't'), 0, &Parser::parseMediaData },
+
+ { FOURCC('m', 'o', 'o', 'f'), 0, NULL },
+ { FOURCC('t', 'r', 'a', 'f'), FOURCC('m', 'o', 'o', 'f'), NULL },
+
+ { FOURCC('t', 'f', 'h', 'd'), FOURCC('t', 'r', 'a', 'f'),
+ &Parser::parseTrackFragmentHeader
+ },
+ { FOURCC('t', 'r', 'u', 'n'), FOURCC('t', 'r', 'a', 'f'),
+ &Parser::parseTrackFragmentRun
+ },
+
+ { FOURCC('m', 'f', 'r', 'a'), 0, NULL },
+};
+
+struct FileSource : public Parser::Source {
+ FileSource(const char *filename)
+ : mFile(fopen(filename, "rb")) {
+ CHECK(mFile != NULL);
+ }
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
+ fseek(mFile, offset, SEEK_SET);
+ return fread(data, 1, size, mFile);
+ }
+
+ private:
+ FILE *mFile;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FileSource);
+};
+
+Parser::Parser()
+ : mBufferPos(0),
+ mSuspended(false) {
+}
+
+Parser::~Parser() {
+}
+
+void Parser::start(const char *filename) {
+ sp<AMessage> msg = new AMessage(kWhatStart, id());
+ msg->setObject("source", new FileSource(filename));
+ msg->post();
+}
+
+void Parser::start(const sp<Source> &source) {
+ sp<AMessage> msg = new AMessage(kWhatStart, id());
+ msg->setObject("source", source);
+ msg->post();
+}
+
+sp<AMessage> Parser::getFormat(bool audio) {
+ sp<AMessage> msg = new AMessage(kWhatGetFormat, id());
+ msg->setInt32("audio", audio);
+
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+
+ if (err != OK) {
+ return NULL;
+ }
+
+ if (response->findInt32("err", &err) && err != OK) {
+ return NULL;
+ }
+
+ sp<AMessage> format;
+ CHECK(response->findMessage("format", &format));
+
+ ALOGV("returning format %s", format->debugString().c_str());
+ return format;
+}
+
+status_t Parser::dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit) {
+ sp<AMessage> msg = new AMessage(kWhatDequeueAccessUnit, id());
+ msg->setInt32("audio", audio);
+
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (response->findInt32("err", &err) && err != OK) {
+ return err;
+ }
+
+ CHECK(response->findBuffer("accessUnit", accessUnit));
+
+ return OK;
+}
+
+ssize_t Parser::findTrack(bool wantAudio) const {
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ const TrackInfo *info = &mTracks.valueAt(i);
+
+ bool isAudio =
+ info->mMediaHandlerType == FOURCC('s', 'o', 'u', 'n');
+
+ bool isVideo =
+ info->mMediaHandlerType == FOURCC('v', 'i', 'd', 'e');
+
+ if ((wantAudio && isAudio) || (!wantAudio && !isAudio)) {
+ if (info->mSampleDescs.empty()) {
+ break;
+ }
+
+ return i;
+ }
+ }
+
+ return -EWOULDBLOCK;
+}
+
+void Parser::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatStart:
+ {
+ sp<RefBase> obj;
+ CHECK(msg->findObject("source", &obj));
+
+ mSource = static_cast<Source *>(obj.get());
+
+ mBuffer = new ABuffer(512 * 1024);
+ mBuffer->setRange(0, 0);
+
+ enter(0, 0);
+
+ (new AMessage(kWhatProceed, id()))->post();
+ break;
+ }
+
+ case kWhatProceed:
+ {
+ CHECK(!mSuspended);
+
+ status_t err = onProceed();
+
+ if (err == OK) {
+ if (!mSuspended) {
+ msg->post();
+ }
+ } else if (err != -EAGAIN) {
+ ALOGE("onProceed returned error %d", err);
+ }
+
+ break;
+ }
+
+ case kWhatReadMore:
+ {
+ size_t needed;
+ CHECK(msg->findSize("needed", &needed));
+
+ memmove(mBuffer->base(), mBuffer->data(), mBuffer->size());
+ mBufferPos += mBuffer->offset();
+ mBuffer->setRange(0, mBuffer->size());
+
+ size_t maxBytesToRead = mBuffer->capacity() - mBuffer->size();
+ CHECK_GE(maxBytesToRead, needed);
+
+ ssize_t n = mSource->readAt(
+ mBufferPos + mBuffer->size(),
+ mBuffer->data() + mBuffer->size(), needed);
+
+ if (n < (ssize_t)needed) {
+ ALOGI("%s", "Reached EOF");
+ } else {
+ mBuffer->setRange(0, mBuffer->size() + n);
+ (new AMessage(kWhatProceed, id()))->post();
+ }
+
+ break;
+ }
+
+ case kWhatGetFormat:
+ {
+ int32_t wantAudio;
+ CHECK(msg->findInt32("audio", &wantAudio));
+
+ status_t err = -EWOULDBLOCK;
+ sp<AMessage> response = new AMessage;
+
+ ssize_t trackIndex = findTrack(wantAudio);
+
+ if (trackIndex < 0) {
+ err = trackIndex;
+ } else {
+ TrackInfo *info = &mTracks.editValueAt(trackIndex);
+
+ response->setMessage(
+ "format", info->mSampleDescs.itemAt(0).mFormat);
+
+ err = OK;
+ }
+
+ response->setInt32("err", err);
+
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatDequeueAccessUnit:
+ {
+ int32_t wantAudio;
+ CHECK(msg->findInt32("audio", &wantAudio));
+
+ status_t err = -EWOULDBLOCK;
+ sp<AMessage> response = new AMessage;
+
+ ssize_t trackIndex = findTrack(wantAudio);
+
+ if (trackIndex < 0) {
+ err = trackIndex;
+ } else {
+ sp<ABuffer> accessUnit;
+ err = onDequeueAccessUnit(trackIndex, &accessUnit);
+
+ if (err == OK) {
+ response->setBuffer("accessUnit", accessUnit);
+ }
+ }
+
+ response->setInt32("err", err);
+
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ response->postReply(replyID);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+status_t Parser::onProceed() {
+ status_t err;
+
+ if ((err = need(8)) != OK) {
+ return err;
+ }
+
+ uint64_t size = readU32(0);
+ uint32_t type = readU32(4);
+
+ size_t offset = 8;
+
+ if (size == 1) {
+ if ((err = need(16)) != OK) {
+ return err;
+ }
+
+ size = readU64(offset);
+ offset += 8;
+ }
+
+ uint8_t userType[16];
+
+ if (type == FOURCC('u', 'u', 'i', 'd')) {
+ if ((err = need(offset + 16)) != OK) {
+ return err;
+ }
+
+ memcpy(userType, mBuffer->data() + offset, 16);
+ offset += 16;
+ }
+
+ CHECK(!mStack.isEmpty());
+ uint32_t ptype = mStack.itemAt(mStack.size() - 1).mType;
+
+ static const size_t kNumDispatchers =
+ sizeof(kDispatchTable) / sizeof(kDispatchTable[0]);
+
+ size_t i;
+ for (i = 0; i < kNumDispatchers; ++i) {
+ if (kDispatchTable[i].mType == type
+ && kDispatchTable[i].mParentType == ptype) {
+ break;
+ }
+ }
+
+ // SampleEntry boxes are container boxes that start with a variable
+ // amount of data depending on the media handler type.
+ // We don't look inside 'hint' type SampleEntry boxes.
+
+ bool isSampleEntryBox =
+ (ptype == FOURCC('s', 't', 's', 'd'))
+ && editTrack(mCurrentTrackID)->mMediaHandlerType
+ != FOURCC('h', 'i', 'n', 't');
+
+ if ((i < kNumDispatchers && kDispatchTable[i].mHandler == 0)
+ || isSampleEntryBox || ptype == FOURCC('i', 'l', 's', 't')) {
+ // This is a container box.
+ if (type == FOURCC('m', 'e', 't', 'a')) {
+ if ((err = need(offset + 4)) < OK) {
+ return err;
+ }
+
+ if (readU32(offset) != 0) {
+ return -EINVAL;
+ }
+
+ offset += 4;
+ } else if (type == FOURCC('s', 't', 's', 'd')) {
+ if ((err = need(offset + 8)) < OK) {
+ return err;
+ }
+
+ if (readU32(offset) != 0) {
+ return -EINVAL;
+ }
+
+ if (readU32(offset + 4) == 0) {
+ // We need at least some entries.
+ return -EINVAL;
+ }
+
+ offset += 8;
+ } else if (isSampleEntryBox) {
+ size_t headerSize;
+
+ switch (editTrack(mCurrentTrackID)->mMediaHandlerType) {
+ case FOURCC('v', 'i', 'd', 'e'):
+ {
+ // 8 bytes SampleEntry + 70 bytes VisualSampleEntry
+ headerSize = 78;
+ break;
+ }
+
+ case FOURCC('s', 'o', 'u', 'n'):
+ {
+ // 8 bytes SampleEntry + 20 bytes AudioSampleEntry
+ headerSize = 28;
+ break;
+ }
+
+ case FOURCC('m', 'e', 't', 'a'):
+ {
+ headerSize = 8; // 8 bytes SampleEntry
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+
+ if (offset + headerSize > size) {
+ return -EINVAL;
+ }
+
+ if ((err = need(offset + headerSize)) != OK) {
+ return err;
+ }
+
+ switch (editTrack(mCurrentTrackID)->mMediaHandlerType) {
+ case FOURCC('v', 'i', 'd', 'e'):
+ {
+ err = parseVisualSampleEntry(
+ type, offset, offset + headerSize);
+ break;
+ }
+
+ case FOURCC('s', 'o', 'u', 'n'):
+ {
+ err = parseAudioSampleEntry(
+ type, offset, offset + headerSize);
+ break;
+ }
+
+ case FOURCC('m', 'e', 't', 'a'):
+ {
+ err = OK;
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+
+ if (err != OK) {
+ return err;
+ }
+
+ offset += headerSize;
+ }
+
+ skip(offset);
+
+ ALOGV("%sentering box of type '%s'",
+ IndentString(mStack.size()), Fourcc2String(type));
+
+ enter(type, size - offset);
+ } else {
+ if (!fitsContainer(size)) {
+ return -EINVAL;
+ }
+
+ if (i < kNumDispatchers && kDispatchTable[i].mHandler != 0) {
+ // We have a handler for this box type.
+
+ if ((err = need(size)) != OK) {
+ return err;
+ }
+
+ ALOGV("%sparsing box of type '%s'",
+ IndentString(mStack.size()), Fourcc2String(type));
+
+ if ((err = (this->*kDispatchTable[i].mHandler)(
+ type, offset, size)) != OK) {
+ return err;
+ }
+ } else {
+ // Unknown box type
+
+ ALOGV("%sskipping box of type '%s', size %llu",
+ IndentString(mStack.size()),
+ Fourcc2String(type), size);
+
+ }
+
+ skip(size);
+ }
+
+ return OK;
+}
+
+// static
+int Parser::CompareSampleLocation(
+ const SampleInfo &sample, const MediaDataInfo &mdatInfo) {
+ if (sample.mOffset + sample.mSize < mdatInfo.mOffset) {
+ return -1;
+ }
+
+ if (sample.mOffset >= mdatInfo.mOffset + mdatInfo.mBuffer->size()) {
+ return 1;
+ }
+
+ // Otherwise make sure the sample is completely contained within this
+ // media data block.
+
+ CHECK_GE(sample.mOffset, mdatInfo.mOffset);
+
+ CHECK_LE(sample.mOffset + sample.mSize,
+ mdatInfo.mOffset + mdatInfo.mBuffer->size());
+
+ return 0;
+}
+
+void Parser::resumeIfNecessary() {
+ if (!mSuspended) {
+ return;
+ }
+
+ ALOGI("resuming.");
+
+ mSuspended = false;
+ (new AMessage(kWhatProceed, id()))->post();
+}
+
+status_t Parser::getSample(
+ TrackInfo *info, sp<TrackFragment> *fragment, SampleInfo *sampleInfo) {
+ for (;;) {
+ if (info->mFragments.empty()) {
+ resumeIfNecessary();
+ return -EWOULDBLOCK;
+ }
+
+ *fragment = *info->mFragments.begin();
+
+ status_t err = (*fragment)->getSample(sampleInfo);
+
+ if (err == OK) {
+ return OK;
+ } else if (err != ERROR_END_OF_STREAM) {
+ return err;
+ }
+
+ // Really, end of this fragment...
+
+ info->mFragments.erase(info->mFragments.begin());
+ }
+}
+
+status_t Parser::onDequeueAccessUnit(
+ size_t trackIndex, sp<ABuffer> *accessUnit) {
+ TrackInfo *info = &mTracks.editValueAt(trackIndex);
+
+ sp<TrackFragment> fragment;
+ SampleInfo sampleInfo;
+ status_t err = getSample(info, &fragment, &sampleInfo);
+
+ if (err == -EWOULDBLOCK) {
+ resumeIfNecessary();
+ return err;
+ } else if (err != OK) {
+ return err;
+ }
+
+ err = -EWOULDBLOCK;
+
+ bool checkDroppable = false;
+
+ for (size_t i = 0; i < mMediaData.size(); ++i) {
+ const MediaDataInfo &mdatInfo = mMediaData.itemAt(i);
+
+ int cmp = CompareSampleLocation(sampleInfo, mdatInfo);
+
+ if (cmp < 0) {
+ return -EPIPE;
+ } else if (cmp == 0) {
+ if (i > 0) {
+ checkDroppable = true;
+ }
+
+ err = makeAccessUnit(info, sampleInfo, mdatInfo, accessUnit);
+ break;
+ }
+ }
+
+ if (err != OK) {
+ return err;
+ }
+
+ fragment->advance();
+
+ if (!mMediaData.empty() && checkDroppable) {
+ size_t numDroppable = 0;
+ bool done = false;
+
+ for (size_t i = 0; !done && i < mMediaData.size(); ++i) {
+ const MediaDataInfo &mdatInfo = mMediaData.itemAt(i);
+
+ for (size_t j = 0; j < mTracks.size(); ++j) {
+ TrackInfo *info = &mTracks.editValueAt(j);
+
+ sp<TrackFragment> fragment;
+ SampleInfo sampleInfo;
+ err = getSample(info, &fragment, &sampleInfo);
+
+ if (err != OK) {
+ done = true;
+ break;
+ }
+
+ int cmp = CompareSampleLocation(sampleInfo, mdatInfo);
+
+ if (cmp <= 0) {
+ done = true;
+ break;
+ }
+ }
+
+ if (!done) {
+ ++numDroppable;
+ }
+ }
+
+ if (numDroppable > 0) {
+ mMediaData.removeItemsAt(0, numDroppable);
+
+ if (mMediaData.size() < 5) {
+ resumeIfNecessary();
+ }
+ }
+ }
+
+ return err;
+}
+
+static size_t parseNALSize(size_t nalLengthSize, const uint8_t *data) {
+ switch (nalLengthSize) {
+ case 1:
+ return *data;
+ case 2:
+ return U16_AT(data);
+ case 3:
+ return ((size_t)data[0] << 16) | U16_AT(&data[1]);
+ case 4:
+ return U32_AT(data);
+ }
+
+ // This cannot happen, mNALLengthSize springs to life by adding 1 to
+ // a 2-bit integer.
+ TRESPASS();
+
+ return 0;
+}
+
+status_t Parser::makeAccessUnit(
+ TrackInfo *info,
+ const SampleInfo &sample,
+ const MediaDataInfo &mdatInfo,
+ sp<ABuffer> *accessUnit) {
+ if (sample.mSampleDescIndex < 1
+ || sample.mSampleDescIndex > info->mSampleDescs.size()) {
+ return ERROR_MALFORMED;
+ }
+
+ int64_t presentationTimeUs =
+ 1000000ll * sample.mPresentationTime / info->mMediaTimeScale;
+
+ const SampleDescription &sampleDesc =
+ info->mSampleDescs.itemAt(sample.mSampleDescIndex - 1);
+
+ size_t nalLengthSize;
+ if (!sampleDesc.mFormat->findSize("nal-length-size", &nalLengthSize)) {
+ *accessUnit = new ABuffer(sample.mSize);
+
+ memcpy((*accessUnit)->data(),
+ mdatInfo.mBuffer->data() + (sample.mOffset - mdatInfo.mOffset),
+ sample.mSize);
+
+ (*accessUnit)->meta()->setInt64("timeUs", presentationTimeUs);
+ return OK;
+ }
+
+ const uint8_t *srcPtr =
+ mdatInfo.mBuffer->data() + (sample.mOffset - mdatInfo.mOffset);
+
+ for (int i = 0; i < 2 ; ++i) {
+ size_t srcOffset = 0;
+ size_t dstOffset = 0;
+
+ while (srcOffset < sample.mSize) {
+ if (srcOffset + nalLengthSize > sample.mSize) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t nalSize = parseNALSize(nalLengthSize, &srcPtr[srcOffset]);
+ srcOffset += nalLengthSize;
+
+ if (srcOffset + nalSize > sample.mSize) {
+ return ERROR_MALFORMED;
+ }
+
+ if (i == 1) {
+ memcpy((*accessUnit)->data() + dstOffset,
+ "\x00\x00\x00\x01",
+ 4);
+
+ memcpy((*accessUnit)->data() + dstOffset + 4,
+ srcPtr + srcOffset,
+ nalSize);
+ }
+
+ srcOffset += nalSize;
+ dstOffset += nalSize + 4;
+ }
+
+ if (i == 0) {
+ (*accessUnit) = new ABuffer(dstOffset);
+ (*accessUnit)->meta()->setInt64(
+ "timeUs", presentationTimeUs);
+ }
+ }
+
+ return OK;
+}
+
+status_t Parser::need(size_t size) {
+ if (!fitsContainer(size)) {
+ return -EINVAL;
+ }
+
+ if (size <= mBuffer->size()) {
+ return OK;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatReadMore, id());
+ msg->setSize("needed", size - mBuffer->size());
+ msg->post();
+
+ // ALOGV("need(%d) returning -EAGAIN, only have %d", size, mBuffer->size());
+
+ return -EAGAIN;
+}
+
+void Parser::enter(uint32_t type, uint64_t size) {
+ Container container;
+ container.mType = type;
+ container.mExtendsToEOF = (size == 0);
+ container.mBytesRemaining = size;
+
+ mStack.push(container);
+}
+
+bool Parser::fitsContainer(uint64_t size) const {
+ CHECK(!mStack.isEmpty());
+ const Container &container = mStack.itemAt(mStack.size() - 1);
+
+ return container.mExtendsToEOF || size <= container.mBytesRemaining;
+}
+
+uint16_t Parser::readU16(size_t offset) {
+ CHECK_LE(offset + 2, mBuffer->size());
+
+ const uint8_t *ptr = mBuffer->data() + offset;
+ return (ptr[0] << 8) | ptr[1];
+}
+
+uint32_t Parser::readU32(size_t offset) {
+ CHECK_LE(offset + 4, mBuffer->size());
+
+ const uint8_t *ptr = mBuffer->data() + offset;
+ return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
+}
+
+uint64_t Parser::readU64(size_t offset) {
+ return (((uint64_t)readU32(offset)) << 32) | readU32(offset + 4);
+}
+
+void Parser::skip(off_t distance) {
+ CHECK(!mStack.isEmpty());
+ for (size_t i = mStack.size(); i-- > 0;) {
+ Container *container = &mStack.editItemAt(i);
+ if (!container->mExtendsToEOF) {
+ CHECK_LE(distance, (off_t)container->mBytesRemaining);
+
+ container->mBytesRemaining -= distance;
+
+ if (container->mBytesRemaining == 0) {
+ ALOGV("%sleaving box of type '%s'",
+ IndentString(mStack.size() - 1),
+ Fourcc2String(container->mType));
+
+#if 0
+ if (container->mType == FOURCC('s', 't', 's', 'd')) {
+ TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+ for (size_t i = 0;
+ i < trackInfo->mSampleDescs.size(); ++i) {
+ ALOGI("format #%d: %s",
+ i,
+ trackInfo->mSampleDescs.itemAt(i)
+ .mFormat->debugString().c_str());
+ }
+ }
+#endif
+
+ if (container->mType == FOURCC('s', 't', 'b', 'l')) {
+ TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+ trackInfo->mStaticFragment->signalCompletion();
+
+ CHECK(trackInfo->mFragments.empty());
+ trackInfo->mFragments.push_back(trackInfo->mStaticFragment);
+ trackInfo->mStaticFragment.clear();
+ } else if (container->mType == FOURCC('t', 'r', 'a', 'f')) {
+ TrackInfo *trackInfo =
+ editTrack(mTrackFragmentHeaderInfo.mTrackID);
+
+ const sp<TrackFragment> &fragment =
+ *--trackInfo->mFragments.end();
+
+ static_cast<DynamicTrackFragment *>(
+ fragment.get())->signalCompletion();
+ }
+
+ container = NULL;
+ mStack.removeItemsAt(i);
+ }
+ }
+ }
+
+ if (distance < (off_t)mBuffer->size()) {
+ mBuffer->setRange(mBuffer->offset() + distance, mBuffer->size() - distance);
+ mBufferPos += distance;
+ return;
+ }
+
+ mBuffer->setRange(0, 0);
+ mBufferPos += distance;
+}
+
+status_t Parser::parseTrackHeader(
+ uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 4 > size) {
+ return -EINVAL;
+ }
+
+ uint32_t flags = readU32(offset);
+
+ uint32_t version = flags >> 24;
+ flags &= 0xffffff;
+
+ uint32_t trackID;
+ uint64_t duration;
+
+ if (version == 1) {
+ if (offset + 36 > size) {
+ return -EINVAL;
+ }
+
+ trackID = readU32(offset + 20);
+ duration = readU64(offset + 28);
+
+ offset += 36;
+ } else if (version == 0) {
+ if (offset + 24 > size) {
+ return -EINVAL;
+ }
+
+ trackID = readU32(offset + 12);
+ duration = readU32(offset + 20);
+
+ offset += 24;
+ } else {
+ return -EINVAL;
+ }
+
+ TrackInfo *info = editTrack(trackID, true /* createIfNecessary */);
+ info->mFlags = flags;
+ info->mDuration = duration;
+
+ info->mStaticFragment = new StaticTrackFragment;
+
+ mCurrentTrackID = trackID;
+
+ return OK;
+}
+
+status_t Parser::parseMediaHeader(
+ uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 4 > size) {
+ return -EINVAL;
+ }
+
+ uint32_t versionAndFlags = readU32(offset);
+
+ if (versionAndFlags & 0xffffff) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t version = versionAndFlags >> 24;
+
+ TrackInfo *info = editTrack(mCurrentTrackID);
+
+ if (version == 1) {
+ if (offset + 4 + 32 > size) {
+ return -EINVAL;
+ }
+ info->mMediaTimeScale = U32_AT(mBuffer->data() + offset + 20);
+ } else if (version == 0) {
+ if (offset + 4 + 20 > size) {
+ return -EINVAL;
+ }
+ info->mMediaTimeScale = U32_AT(mBuffer->data() + offset + 12);
+ } else {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+}
+
+status_t Parser::parseMediaHandler(
+ uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 12 > size) {
+ return -EINVAL;
+ }
+
+ if (readU32(offset) != 0) {
+ return -EINVAL;
+ }
+
+ uint32_t handlerType = readU32(offset + 8);
+
+ switch (handlerType) {
+ case FOURCC('v', 'i', 'd', 'e'):
+ case FOURCC('s', 'o', 'u', 'n'):
+ case FOURCC('h', 'i', 'n', 't'):
+ case FOURCC('m', 'e', 't', 'a'):
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ editTrack(mCurrentTrackID)->mMediaHandlerType = handlerType;
+
+ return OK;
+}
+
+status_t Parser::parseVisualSampleEntry(
+ uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 78 > size) {
+ return -EINVAL;
+ }
+
+ TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+ trackInfo->mSampleDescs.push();
+ SampleDescription *sampleDesc =
+ &trackInfo->mSampleDescs.editItemAt(
+ trackInfo->mSampleDescs.size() - 1);
+
+ sampleDesc->mType = type;
+ sampleDesc->mDataRefIndex = readU16(offset + 6);
+
+ sp<AMessage> format = new AMessage;
+
+ switch (type) {
+ case FOURCC('a', 'v', 'c', '1'):
+ format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
+ break;
+ case FOURCC('m', 'p', '4', 'v'):
+ format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
+ break;
+ default:
+ format->setString("mime", "application/octet-stream");
+ break;
+ }
+
+ format->setInt32("width", readU16(offset + 8 + 16));
+ format->setInt32("height", readU16(offset + 8 + 18));
+
+ sampleDesc->mFormat = format;
+
+ return OK;
+}
+
+status_t Parser::parseAudioSampleEntry(
+ uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 28 > size) {
+ return -EINVAL;
+ }
+
+ TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+ trackInfo->mSampleDescs.push();
+ SampleDescription *sampleDesc =
+ &trackInfo->mSampleDescs.editItemAt(
+ trackInfo->mSampleDescs.size() - 1);
+
+ sampleDesc->mType = type;
+ sampleDesc->mDataRefIndex = readU16(offset + 6);
+
+ sp<AMessage> format = new AMessage;
+
+ format->setInt32("channel-count", readU16(offset + 8 + 8));
+ format->setInt32("sample-size", readU16(offset + 8 + 10));
+ format->setInt32("sample-rate", readU32(offset + 8 + 16) / 65536.0f);
+
+ switch (type) {
+ case FOURCC('m', 'p', '4', 'a'):
+ format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
+ break;
+ case FOURCC('s', 'a', 'm', 'r'):
+ format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
+ format->setInt32("channel-count", 1);
+ format->setInt32("sample-rate", 8000);
+ break;
+ case FOURCC('s', 'a', 'w', 'b'):
+ format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB);
+ format->setInt32("channel-count", 1);
+ format->setInt32("sample-rate", 16000);
+ break;
+ default:
+ format->setString("mime", "application/octet-stream");
+ break;
+ }
+
+ sampleDesc->mFormat = format;
+
+ return OK;
+}
+
+static void addCodecSpecificData(
+ const sp<AMessage> &format, int32_t index,
+ const void *data, size_t size,
+ bool insertStartCode = false) {
+ sp<ABuffer> csd = new ABuffer(insertStartCode ? size + 4 : size);
+
+ memcpy(csd->data() + (insertStartCode ? 4 : 0), data, size);
+
+ if (insertStartCode) {
+ memcpy(csd->data(), "\x00\x00\x00\x01", 4);
+ }
+
+ csd->meta()->setInt32("csd", true);
+ csd->meta()->setInt64("timeUs", 0ll);
+
+ format->setBuffer(StringPrintf("csd-%d", index).c_str(), csd);
+}
+
+status_t Parser::parseSampleSizes(
+ uint32_t type, size_t offset, uint64_t size) {
+ return editTrack(mCurrentTrackID)->mStaticFragment->parseSampleSizes(
+ this, type, offset, size);
+}
+
+status_t Parser::parseCompactSampleSizes(
+ uint32_t type, size_t offset, uint64_t size) {
+ return editTrack(mCurrentTrackID)->mStaticFragment->parseCompactSampleSizes(
+ this, type, offset, size);
+}
+
+status_t Parser::parseSampleToChunk(
+ uint32_t type, size_t offset, uint64_t size) {
+ return editTrack(mCurrentTrackID)->mStaticFragment->parseSampleToChunk(
+ this, type, offset, size);
+}
+
+status_t Parser::parseChunkOffsets(
+ uint32_t type, size_t offset, uint64_t size) {
+ return editTrack(mCurrentTrackID)->mStaticFragment->parseChunkOffsets(
+ this, type, offset, size);
+}
+
+status_t Parser::parseChunkOffsets64(
+ uint32_t type, size_t offset, uint64_t size) {
+ return editTrack(mCurrentTrackID)->mStaticFragment->parseChunkOffsets64(
+ this, type, offset, size);
+}
+
+status_t Parser::parseAVCCodecSpecificData(
+ uint32_t type, size_t offset, uint64_t size) {
+ TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+ SampleDescription *sampleDesc =
+ &trackInfo->mSampleDescs.editItemAt(
+ trackInfo->mSampleDescs.size() - 1);
+
+ if (sampleDesc->mType != FOURCC('a', 'v', 'c', '1')) {
+ return -EINVAL;
+ }
+
+ const uint8_t *ptr = mBuffer->data() + offset;
+
+ size -= offset;
+ offset = 0;
+
+ if (size < 7 || ptr[0] != 0x01) {
+ return ERROR_MALFORMED;
+ }
+
+ sampleDesc->mFormat->setSize("nal-length-size", 1 + (ptr[4] & 3));
+
+ size_t numSPS = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ for (size_t i = 0; i < numSPS; ++i) {
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ if (size < length) {
+ return ERROR_MALFORMED;
+ }
+
+ addCodecSpecificData(
+ sampleDesc->mFormat, i, ptr, length,
+ true /* insertStartCode */);
+
+ ptr += length;
+ size -= length;
+ }
+
+ if (size < 1) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t numPPS = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPPS; ++i) {
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ if (size < length) {
+ return ERROR_MALFORMED;
+ }
+
+ addCodecSpecificData(
+ sampleDesc->mFormat, numSPS + i, ptr, length,
+ true /* insertStartCode */);
+
+ ptr += length;
+ size -= length;
+ }
+
+ return OK;
+}
+
+status_t Parser::parseESDSCodecSpecificData(
+ uint32_t type, size_t offset, uint64_t size) {
+ TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+ SampleDescription *sampleDesc =
+ &trackInfo->mSampleDescs.editItemAt(
+ trackInfo->mSampleDescs.size() - 1);
+
+ if (sampleDesc->mType != FOURCC('m', 'p', '4', 'a')
+ && sampleDesc->mType != FOURCC('m', 'p', '4', 'v')) {
+ return -EINVAL;
+ }
+
+ const uint8_t *ptr = mBuffer->data() + offset;
+
+ size -= offset;
+ offset = 0;
+
+ if (size < 4) {
+ return -EINVAL;
+ }
+
+ if (U32_AT(ptr) != 0) {
+ return -EINVAL;
+ }
+
+ ptr += 4;
+ size -=4;
+
+ ESDS esds(ptr, size);
+
+ uint8_t objectTypeIndication;
+ if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) {
+ return ERROR_MALFORMED;
+ }
+
+ const uint8_t *csd;
+ size_t csd_size;
+ if (esds.getCodecSpecificInfo(
+ (const void **)&csd, &csd_size) != OK) {
+ return ERROR_MALFORMED;
+ }
+
+ addCodecSpecificData(sampleDesc->mFormat, 0, csd, csd_size);
+
+ if (sampleDesc->mType != FOURCC('m', 'p', '4', 'a')) {
+ return OK;
+ }
+
+ if (csd_size == 0) {
+ // There's no further information, i.e. no codec specific data
+ // Let's assume that the information provided in the mpeg4 headers
+ // is accurate and hope for the best.
+
+ return OK;
+ }
+
+ if (csd_size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t objectType = csd[0] >> 3;
+
+ if (objectType == 31) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ uint32_t freqIndex = (csd[0] & 7) << 1 | (csd[1] >> 7);
+ int32_t sampleRate = 0;
+ int32_t numChannels = 0;
+ if (freqIndex == 15) {
+ if (csd_size < 5) {
+ return ERROR_MALFORMED;
+ }
+
+ sampleRate = (csd[1] & 0x7f) << 17
+ | csd[2] << 9
+ | csd[3] << 1
+ | (csd[4] >> 7);
+
+ numChannels = (csd[4] >> 3) & 15;
+ } else {
+ static uint32_t kSamplingRate[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000, 7350
+ };
+
+ if (freqIndex == 13 || freqIndex == 14) {
+ return ERROR_MALFORMED;
+ }
+
+ sampleRate = kSamplingRate[freqIndex];
+ numChannels = (csd[1] >> 3) & 15;
+ }
+
+ if (numChannels == 0) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sampleDesc->mFormat->setInt32("sample-rate", sampleRate);
+ sampleDesc->mFormat->setInt32("channel-count", numChannels);
+
+ return OK;
+}
+
+status_t Parser::parseMediaData(
+ uint32_t type, size_t offset, uint64_t size) {
+ ALOGV("skipping 'mdat' chunk at offsets 0x%08lx-0x%08llx.",
+ mBufferPos + offset, mBufferPos + size);
+
+ sp<ABuffer> buffer = new ABuffer(size - offset);
+ memcpy(buffer->data(), mBuffer->data() + offset, size - offset);
+
+ mMediaData.push();
+ MediaDataInfo *info = &mMediaData.editItemAt(mMediaData.size() - 1);
+ info->mBuffer = buffer;
+ info->mOffset = mBufferPos + offset;
+
+ if (mMediaData.size() > 10) {
+ ALOGI("suspending for now.");
+ mSuspended = true;
+ }
+
+ return OK;
+}
+
+status_t Parser::parseTrackExtends(
+ uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 24 > size) {
+ return -EINVAL;
+ }
+
+ if (readU32(offset) != 0) {
+ return -EINVAL;
+ }
+
+ uint32_t trackID = readU32(offset + 4);
+
+ TrackInfo *info = editTrack(trackID, true /* createIfNecessary */);
+ info->mDefaultSampleDescriptionIndex = readU32(offset + 8);
+ info->mDefaultSampleDuration = readU32(offset + 12);
+ info->mDefaultSampleSize = readU32(offset + 16);
+ info->mDefaultSampleFlags = readU32(offset + 20);
+
+ return OK;
+}
+
+Parser::TrackInfo *Parser::editTrack(
+ uint32_t trackID, bool createIfNecessary) {
+ ssize_t i = mTracks.indexOfKey(trackID);
+
+ if (i >= 0) {
+ return &mTracks.editValueAt(i);
+ }
+
+ if (!createIfNecessary) {
+ return NULL;
+ }
+
+ TrackInfo info;
+ info.mTrackID = trackID;
+ info.mFlags = 0;
+ info.mDuration = 0xffffffff;
+ info.mMediaTimeScale = 0;
+ info.mMediaHandlerType = 0;
+ info.mDefaultSampleDescriptionIndex = 0;
+ info.mDefaultSampleDuration = 0;
+ info.mDefaultSampleSize = 0;
+ info.mDefaultSampleFlags = 0;
+
+ info.mDecodingTime = 0;
+
+ mTracks.add(trackID, info);
+ return &mTracks.editValueAt(mTracks.indexOfKey(trackID));
+}
+
+status_t Parser::parseTrackFragmentHeader(
+ uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 8 > size) {
+ return -EINVAL;
+ }
+
+ uint32_t flags = readU32(offset);
+
+ if (flags & 0xff000000) {
+ return -EINVAL;
+ }
+
+ mTrackFragmentHeaderInfo.mFlags = flags;
+
+ mTrackFragmentHeaderInfo.mTrackID = readU32(offset + 4);
+ offset += 8;
+
+ if (flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent) {
+ if (offset + 8 > size) {
+ return -EINVAL;
+ }
+
+ mTrackFragmentHeaderInfo.mBaseDataOffset = readU64(offset);
+ offset += 8;
+ }
+
+ if (flags & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) {
+ if (offset + 4 > size) {
+ return -EINVAL;
+ }
+
+ mTrackFragmentHeaderInfo.mSampleDescriptionIndex = readU32(offset);
+ offset += 4;
+ }
+
+ if (flags & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
+ if (offset + 4 > size) {
+ return -EINVAL;
+ }
+
+ mTrackFragmentHeaderInfo.mDefaultSampleDuration = readU32(offset);
+ offset += 4;
+ }
+
+ if (flags & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
+ if (offset + 4 > size) {
+ return -EINVAL;
+ }
+
+ mTrackFragmentHeaderInfo.mDefaultSampleSize = readU32(offset);
+ offset += 4;
+ }
+
+ if (flags & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
+ if (offset + 4 > size) {
+ return -EINVAL;
+ }
+
+ mTrackFragmentHeaderInfo.mDefaultSampleFlags = readU32(offset);
+ offset += 4;
+ }
+
+ if (!(flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent)) {
+ CHECK(!mStack.isEmpty());
+
+ // This should point to the start of the data inside the 'mdat' box
+ // following the current 'moof' box.
+
+ mTrackFragmentHeaderInfo.mBaseDataOffset =
+ mBufferPos + mStack.itemAt(mStack.size() - 1).mBytesRemaining + 8;
+ }
+
+ mTrackFragmentHeaderInfo.mDataOffset =
+ mTrackFragmentHeaderInfo.mBaseDataOffset;
+
+ TrackInfo *trackInfo = editTrack(mTrackFragmentHeaderInfo.mTrackID);
+
+ if (trackInfo->mFragments.empty()
+ || (*trackInfo->mFragments.begin())->complete()) {
+ trackInfo->mFragments.push_back(new DynamicTrackFragment);
+ }
+
+ return OK;
+}
+
+status_t Parser::parseTrackFragmentRun(
+ uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 8 > size) {
+ return -EINVAL;
+ }
+
+ enum {
+ kDataOffsetPresent = 0x01,
+ kFirstSampleFlagsPresent = 0x04,
+ kSampleDurationPresent = 0x100,
+ kSampleSizePresent = 0x200,
+ kSampleFlagsPresent = 0x400,
+ kSampleCompositionTimeOffsetPresent = 0x800,
+ };
+
+ uint32_t flags = readU32(offset);
+
+ if (flags & 0xff000000) {
+ return -EINVAL;
+ }
+
+ if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) {
+ // These two shall not be used together.
+ return -EINVAL;
+ }
+
+ uint32_t sampleCount = readU32(offset + 4);
+ offset += 8;
+
+ uint64_t dataOffset = mTrackFragmentHeaderInfo.mDataOffset;
+
+ uint32_t firstSampleFlags = 0;
+
+ if (flags & kDataOffsetPresent) {
+ if (offset + 4 > size) {
+ return -EINVAL;
+ }
+
+ int32_t dataOffsetDelta = (int32_t)readU32(offset);
+
+ dataOffset = mTrackFragmentHeaderInfo.mBaseDataOffset + dataOffsetDelta;
+
+ offset += 4;
+ }
+
+ if (flags & kFirstSampleFlagsPresent) {
+ if (offset + 4 > size) {
+ return -EINVAL;
+ }
+
+ firstSampleFlags = readU32(offset);
+ offset += 4;
+ }
+
+ TrackInfo *info = editTrack(mTrackFragmentHeaderInfo.mTrackID);
+
+ if (info == NULL) {
+ return -EINVAL;
+ }
+
+ uint32_t sampleDuration = 0, sampleSize = 0, sampleFlags = 0,
+ sampleCtsOffset = 0;
+
+ size_t bytesPerSample = 0;
+ if (flags & kSampleDurationPresent) {
+ bytesPerSample += 4;
+ } else if (mTrackFragmentHeaderInfo.mFlags
+ & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
+ sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration;
+ } else {
+ sampleDuration = info->mDefaultSampleDuration;
+ }
+
+ if (flags & kSampleSizePresent) {
+ bytesPerSample += 4;
+ } else if (mTrackFragmentHeaderInfo.mFlags
+ & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
+ sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
+ } else {
+ sampleSize = info->mDefaultSampleSize;
+ }
+
+ if (flags & kSampleFlagsPresent) {
+ bytesPerSample += 4;
+ } else if (mTrackFragmentHeaderInfo.mFlags
+ & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
+ sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
+ } else {
+ sampleFlags = info->mDefaultSampleFlags;
+ }
+
+ if (flags & kSampleCompositionTimeOffsetPresent) {
+ bytesPerSample += 4;
+ } else {
+ sampleCtsOffset = 0;
+ }
+
+ if (offset + sampleCount * bytesPerSample > size) {
+ return -EINVAL;
+ }
+
+ uint32_t sampleDescIndex =
+ (mTrackFragmentHeaderInfo.mFlags
+ & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent)
+ ? mTrackFragmentHeaderInfo.mSampleDescriptionIndex
+ : info->mDefaultSampleDescriptionIndex;
+
+ for (uint32_t i = 0; i < sampleCount; ++i) {
+ if (flags & kSampleDurationPresent) {
+ sampleDuration = readU32(offset);
+ offset += 4;
+ }
+
+ if (flags & kSampleSizePresent) {
+ sampleSize = readU32(offset);
+ offset += 4;
+ }
+
+ if (flags & kSampleFlagsPresent) {
+ sampleFlags = readU32(offset);
+ offset += 4;
+ }
+
+ if (flags & kSampleCompositionTimeOffsetPresent) {
+ sampleCtsOffset = readU32(offset);
+ offset += 4;
+ }
+
+ ALOGV("adding sample at offset 0x%08llx, size %u, duration %u, "
+ "sampleDescIndex=%u, flags 0x%08x",
+ dataOffset, sampleSize, sampleDuration,
+ sampleDescIndex,
+ (flags & kFirstSampleFlagsPresent) && i == 0
+ ? firstSampleFlags : sampleFlags);
+
+ const sp<TrackFragment> &fragment = *--info->mFragments.end();
+
+ uint32_t decodingTime = info->mDecodingTime;
+ info->mDecodingTime += sampleDuration;
+ uint32_t presentationTime = decodingTime + sampleCtsOffset;
+
+ static_cast<DynamicTrackFragment *>(
+ fragment.get())->addSample(
+ dataOffset,
+ sampleSize,
+ presentationTime,
+ sampleDescIndex,
+ ((flags & kFirstSampleFlagsPresent) && i == 0)
+ ? firstSampleFlags : sampleFlags);
+
+ dataOffset += sampleSize;
+ }
+
+ mTrackFragmentHeaderInfo.mDataOffset = dataOffset;
+
+ return OK;
+}
+
+void Parser::copyBuffer(
+ sp<ABuffer> *dst, size_t offset, uint64_t size, size_t extra) const {
+ sp<ABuffer> buf = new ABuffer(size + extra);
+ memcpy(buf->data(), mBuffer->data() + offset, size);
+
+ *dst = buf;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/mp4/Parser.h b/media/libmediaplayerservice/nuplayer/mp4/Parser.h
new file mode 100644
index 0000000..50c96bb
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/Parser.h
@@ -0,0 +1,252 @@
+/*
+ * 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.
+ */
+
+#ifndef PARSER_H_
+
+#define PARSER_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct Parser : public AHandler {
+ struct Source : public RefBase {
+ Source() {}
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
+
+ protected:
+ virtual ~Source() {}
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(Source);
+ };
+
+ Parser();
+
+ void start(const char *filename);
+ void start(const sp<Source> &source);
+
+ sp<AMessage> getFormat(bool audio);
+ status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+ virtual ~Parser();
+
+private:
+ enum {
+ kWhatStart,
+ kWhatProceed,
+ kWhatReadMore,
+ kWhatGetFormat,
+ kWhatDequeueAccessUnit,
+ };
+
+ struct TrackFragment;
+ struct DynamicTrackFragment;
+ struct StaticTrackFragment;
+
+ struct DispatchEntry {
+ uint32_t mType;
+ uint32_t mParentType;
+ status_t (Parser::*mHandler)(uint32_t, size_t, uint64_t);
+ };
+
+ struct Container {
+ uint64_t mBytesRemaining;
+ uint32_t mType;
+ bool mExtendsToEOF;
+ };
+
+ struct SampleDescription {
+ uint32_t mType;
+ uint16_t mDataRefIndex;
+
+ sp<AMessage> mFormat;
+ };
+
+ struct SampleInfo {
+ off64_t mOffset;
+ size_t mSize;
+ uint32_t mPresentationTime;
+ size_t mSampleDescIndex;
+ uint32_t mFlags;
+ };
+
+ struct MediaDataInfo {
+ sp<ABuffer> mBuffer;
+ off64_t mOffset;
+ };
+
+ struct TrackInfo {
+ enum Flags {
+ kTrackEnabled = 0x01,
+ kTrackInMovie = 0x02,
+ kTrackInPreview = 0x04,
+ };
+
+ uint32_t mTrackID;
+ uint32_t mFlags;
+ uint32_t mDuration; // This is the duration in terms of movie timescale!
+
+ uint32_t mMediaTimeScale;
+
+ uint32_t mMediaHandlerType;
+ Vector<SampleDescription> mSampleDescs;
+
+ // from track extends:
+ uint32_t mDefaultSampleDescriptionIndex;
+ uint32_t mDefaultSampleDuration;
+ uint32_t mDefaultSampleSize;
+ uint32_t mDefaultSampleFlags;
+
+ uint32_t mDecodingTime;
+
+ sp<StaticTrackFragment> mStaticFragment;
+ List<sp<TrackFragment> > mFragments;
+ };
+
+ struct TrackFragmentHeaderInfo {
+ enum Flags {
+ kBaseDataOffsetPresent = 0x01,
+ kSampleDescriptionIndexPresent = 0x02,
+ kDefaultSampleDurationPresent = 0x08,
+ kDefaultSampleSizePresent = 0x10,
+ kDefaultSampleFlagsPresent = 0x20,
+ kDurationIsEmpty = 0x10000,
+ };
+
+ uint32_t mTrackID;
+ uint32_t mFlags;
+ uint64_t mBaseDataOffset;
+ uint32_t mSampleDescriptionIndex;
+ uint32_t mDefaultSampleDuration;
+ uint32_t mDefaultSampleSize;
+ uint32_t mDefaultSampleFlags;
+
+ uint64_t mDataOffset;
+ };
+
+ static const DispatchEntry kDispatchTable[];
+
+ sp<Source> mSource;
+ off_t mBufferPos;
+ bool mSuspended;
+ sp<ABuffer> mBuffer;
+ Vector<Container> mStack;
+ KeyedVector<uint32_t, TrackInfo> mTracks; // TrackInfo by trackID
+ Vector<MediaDataInfo> mMediaData;
+
+ uint32_t mCurrentTrackID;
+
+ TrackFragmentHeaderInfo mTrackFragmentHeaderInfo;
+
+ status_t onProceed();
+ status_t onDequeueAccessUnit(size_t trackIndex, sp<ABuffer> *accessUnit);
+
+ void enter(uint32_t type, uint64_t size);
+
+ uint16_t readU16(size_t offset);
+ uint32_t readU32(size_t offset);
+ uint64_t readU64(size_t offset);
+ void skip(off_t distance);
+ status_t need(size_t size);
+ bool fitsContainer(uint64_t size) const;
+
+ status_t parseTrackHeader(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseMediaHeader(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseMediaHandler(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseTrackExtends(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseTrackFragmentHeader(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseTrackFragmentRun(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseVisualSampleEntry(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseAudioSampleEntry(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseSampleSizes(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseCompactSampleSizes(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseSampleToChunk(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseChunkOffsets(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseChunkOffsets64(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseAVCCodecSpecificData(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseESDSCodecSpecificData(
+ uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseMediaData(
+ uint32_t type, size_t offset, uint64_t size);
+
+ TrackInfo *editTrack(uint32_t trackID, bool createIfNecessary = false);
+
+ ssize_t findTrack(bool wantAudio) const;
+
+ status_t makeAccessUnit(
+ TrackInfo *info,
+ const SampleInfo &sample,
+ const MediaDataInfo &mdatInfo,
+ sp<ABuffer> *accessUnit);
+
+ status_t getSample(
+ TrackInfo *info,
+ sp<TrackFragment> *fragment,
+ SampleInfo *sampleInfo);
+
+ static int CompareSampleLocation(
+ const SampleInfo &sample, const MediaDataInfo &mdatInfo);
+
+ void resumeIfNecessary();
+
+ void copyBuffer(
+ sp<ABuffer> *dst,
+ size_t offset, uint64_t size, size_t extra = 0) const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Parser);
+};
+
+} // namespace android
+
+#endif // PARSER_H_
+
diff --git a/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp
new file mode 100644
index 0000000..e2df468
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TrackFragment"
+#include <utils/Log.h>
+
+#include "TrackFragment.h"
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+Parser::DynamicTrackFragment::DynamicTrackFragment()
+ : mComplete(false),
+ mSampleIndex(0) {
+}
+
+Parser::DynamicTrackFragment::~DynamicTrackFragment() {
+}
+
+status_t Parser::DynamicTrackFragment::getSample(SampleInfo *info) {
+ if (mSampleIndex >= mSamples.size()) {
+ return mComplete ? ERROR_END_OF_STREAM : -EWOULDBLOCK;
+ }
+
+ *info = mSamples.itemAt(mSampleIndex);
+
+ return OK;
+}
+
+void Parser::DynamicTrackFragment::advance() {
+ ++mSampleIndex;
+}
+
+void Parser::DynamicTrackFragment::addSample(
+ off64_t dataOffset, size_t sampleSize,
+ uint32_t presentationTime,
+ size_t sampleDescIndex,
+ uint32_t flags) {
+ mSamples.push();
+ SampleInfo *sampleInfo = &mSamples.editItemAt(mSamples.size() - 1);
+
+ sampleInfo->mOffset = dataOffset;
+ sampleInfo->mSize = sampleSize;
+ sampleInfo->mPresentationTime = presentationTime;
+ sampleInfo->mSampleDescIndex = sampleDescIndex;
+ sampleInfo->mFlags = flags;
+}
+
+status_t Parser::DynamicTrackFragment::signalCompletion() {
+ mComplete = true;
+
+ return OK;
+}
+
+bool Parser::DynamicTrackFragment::complete() const {
+ return mComplete;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+Parser::StaticTrackFragment::StaticTrackFragment()
+ : mSampleIndex(0),
+ mSampleCount(0),
+ mSampleToChunkIndex(-1),
+ mSampleToChunkRemaining(0),
+ mPrevChunkIndex(0xffffffff),
+ mNextSampleOffset(0) {
+}
+
+Parser::StaticTrackFragment::~StaticTrackFragment() {
+}
+
+status_t Parser::StaticTrackFragment::getSample(SampleInfo *info) {
+ if (mSampleIndex >= mSampleCount) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ *info = mSampleInfo;
+
+ ALOGV("returning sample %d at [0x%08llx, 0x%08llx)",
+ mSampleIndex,
+ info->mOffset, info->mOffset + info->mSize);
+
+ return OK;
+}
+
+void Parser::StaticTrackFragment::updateSampleInfo() {
+ if (mSampleIndex >= mSampleCount) {
+ return;
+ }
+
+ if (mSampleSizes != NULL) {
+ uint32_t defaultSampleSize = U32_AT(mSampleSizes->data() + 4);
+ if (defaultSampleSize > 0) {
+ mSampleInfo.mSize = defaultSampleSize;
+ } else {
+ mSampleInfo.mSize= U32_AT(mSampleSizes->data() + 12 + 4 * mSampleIndex);
+ }
+ } else {
+ CHECK(mCompactSampleSizes != NULL);
+
+ uint32_t fieldSize = U32_AT(mCompactSampleSizes->data() + 4);
+
+ switch (fieldSize) {
+ case 4:
+ {
+ unsigned byte = mCompactSampleSizes->data()[12 + mSampleIndex / 2];
+ mSampleInfo.mSize = (mSampleIndex & 1) ? byte & 0x0f : byte >> 4;
+ break;
+ }
+
+ case 8:
+ {
+ mSampleInfo.mSize = mCompactSampleSizes->data()[12 + mSampleIndex];
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ(fieldSize, 16);
+ mSampleInfo.mSize =
+ U16_AT(mCompactSampleSizes->data() + 12 + mSampleIndex * 2);
+ break;
+ }
+ }
+ }
+
+ CHECK_GT(mSampleToChunkRemaining, 0);
+
+ // The sample desc index is 1-based... XXX
+ mSampleInfo.mSampleDescIndex =
+ U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 8);
+
+ uint32_t chunkIndex =
+ U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex);
+
+ CHECK_GE(chunkIndex, 1);
+ --chunkIndex;
+
+ if (chunkIndex != mPrevChunkIndex) {
+ mPrevChunkIndex = chunkIndex;
+
+ if (mChunkOffsets != NULL) {
+ uint32_t entryCount = U32_AT(mChunkOffsets->data() + 4);
+
+ if (chunkIndex >= entryCount) {
+ mSampleIndex = mSampleCount;
+ return;
+ }
+
+ mNextSampleOffset =
+ U32_AT(mChunkOffsets->data() + 8 + 4 * chunkIndex);
+ } else {
+ CHECK(mChunkOffsets64 != NULL);
+
+ uint32_t entryCount = U32_AT(mChunkOffsets64->data() + 4);
+
+ if (chunkIndex >= entryCount) {
+ mSampleIndex = mSampleCount;
+ return;
+ }
+
+ mNextSampleOffset =
+ U64_AT(mChunkOffsets64->data() + 8 + 8 * chunkIndex);
+ }
+ }
+
+ mSampleInfo.mOffset = mNextSampleOffset;
+
+ mSampleInfo.mPresentationTime = 0;
+ mSampleInfo.mFlags = 0;
+}
+
+void Parser::StaticTrackFragment::advance() {
+ mNextSampleOffset += mSampleInfo.mSize;
+
+ ++mSampleIndex;
+ if (--mSampleToChunkRemaining == 0) {
+ uint32_t entryCount = U32_AT(mSampleToChunk->data() + 4);
+
+ if ((uint32_t)(mSampleToChunkIndex + 1) == entryCount) {
+ mSampleIndex = mSampleCount; // EOS.
+ return;
+ }
+
+ ++mSampleToChunkIndex;
+ mSampleToChunkRemaining =
+ U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
+ }
+
+ updateSampleInfo();
+}
+
+static void setU32At(uint8_t *ptr, uint32_t x) {
+ ptr[0] = x >> 24;
+ ptr[1] = (x >> 16) & 0xff;
+ ptr[2] = (x >> 8) & 0xff;
+ ptr[3] = x & 0xff;
+}
+
+void Parser::StaticTrackFragment::fixSampleToChunkTableIfNecessary() {
+ uint32_t entryCount = U32_AT(mSampleToChunk->data() + 4);
+ uint32_t totalSamples = 0;
+ for (uint32_t i = 0; i < entryCount; ++i) {
+ totalSamples += U32_AT(mSampleToChunk->data() + 8 + 12 * i + 4);
+ }
+
+ if (totalSamples < mSampleCount) {
+ // Some samples are not accounted for in the sample-to-chunk
+ // data. Fabricate an extra chunk adjacent to the last one
+ // in the table with the same sample desription index.
+
+ ALOGW("Faking an extra sample-to-chunk entry for %d samples.",
+ mSampleCount - totalSamples);
+
+ uint32_t lastChunkIndex =
+ U32_AT(mSampleToChunk->data() + 8 + 12 * (entryCount - 1));
+
+ uint32_t lastSampleDescriptionIndex =
+ U32_AT(mSampleToChunk->data() + 8 + 12 * (entryCount - 1) + 8);
+
+ uint8_t *ptr = mSampleToChunk->data() + 8 + 12 * entryCount;
+
+ setU32At(ptr, lastChunkIndex + 1);
+ setU32At(ptr + 4, mSampleCount - totalSamples);
+ setU32At(ptr + 8, lastSampleDescriptionIndex);
+ setU32At(mSampleToChunk->data() + 4, entryCount + 1);
+ }
+}
+
+status_t Parser::StaticTrackFragment::signalCompletion() {
+ fixSampleToChunkTableIfNecessary();
+
+ mSampleToChunkIndex = 0;
+
+ mSampleToChunkRemaining =
+ U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
+
+ updateSampleInfo();
+
+ return OK;
+}
+
+bool Parser::StaticTrackFragment::complete() const {
+ return true;
+}
+
+status_t Parser::StaticTrackFragment::parseSampleSizes(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 12 > size) {
+ return ERROR_MALFORMED;
+ }
+
+ if (parser->readU32(offset) != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t sampleSize = parser->readU32(offset + 4);
+ uint32_t sampleCount = parser->readU32(offset + 8);
+
+ if (sampleSize == 0 && offset + 12 + sampleCount * 4 != size) {
+ return ERROR_MALFORMED;
+ }
+
+ parser->copyBuffer(&mSampleSizes, offset, size);
+
+ mSampleCount = sampleCount;
+
+ return OK;
+}
+
+status_t Parser::StaticTrackFragment::parseCompactSampleSizes(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 12 > size) {
+ return ERROR_MALFORMED;
+ }
+
+ if (parser->readU32(offset) != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t fieldSize = parser->readU32(offset + 4);
+
+ if (fieldSize != 4 && fieldSize != 8 && fieldSize != 16) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t sampleCount = parser->readU32(offset + 8);
+
+ if (offset + 12 + (sampleCount * fieldSize + 4) / 8 != size) {
+ return ERROR_MALFORMED;
+ }
+
+ parser->copyBuffer(&mCompactSampleSizes, offset, size);
+
+ mSampleCount = sampleCount;
+
+ return OK;
+}
+
+status_t Parser::StaticTrackFragment::parseSampleToChunk(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 8 > size) {
+ return ERROR_MALFORMED;
+ }
+
+ if (parser->readU32(offset) != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t entryCount = parser->readU32(offset + 4);
+
+ if (entryCount == 0 || offset + 8 + entryCount * 12 != size) {
+ return ERROR_MALFORMED;
+ }
+
+ parser->copyBuffer(&mSampleToChunk, offset, size, 12 /* extra */);
+
+ return OK;
+}
+
+status_t Parser::StaticTrackFragment::parseChunkOffsets(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 8 > size) {
+ return ERROR_MALFORMED;
+ }
+
+ if (parser->readU32(offset) != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t entryCount = parser->readU32(offset + 4);
+
+ if (offset + 8 + entryCount * 4 != size) {
+ return ERROR_MALFORMED;
+ }
+
+ parser->copyBuffer(&mChunkOffsets, offset, size);
+
+ return OK;
+}
+
+status_t Parser::StaticTrackFragment::parseChunkOffsets64(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+ if (offset + 8 > size) {
+ return ERROR_MALFORMED;
+ }
+
+ if (parser->readU32(offset) != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t entryCount = parser->readU32(offset + 4);
+
+ if (offset + 8 + entryCount * 8 != size) {
+ return ERROR_MALFORMED;
+ }
+
+ parser->copyBuffer(&mChunkOffsets64, offset, size);
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h
new file mode 100644
index 0000000..e5945ac
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#ifndef TRACK_FRAGMENT_H_
+
+#define TRACK_FRAGMENT_H_
+
+#include "Parser.h"
+
+namespace android {
+
+struct Parser::TrackFragment : public RefBase {
+ TrackFragment() {}
+
+ virtual status_t getSample(SampleInfo *info) = 0;
+ virtual void advance() = 0;
+
+ virtual status_t signalCompletion() = 0;
+ virtual bool complete() const = 0;
+
+protected:
+ virtual ~TrackFragment() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(TrackFragment);
+};
+
+struct Parser::DynamicTrackFragment : public Parser::TrackFragment {
+ DynamicTrackFragment();
+
+ virtual status_t getSample(SampleInfo *info);
+ virtual void advance();
+
+ void addSample(
+ off64_t dataOffset, size_t sampleSize,
+ uint32_t presentationTime,
+ size_t sampleDescIndex,
+ uint32_t flags);
+
+ // No more samples will be added to this fragment.
+ virtual status_t signalCompletion();
+
+ virtual bool complete() const;
+
+protected:
+ virtual ~DynamicTrackFragment();
+
+private:
+ bool mComplete;
+ size_t mSampleIndex;
+ Vector<SampleInfo> mSamples;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DynamicTrackFragment);
+};
+
+struct Parser::StaticTrackFragment : public Parser::TrackFragment {
+ StaticTrackFragment();
+
+ virtual status_t getSample(SampleInfo *info);
+ virtual void advance();
+
+ virtual status_t signalCompletion();
+ virtual bool complete() const;
+
+ status_t parseSampleSizes(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseCompactSampleSizes(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseSampleToChunk(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseChunkOffsets(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+ status_t parseChunkOffsets64(
+ Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+protected:
+ virtual ~StaticTrackFragment();
+
+private:
+ size_t mSampleIndex;
+ size_t mSampleCount;
+
+ SampleInfo mSampleInfo;
+
+ sp<ABuffer> mSampleSizes;
+ sp<ABuffer> mCompactSampleSizes;
+
+ sp<ABuffer> mSampleToChunk;
+ ssize_t mSampleToChunkIndex;
+ size_t mSampleToChunkRemaining;
+
+ sp<ABuffer> mChunkOffsets;
+ sp<ABuffer> mChunkOffsets64;
+ uint32_t mPrevChunkIndex;
+ uint64_t mNextSampleOffset;
+
+ void updateSampleInfo();
+ void fixSampleToChunkTableIfNecessary();
+
+ DISALLOW_EVIL_CONSTRUCTORS(StaticTrackFragment);
+};
+
+} // namespace android
+
+#endif // TRACK_FRAGMENT_H_