summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/LoadedApk.java7
-rw-r--r--core/java/android/provider/Calendar.java2
-rw-r--r--core/res/res/layout/preference.xml17
-rw-r--r--core/res/res/layout/preference_child.xml17
-rw-r--r--core/res/res/layout/preference_dialog.xml53
-rw-r--r--core/res/res/layout/preference_widget_checkbox.xml56
-rw-r--r--core/res/res/values/styles.xml10
-rw-r--r--docs/html/sdk/eclipse-adt.jd44
-rw-r--r--docs/html/sdk/sdk_toc.cs5
-rw-r--r--media/libstagefright/Android.mk2
-rw-r--r--media/libstagefright/MP3Extractor.cpp261
-rw-r--r--media/libstagefright/VBRISeeker.cpp164
-rw-r--r--media/libstagefright/XINGSeeker.cpp223
-rw-r--r--media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp7
-rw-r--r--media/libstagefright/include/MP3Extractor.h9
-rw-r--r--media/libstagefright/include/MP3Seeker.h46
-rw-r--r--media/libstagefright/include/VBRISeeker.h50
-rw-r--r--media/libstagefright/include/XINGSeeker.h50
18 files changed, 700 insertions, 323 deletions
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 7f24d27..03a98d1 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -35,6 +35,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
+import android.os.StrictMode;
import android.util.AndroidRuntimeException;
import android.util.Slog;
@@ -285,10 +286,16 @@ final class LoadedApk {
if (ActivityThread.localLOGV)
Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + mLibDir);
+ // Temporarily disable logging of disk reads on the Looper thread
+ // as this is early and necessary.
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+
mClassLoader =
ApplicationLoaders.getDefault().getClassLoader(
zip, mLibDir, mBaseClassLoader);
initializeJavaContextClassLoader();
+
+ StrictMode.setThreadPolicy(oldPolicy);
} else {
if (mBaseClassLoader == null) {
mClassLoader = ClassLoader.getSystemClassLoader();
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 88ce0f0..c007605 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -861,6 +861,8 @@ public final class Calendar {
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
+ Events.SYNC_ADAPTER_DATA);
Entity entity = new Entity(cv);
Cursor subCursor;
diff --git a/core/res/res/layout/preference.xml b/core/res/res/layout/preference.xml
index ba0e225..46c3e9c 100644
--- a/core/res/res/layout/preference.xml
+++ b/core/res/res/layout/preference.xml
@@ -24,9 +24,13 @@
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">
- <View
- android:layout_width="@dimen/preference_widget_width"
- android:layout_height="match_parent" />
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@+android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/preference_widget_width"
+ android:gravity="center"
+ android:orientation="vertical" />
<RelativeLayout
android:layout_width="wrap_content"
@@ -54,12 +58,5 @@
android:maxLines="4" />
</RelativeLayout>
-
- <!-- Preference should place its actual preference widget here. -->
- <LinearLayout android:id="@+android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:orientation="vertical" />
</LinearLayout>
diff --git a/core/res/res/layout/preference_child.xml b/core/res/res/layout/preference_child.xml
index d6f1182..713aa17 100644
--- a/core/res/res/layout/preference_child.xml
+++ b/core/res/res/layout/preference_child.xml
@@ -22,9 +22,13 @@
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">
- <View
- android:layout_width="@dimen/preference_widget_width"
- android:layout_height="match_parent" />
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@+android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/preference_widget_width"
+ android:gravity="center"
+ android:orientation="vertical" />
<RelativeLayout
android:layout_width="wrap_content"
@@ -54,11 +58,4 @@
</RelativeLayout>
- <!-- Preference should place its actual preference widget here. -->
- <LinearLayout android:id="@+android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:orientation="vertical" />
-
</LinearLayout>
diff --git a/core/res/res/layout/preference_dialog.xml b/core/res/res/layout/preference_dialog.xml
index 035b411..9e988ad 100644
--- a/core/res/res/layout/preference_dialog.xml
+++ b/core/res/res/layout/preference_dialog.xml
@@ -15,52 +15,9 @@
-->
<!-- Layout used by DialogPreference in a PreferenceActivity. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical"
- android:paddingRight="?android:attr/scrollbarSize">
-
- <!-- Preference should place its actual preference widget here. -->
- <LinearLayout android:id="@+android:id/widget_frame"
- android:layout_width="@dimen/preference_widget_width"
- android:layout_height="match_parent"
- android:gravity="center"
- android:orientation="horizontal" >
- <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:background="@drawable/btn_circle"
- android:src="@drawable/ic_btn_round_more" />
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="6dip"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_weight="1">
-
- <TextView android:id="@+android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal" />
-
- <TextView android:id="@+android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_alignLeft="@android:id/title"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="4" />
-
- </RelativeLayout>
-</LinearLayout>
-
+ android:layout_gravity="center"
+ android:background="@drawable/btn_circle"
+ android:src="@drawable/ic_btn_round_more" />
diff --git a/core/res/res/layout/preference_widget_checkbox.xml b/core/res/res/layout/preference_widget_checkbox.xml
index 1ca4393..33a43f4 100644
--- a/core/res/res/layout/preference_widget_checkbox.xml
+++ b/core/res/res/layout/preference_widget_checkbox.xml
@@ -16,54 +16,10 @@
<!-- Layout used by CheckBoxPreference for the checkbox style. This is inflated
inside android.R.layout.preference. -->
-<!-- Layout used by DialogPreference in a PreferenceActivity. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/checkbox"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical"
- android:paddingRight="?android:attr/scrollbarSize">
-
- <!-- Preference should place its actual preference widget here. -->
- <LinearLayout android:id="@+android:id/widget_frame"
- android:layout_width="@dimen/preference_widget_width"
- android:layout_height="match_parent"
- android:gravity="center"
- android:orientation="horizontal" >
- <CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+android:id/checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:focusable="false"
- android:clickable="false" />
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="6dip"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_weight="1">
-
- <TextView android:id="@+android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal" />
-
- <TextView android:id="@+android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_alignLeft="@android:id/title"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="4" />
-
- </RelativeLayout>
-</LinearLayout>
-
+ android:layout_gravity="center"
+ android:focusable="false"
+ android:clickable="false" />
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 1fd2565..f63805b 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -818,18 +818,18 @@
<item name="android:shouldDisableView">false</item>
<item name="android:selectable">false</item>
</style>
-
+
<style name="Preference.CheckBoxPreference">
- <item name="android:layout">@android:layout/preference_widget_checkbox</item>
+ <item name="android:widgetLayout">@android:layout/preference_widget_checkbox</item>
</style>
-
+
<style name="Preference.PreferenceScreen">
</style>
<style name="Preference.DialogPreference">
<item name="android:positiveButtonText">@android:string/ok</item>
<item name="android:negativeButtonText">@android:string/cancel</item>
- <item name="android:layout">@android:layout/preference_dialog</item>
+ <item name="android:widgetLayout">@android:layout/preference_dialog</item>
</style>
<style name="Preference.DialogPreference.YesNoPreference">
@@ -845,7 +845,7 @@
<item name="android:ringtoneType">ringtone</item>
<item name="android:showSilent">true</item>
<item name="android:showDefault">true</item>
- <item name="android:layout">@android:layout/preference_dialog</item>
+ <item name="android:widgetLayout">@android:layout/preference_dialog</item>
</style>
<!-- Other Misc Styles -->
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 632fa4b..350f2b0 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -103,12 +103,54 @@ padding: .25em 1em;
<div class="toggleable opened">
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
-ADT 0.9.9</a> <em>(September 2010)</em>
+ADT 8.0</a> <em>(November 2010)</em>
<div class="toggleme">
+<dl>
+
+<dt>Dependencies:</dt>
+
+<p><p>ADT 8.0 is designed for use with SDK Tools r8. If you haven't
+already installed SDK Tools r8 into your SDK, use the Android SDK and AVD Manager to do
+so.</p></dd>
+<dt>General notes:</dt>
+<dd>
+<ul>
+ <li>New version number scheme that follows the SDK Tools revision number. The major version
+number for your ADT plugin should now always match the revision number of your SDK Tools. For
+example, ADT 8.x is for SDK Tools r8.</li>
+ <li>Support for true debug build. You no longer need to change the value of the
+ <code>debuggable</code> attribute in the Android Manifest.
+ <p>Incremental builds automatically insert <code>debuggable="true"</code>, but if you perform
+ "export signed/unsigned application package", ADT does <em>not</em> insert it.
+ If you manually set <code>debuggable="true"</code> in the manifest file, then release builds will
+ actually create a debug build (it does not remove it if you placed it there).</p></li>
+ <li>Automatic <a href="{@docRoot}guide/developing/tools/proguard.html">Proguard</a> support in
+release builds. For it to work, you need to have a <code>proguard.config</code>
+ property in the <code>default.properties</code> file that points to a proguard config file.</li>
+ <li>Completely rewritten Visual Layout Editor. (This is still a work in progress.) Now includes:
+ <ul>
+ <li>Full drag and drop from palette to layout for all Layout classes.</li>
+ <li>Move widgets inside a Layout view, from one Layout view to another and from one layout file to another.</li>
+ <li>Contextual menu with enum/flag type properties.</li>
+ <li>New zoom controls.</li>
+ </ul></li>
+ <li>New HierarchyViewer plug-in integrated in Eclipse.</li>
+ <li>Android launch configurations don't recompile the whole workspace on launch anymore.</li>
+ <li><code>android.jar</code> source and javadoc location can now be configured.</li>
</ul>
</dd>
+</dl>
+ </div>
+</div>
+
+
+<div class="toggleable closed">
+ <a href="#" onclick="return toggleDiv(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
+ADT 0.9.9</a> <em>(September 2010)</em>
+ <div class="toggleme">
<dl>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index ecc69c2..7ccb7a0 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -93,7 +93,7 @@
<span style="display:none" class="zh-TW"></span>
</h2>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 0.9.9
+ <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 8.0
<span style="display:none" class="de"></span>
<span style="display:none" class="es"></span>
<span style="display:none" class="fr"></span>
@@ -115,7 +115,8 @@
<span style="display:none" class="zh-TW"></span>
</h2>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Download the Android NDK, r5</a></li>
+ <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Download the Android NDK, r5</a>
+ <span class="new">new!</span></li>
<li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li>
</ul>
</li>
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 562eb60..1df0bed 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -44,8 +44,10 @@ LOCAL_SRC_FILES:= \
TimeSource.cpp \
TimedEventQueue.cpp \
Utils.cpp \
+ VBRISeeker.cpp \
WAVExtractor.cpp \
WVMExtractor.cpp \
+ XINGSeeker.cpp \
avc_utils.cpp \
string.cpp
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 82c0426..84ced8f 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -21,6 +21,8 @@
#include "include/MP3Extractor.h"
#include "include/ID3.h"
+#include "include/VBRISeeker.h"
+#include "include/XINGSeeker.h"
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
@@ -42,10 +44,11 @@ namespace android {
// Yes ... there are things that must indeed match...
static const uint32_t kMask = 0xfffe0cc0;
-static bool get_mp3_frame_size(
+// static
+bool MP3Extractor::get_mp3_frame_size(
uint32_t header, size_t *frame_size,
- int *out_sampling_rate = NULL, int *out_channels = NULL,
- int *out_bitrate = NULL) {
+ int *out_sampling_rate, int *out_channels,
+ int *out_bitrate) {
*frame_size = 0;
if (out_sampling_rate) {
@@ -178,136 +181,13 @@ static bool get_mp3_frame_size(
return true;
}
-static bool parse_xing_header(
- const sp<DataSource> &source, off_t first_frame_pos,
- int32_t *frame_number = NULL, int32_t *byte_number = NULL,
- char *table_of_contents = NULL, int32_t *quality_indicator = NULL,
- int64_t *duration = NULL) {
-
- if (frame_number) {
- *frame_number = 0;
- }
- if (byte_number) {
- *byte_number = 0;
- }
- if (table_of_contents) {
- table_of_contents[0] = 0;
- }
- if (quality_indicator) {
- *quality_indicator = 0;
- }
- if (duration) {
- *duration = 0;
- }
-
- uint8_t buffer[4];
- int offset = first_frame_pos;
- if (source->readAt(offset, &buffer, 4) < 4) { // get header
- return false;
- }
- offset += 4;
-
- uint8_t id, layer, sr_index, mode;
- layer = (buffer[1] >> 1) & 3;
- id = (buffer[1] >> 3) & 3;
- sr_index = (buffer[2] >> 2) & 3;
- mode = (buffer[3] >> 6) & 3;
- if (layer == 0) {
- return false;
- }
- if (id == 1) {
- return false;
- }
- if (sr_index == 3) {
- return false;
- }
- // determine offset of XING header
- if(id&1) { // mpeg1
- if (mode != 3) offset += 32;
- else offset += 17;
- } else { // mpeg2
- if (mode != 3) offset += 17;
- else offset += 9;
- }
-
- if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
- return false;
- }
- offset += 4;
- // Check XING ID
- if ((buffer[0] != 'X') || (buffer[1] != 'i')
- || (buffer[2] != 'n') || (buffer[3] != 'g')) {
- if ((buffer[0] != 'I') || (buffer[1] != 'n')
- || (buffer[2] != 'f') || (buffer[3] != 'o')) {
- return false;
- }
- }
-
- if (source->readAt(offset, &buffer, 4) < 4) { // flags
- return false;
- }
- offset += 4;
- uint32_t flags = U32_AT(buffer);
-
- if (flags & 0x0001) { // Frames field is present
- if (source->readAt(offset, buffer, 4) < 4) {
- return false;
- }
- if (frame_number) {
- *frame_number = U32_AT(buffer);
- }
- int32_t frame = U32_AT(buffer);
- // Samples per Frame: 1. index = MPEG Version ID, 2. index = Layer
- const int samplesPerFrames[2][3] =
- {
- { 384, 1152, 576 }, // MPEG 2, 2.5: layer1, layer2, layer3
- { 384, 1152, 1152 }, // MPEG 1: layer1, layer2, layer3
- };
- // sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
- const int samplingRates[4][3] =
- {
- { 11025, 12000, 8000, }, // MPEG 2.5
- { 0, 0, 0, }, // reserved
- { 22050, 24000, 16000, }, // MPEG 2
- { 44100, 48000, 32000, } // MPEG 1
- };
- if (duration) {
- *duration = (int64_t)frame * samplesPerFrames[id&1][3-layer] * 1000000LL
- / samplingRates[id][sr_index];
- }
- offset += 4;
- }
- if (flags & 0x0002) { // Bytes field is present
- if (byte_number) {
- if (source->readAt(offset, buffer, 4) < 4) {
- return false;
- }
- *byte_number = U32_AT(buffer);
- }
- offset += 4;
- }
- if (flags & 0x0004) { // TOC field is present
- if (table_of_contents) {
- if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
- return false;
- }
- }
- offset += 100;
- }
- if (flags & 0x0008) { // Quality indicator field is present
- if (quality_indicator) {
- if (source->readAt(offset, buffer, 4) < 4) {
- return false;
- }
- *quality_indicator = U32_AT(buffer);
- }
- }
- return true;
-}
-
static bool Resync(
const sp<DataSource> &source, uint32_t match_header,
- off_t *inout_pos, uint32_t *out_header) {
+ off_t *inout_pos, off_t *post_id3_pos, uint32_t *out_header) {
+ if (post_id3_pos != NULL) {
+ *post_id3_pos = 0;
+ }
+
if (*inout_pos == 0) {
// Skip an optional ID3 header if syncing at the very beginning
// of the datasource.
@@ -340,6 +220,10 @@ static bool Resync(
LOGV("skipped ID3 tag, new starting offset is %ld (0x%08lx)",
*inout_pos, *inout_pos);
}
+
+ if (post_id3_pos != NULL) {
+ *post_id3_pos = *inout_pos;
+ }
}
off_t pos = *inout_pos;
@@ -365,8 +249,9 @@ static bool Resync(
size_t frame_size;
int sample_rate, num_channels, bitrate;
- if (!get_mp3_frame_size(header, &frame_size,
- &sample_rate, &num_channels, &bitrate)) {
+ if (!MP3Extractor::get_mp3_frame_size(
+ header, &frame_size,
+ &sample_rate, &num_channels, &bitrate)) {
++pos;
continue;
}
@@ -396,7 +281,8 @@ static bool Resync(
}
size_t test_frame_size;
- if (!get_mp3_frame_size(test_header, &test_frame_size)) {
+ if (!MP3Extractor::get_mp3_frame_size(
+ test_header, &test_frame_size)) {
valid = false;
break;
}
@@ -427,7 +313,7 @@ public:
MP3Source(
const sp<MetaData> &meta, const sp<DataSource> &source,
off_t first_frame_pos, uint32_t fixed_header,
- int32_t byte_number, const char *table_of_contents);
+ const sp<MP3Seeker> &seeker);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -448,9 +334,7 @@ private:
off_t mCurrentPos;
int64_t mCurrentTimeUs;
bool mStarted;
- int32_t mByteNumber; // total number of bytes in this MP3
- // TOC entries in XING header. Skip the first one since it's always 0.
- char mTableOfContents[99];
+ sp<MP3Seeker> mSeeker;
MediaBufferGroup *mGroup;
MP3Source(const MP3Source &);
@@ -462,25 +346,28 @@ MP3Extractor::MP3Extractor(
: mInitCheck(NO_INIT),
mDataSource(source),
mFirstFramePos(-1),
- mFixedHeader(0),
- mByteNumber(0) {
+ mFixedHeader(0) {
off_t pos = 0;
+ off_t post_id3_pos;
uint32_t header;
bool success;
int64_t meta_offset;
uint32_t meta_header;
+ int64_t meta_post_id3_offset;
if (meta != NULL
&& meta->findInt64("offset", &meta_offset)
- && meta->findInt32("header", (int32_t *)&meta_header)) {
+ && meta->findInt32("header", (int32_t *)&meta_header)
+ && meta->findInt64("post-id3-offset", &meta_post_id3_offset)) {
// The sniffer has already done all the hard work for us, simply
// accept its judgement.
pos = (off_t)meta_offset;
header = meta_header;
+ post_id3_pos = (off_t)meta_post_id3_offset;
success = true;
} else {
- success = Resync(mDataSource, 0, &pos, &header);
+ success = Resync(mDataSource, 0, &pos, &post_id3_pos, &header);
}
if (!success) {
@@ -505,21 +392,27 @@ MP3Extractor::MP3Extractor(
mMeta->setInt32(kKeyBitRate, bitrate * 1000);
mMeta->setInt32(kKeyChannelCount, num_channels);
- int64_t duration;
- parse_xing_header(
- mDataSource, mFirstFramePos, NULL, &mByteNumber,
- mTableOfContents, NULL, &duration);
- if (duration > 0) {
- mMeta->setInt64(kKeyDuration, duration);
- } else {
+ mSeeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
+
+ if (mSeeker == NULL) {
+ mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
+ }
+
+ int64_t durationUs;
+
+ if (mSeeker == NULL || !mSeeker->getDuration(&durationUs)) {
off_t fileSize;
if (mDataSource->getSize(&fileSize) == OK) {
- mMeta->setInt64(
- kKeyDuration,
- 8000LL * (fileSize - mFirstFramePos) / bitrate);
+ durationUs = 8000LL * (fileSize - mFirstFramePos) / bitrate;
+ } else {
+ durationUs = -1;
}
}
+ if (durationUs >= 0) {
+ mMeta->setInt64(kKeyDuration, durationUs);
+ }
+
mInitCheck = OK;
}
@@ -534,7 +427,7 @@ sp<MediaSource> MP3Extractor::getTrack(size_t index) {
return new MP3Source(
mMeta, mDataSource, mFirstFramePos, mFixedHeader,
- mByteNumber, mTableOfContents);
+ mSeeker);
}
sp<MetaData> MP3Extractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -550,7 +443,7 @@ sp<MetaData> MP3Extractor::getTrackMetaData(size_t index, uint32_t flags) {
MP3Source::MP3Source(
const sp<MetaData> &meta, const sp<DataSource> &source,
off_t first_frame_pos, uint32_t fixed_header,
- int32_t byte_number, const char *table_of_contents)
+ const sp<MP3Seeker> &seeker)
: mMeta(meta),
mDataSource(source),
mFirstFramePos(first_frame_pos),
@@ -558,9 +451,8 @@ MP3Source::MP3Source(
mCurrentPos(0),
mCurrentTimeUs(0),
mStarted(false),
- mByteNumber(byte_number),
+ mSeeker(seeker),
mGroup(NULL) {
- memcpy (mTableOfContents, table_of_contents, sizeof(mTableOfContents));
}
MP3Source::~MP3Source() {
@@ -607,43 +499,21 @@ status_t MP3Source::read(
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
- int32_t bitrate;
- if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
- // bitrate is in bits/sec.
- LOGI("no bitrate");
-
- return ERROR_UNSUPPORTED;
- }
-
- mCurrentTimeUs = seekTimeUs;
- // interpolate in TOC to get file seek point in bytes
- int64_t duration;
- if ((mByteNumber > 0) && (mTableOfContents[0] > 0)
- && mMeta->findInt64(kKeyDuration, &duration)) {
- float percent = (float)seekTimeUs * 100 / duration;
- float fx;
- if( percent <= 0.0f ) {
- fx = 0.0f;
- } else if( percent >= 100.0f ) {
- fx = 256.0f;
- } else {
- int a = (int)percent;
- float fa, fb;
- if ( a == 0 ) {
- fa = 0.0f;
- } else {
- fa = (float)mTableOfContents[a-1];
- }
- if ( a < 99 ) {
- fb = (float)mTableOfContents[a];
- } else {
- fb = 256.0f;
- }
- fx = fa + (fb-fa)*(percent-a);
+ int64_t actualSeekTimeUs = seekTimeUs;
+ if (mSeeker == NULL
+ || !mSeeker->getOffsetForTime(&actualSeekTimeUs, &mCurrentPos)) {
+ int32_t bitrate;
+ if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
+ // bitrate is in bits/sec.
+ LOGI("no bitrate");
+
+ return ERROR_UNSUPPORTED;
}
- mCurrentPos = mFirstFramePos + (int)((1.0f/256.0f)*fx*mByteNumber);
- } else {
+
+ mCurrentTimeUs = seekTimeUs;
mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000;
+ } else {
+ mCurrentTimeUs = actualSeekTimeUs;
}
}
@@ -667,7 +537,8 @@ status_t MP3Source::read(
uint32_t header = U32_AT((const uint8_t *)buffer->data());
if ((header & kMask) == (mFixedHeader & kMask)
- && get_mp3_frame_size(header, &frame_size, NULL, NULL, &bitrate)) {
+ && MP3Extractor::get_mp3_frame_size(
+ header, &frame_size, NULL, NULL, &bitrate)) {
break;
}
@@ -675,7 +546,7 @@ status_t MP3Source::read(
LOGV("lost sync! header = 0x%08x, old header = 0x%08x\n", header, mFixedHeader);
off_t pos = mCurrentPos;
- if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
+ if (!Resync(mDataSource, mFixedHeader, &pos, NULL, NULL)) {
LOGE("Unable to resync. Signalling end of stream.");
buffer->release();
@@ -781,14 +652,16 @@ bool SniffMP3(
const sp<DataSource> &source, String8 *mimeType,
float *confidence, sp<AMessage> *meta) {
off_t pos = 0;
+ off_t post_id3_pos;
uint32_t header;
- if (!Resync(source, 0, &pos, &header)) {
+ if (!Resync(source, 0, &pos, &post_id3_pos, &header)) {
return false;
}
*meta = new AMessage;
(*meta)->setInt64("offset", pos);
(*meta)->setInt32("header", header);
+ (*meta)->setInt64("post-id3-offset", post_id3_pos);
*mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
*confidence = 0.2f;
diff --git a/media/libstagefright/VBRISeeker.cpp b/media/libstagefright/VBRISeeker.cpp
new file mode 100644
index 0000000..6608644
--- /dev/null
+++ b/media/libstagefright/VBRISeeker.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2010 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 "VBRISeeker"
+#include <utils/Log.h>
+
+#include "include/VBRISeeker.h"
+
+#include "include/MP3Extractor.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static uint32_t U24_AT(const uint8_t *ptr) {
+ return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
+}
+
+// static
+sp<VBRISeeker> VBRISeeker::CreateFromSource(
+ const sp<DataSource> &source, off_t post_id3_pos) {
+ off_t pos = post_id3_pos;
+
+ uint8_t header[4];
+ ssize_t n = source->readAt(pos, header, sizeof(header));
+ if (n < (ssize_t)sizeof(header)) {
+ return NULL;
+ }
+
+ uint32_t tmp = U32_AT(&header[0]);
+ size_t frameSize;
+ int sampleRate;
+ if (!MP3Extractor::get_mp3_frame_size(tmp, &frameSize, &sampleRate)) {
+ return NULL;
+ }
+
+ // VBRI header follows 32 bytes after the header _ends_.
+ pos += sizeof(header) + 32;
+
+ uint8_t vbriHeader[26];
+ n = source->readAt(pos, vbriHeader, sizeof(vbriHeader));
+ if (n < (ssize_t)sizeof(vbriHeader)) {
+ return NULL;
+ }
+
+ if (memcmp(vbriHeader, "VBRI", 4)) {
+ return NULL;
+ }
+
+ size_t numFrames = U32_AT(&vbriHeader[14]);
+
+ int64_t durationUs =
+ numFrames * 1000000ll * (sampleRate >= 32000 ? 1152 : 576) / sampleRate;
+
+ LOGV("duration = %.2f secs", durationUs / 1E6);
+
+ size_t numEntries = U16_AT(&vbriHeader[18]);
+ size_t entrySize = U16_AT(&vbriHeader[22]);
+ size_t scale = U16_AT(&vbriHeader[20]);
+
+ LOGV("%d entries, scale=%d, size_per_entry=%d",
+ numEntries,
+ scale,
+ entrySize);
+
+ size_t totalEntrySize = numEntries * entrySize;
+ uint8_t *buffer = new uint8_t[totalEntrySize];
+
+ n = source->readAt(pos + sizeof(vbriHeader), buffer, totalEntrySize);
+ if (n < (ssize_t)totalEntrySize) {
+ delete[] buffer;
+ buffer = NULL;
+
+ return NULL;
+ }
+
+ sp<VBRISeeker> seeker = new VBRISeeker;
+ seeker->mBasePos = post_id3_pos;
+ seeker->mDurationUs = durationUs;
+
+ off_t offset = post_id3_pos;
+ for (size_t i = 0; i < numEntries; ++i) {
+ uint32_t numBytes;
+ switch (entrySize) {
+ case 1: numBytes = buffer[i]; break;
+ case 2: numBytes = U16_AT(buffer + 2 * i); break;
+ case 3: numBytes = U24_AT(buffer + 3 * i); break;
+ default:
+ {
+ CHECK_EQ(entrySize, 4u);
+ numBytes = U32_AT(buffer + 4 * i); break;
+ }
+ }
+
+ numBytes *= scale;
+
+ seeker->mSegments.push(numBytes);
+
+ LOGV("entry #%d: %d offset 0x%08lx", i, numBytes, offset);
+ offset += numBytes;
+ }
+
+ delete[] buffer;
+ buffer = NULL;
+
+ LOGI("Found VBRI header.");
+
+ return seeker;
+}
+
+VBRISeeker::VBRISeeker()
+ : mDurationUs(-1) {
+}
+
+bool VBRISeeker::getDuration(int64_t *durationUs) {
+ if (mDurationUs < 0) {
+ return false;
+ }
+
+ *durationUs = mDurationUs;
+
+ return true;
+}
+
+bool VBRISeeker::getOffsetForTime(int64_t *timeUs, off_t *pos) {
+ if (mDurationUs < 0) {
+ return false;
+ }
+
+ int64_t segmentDurationUs = mDurationUs / mSegments.size();
+
+ int64_t nowUs = 0;
+ *pos = mBasePos;
+ size_t segmentIndex = 0;
+ while (segmentIndex < mSegments.size() && nowUs < *timeUs) {
+ nowUs += segmentDurationUs;
+ *pos += mSegments.itemAt(segmentIndex++);
+ }
+
+ LOGV("getOffsetForTime %lld us => 0x%08lx", *timeUs, *pos);
+
+ *timeUs = nowUs;
+
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp
new file mode 100644
index 0000000..72f260e
--- /dev/null
+++ b/media/libstagefright/XINGSeeker.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/XINGSeeker.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static bool parse_xing_header(
+ const sp<DataSource> &source, off_t first_frame_pos,
+ int32_t *frame_number = NULL, int32_t *byte_number = NULL,
+ char *table_of_contents = NULL, int32_t *quality_indicator = NULL,
+ int64_t *duration = NULL);
+
+// static
+sp<XINGSeeker> XINGSeeker::CreateFromSource(
+ const sp<DataSource> &source, off_t first_frame_pos) {
+ sp<XINGSeeker> seeker = new XINGSeeker;
+
+ seeker->mFirstFramePos = first_frame_pos;
+
+ if (!parse_xing_header(
+ source, first_frame_pos,
+ NULL, &seeker->mSizeBytes, seeker->mTableOfContents,
+ NULL, &seeker->mDurationUs)) {
+ return NULL;
+ }
+
+ LOGI("Found XING header.");
+
+ return seeker;
+}
+
+XINGSeeker::XINGSeeker()
+ : mDurationUs(-1),
+ mSizeBytes(0) {
+}
+
+bool XINGSeeker::getDuration(int64_t *durationUs) {
+ if (mDurationUs < 0) {
+ return false;
+ }
+
+ *durationUs = mDurationUs;
+
+ return true;
+}
+
+bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off_t *pos) {
+ if (mSizeBytes == 0 || mTableOfContents[0] <= 0 || mDurationUs < 0) {
+ return false;
+ }
+
+ float percent = (float)(*timeUs) * 100 / mDurationUs;
+ float fx;
+ if( percent <= 0.0f ) {
+ fx = 0.0f;
+ } else if( percent >= 100.0f ) {
+ fx = 256.0f;
+ } else {
+ int a = (int)percent;
+ float fa, fb;
+ if ( a == 0 ) {
+ fa = 0.0f;
+ } else {
+ fa = (float)mTableOfContents[a-1];
+ }
+ if ( a < 99 ) {
+ fb = (float)mTableOfContents[a];
+ } else {
+ fb = 256.0f;
+ }
+ fx = fa + (fb-fa)*(percent-a);
+ }
+
+ *pos = (int)((1.0f/256.0f)*fx*mSizeBytes) + mFirstFramePos;
+
+ return true;
+}
+
+static bool parse_xing_header(
+ const sp<DataSource> &source, off_t first_frame_pos,
+ int32_t *frame_number, int32_t *byte_number,
+ char *table_of_contents, int32_t *quality_indicator,
+ int64_t *duration) {
+ if (frame_number) {
+ *frame_number = 0;
+ }
+ if (byte_number) {
+ *byte_number = 0;
+ }
+ if (table_of_contents) {
+ table_of_contents[0] = 0;
+ }
+ if (quality_indicator) {
+ *quality_indicator = 0;
+ }
+ if (duration) {
+ *duration = 0;
+ }
+
+ uint8_t buffer[4];
+ int offset = first_frame_pos;
+ if (source->readAt(offset, &buffer, 4) < 4) { // get header
+ return false;
+ }
+ offset += 4;
+
+ uint8_t id, layer, sr_index, mode;
+ layer = (buffer[1] >> 1) & 3;
+ id = (buffer[1] >> 3) & 3;
+ sr_index = (buffer[2] >> 2) & 3;
+ mode = (buffer[3] >> 6) & 3;
+ if (layer == 0) {
+ return false;
+ }
+ if (id == 1) {
+ return false;
+ }
+ if (sr_index == 3) {
+ return false;
+ }
+ // determine offset of XING header
+ if(id&1) { // mpeg1
+ if (mode != 3) offset += 32;
+ else offset += 17;
+ } else { // mpeg2
+ if (mode != 3) offset += 17;
+ else offset += 9;
+ }
+
+ if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
+ return false;
+ }
+ offset += 4;
+ // Check XING ID
+ if ((buffer[0] != 'X') || (buffer[1] != 'i')
+ || (buffer[2] != 'n') || (buffer[3] != 'g')) {
+ if ((buffer[0] != 'I') || (buffer[1] != 'n')
+ || (buffer[2] != 'f') || (buffer[3] != 'o')) {
+ return false;
+ }
+ }
+
+ if (source->readAt(offset, &buffer, 4) < 4) { // flags
+ return false;
+ }
+ offset += 4;
+ uint32_t flags = U32_AT(buffer);
+
+ if (flags & 0x0001) { // Frames field is present
+ if (source->readAt(offset, buffer, 4) < 4) {
+ return false;
+ }
+ if (frame_number) {
+ *frame_number = U32_AT(buffer);
+ }
+ int32_t frame = U32_AT(buffer);
+ // Samples per Frame: 1. index = MPEG Version ID, 2. index = Layer
+ const int samplesPerFrames[2][3] =
+ {
+ { 384, 1152, 576 }, // MPEG 2, 2.5: layer1, layer2, layer3
+ { 384, 1152, 1152 }, // MPEG 1: layer1, layer2, layer3
+ };
+ // sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
+ const int samplingRates[4][3] =
+ {
+ { 11025, 12000, 8000, }, // MPEG 2.5
+ { 0, 0, 0, }, // reserved
+ { 22050, 24000, 16000, }, // MPEG 2
+ { 44100, 48000, 32000, } // MPEG 1
+ };
+ if (duration) {
+ *duration = (int64_t)frame * samplesPerFrames[id&1][3-layer] * 1000000LL
+ / samplingRates[id][sr_index];
+ }
+ offset += 4;
+ }
+ if (flags & 0x0002) { // Bytes field is present
+ if (byte_number) {
+ if (source->readAt(offset, buffer, 4) < 4) {
+ return false;
+ }
+ *byte_number = U32_AT(buffer);
+ }
+ offset += 4;
+ }
+ if (flags & 0x0004) { // TOC field is present
+ if (table_of_contents) {
+ if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
+ return false;
+ }
+ }
+ offset += 100;
+ }
+ if (flags & 0x0008) { // Quality indicator field is present
+ if (quality_indicator) {
+ if (source->readAt(offset, buffer, 4) < 4) {
+ return false;
+ }
+ *quality_indicator = U32_AT(buffer);
+ }
+ }
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index d5a5313..72611cf 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -50,6 +50,7 @@ static status_t ConvertOmxProfileLevel(
profileLevel = CORE_PROFILE_LEVEL2;
break;
}
+ break;
default:
LOGE("Unsupported profile (%d) for H263", omxProfile);
return BAD_VALUE;
@@ -74,7 +75,8 @@ static status_t ConvertOmxProfileLevel(
LOGE("Unsupported level (%d) for MPEG4 simple profile",
omxLevel);
return BAD_VALUE;
- }
+ }
+ break;
case OMX_VIDEO_MPEG4ProfileSimpleScalable:
switch (omxLevel) {
case OMX_VIDEO_MPEG4Level0b:
@@ -91,6 +93,7 @@ static status_t ConvertOmxProfileLevel(
"scalable profile", omxLevel);
return BAD_VALUE;
}
+ break;
case OMX_VIDEO_MPEG4ProfileCore:
switch (omxLevel) {
case OMX_VIDEO_MPEG4Level1:
@@ -104,6 +107,7 @@ static status_t ConvertOmxProfileLevel(
"profile", omxLevel);
return BAD_VALUE;
}
+ break;
case OMX_VIDEO_MPEG4ProfileCoreScalable:
switch (omxLevel) {
case OMX_VIDEO_MPEG4Level1:
@@ -120,6 +124,7 @@ static status_t ConvertOmxProfileLevel(
"scalable profile", omxLevel);
return BAD_VALUE;
}
+ break;
default:
LOGE("Unsupported MPEG4 profile (%d)", omxProfile);
return BAD_VALUE;
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index 30136e7d..11ca243 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -24,6 +24,7 @@ namespace android {
struct AMessage;
class DataSource;
+struct MP3Seeker;
class String8;
class MP3Extractor : public MediaExtractor {
@@ -37,6 +38,11 @@ public:
virtual sp<MetaData> getMetaData();
+ static bool get_mp3_frame_size(
+ uint32_t header, size_t *frame_size,
+ int *out_sampling_rate = NULL, int *out_channels = NULL,
+ int *out_bitrate = NULL);
+
private:
status_t mInitCheck;
@@ -44,8 +50,7 @@ private:
off_t mFirstFramePos;
sp<MetaData> mMeta;
uint32_t mFixedHeader;
- int32_t mByteNumber; // total number of bytes in this MP3
- char mTableOfContents[99]; // TOC entries in XING header
+ sp<MP3Seeker> mSeeker;
MP3Extractor(const MP3Extractor &);
MP3Extractor &operator=(const MP3Extractor &);
diff --git a/media/libstagefright/include/MP3Seeker.h b/media/libstagefright/include/MP3Seeker.h
new file mode 100644
index 0000000..190eaed
--- /dev/null
+++ b/media/libstagefright/include/MP3Seeker.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 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 MP3_SEEKER_H_
+
+#define MP3_SEEKER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct MP3Seeker : public RefBase {
+ MP3Seeker() {}
+
+ virtual bool getDuration(int64_t *durationUs) = 0;
+
+ // Given a request seek time in "*timeUs", find the byte offset closest
+ // to that position and return it in "*pos". Update "*timeUs" to reflect
+ // the actual time that seekpoint represents.
+ virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos) = 0;
+
+protected:
+ virtual ~MP3Seeker() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(MP3Seeker);
+};
+
+} // namespace android
+
+#endif // MP3_SEEKER_H_
+
diff --git a/media/libstagefright/include/VBRISeeker.h b/media/libstagefright/include/VBRISeeker.h
new file mode 100644
index 0000000..d6bd19d
--- /dev/null
+++ b/media/libstagefright/include/VBRISeeker.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 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 VBRI_SEEKER_H_
+
+#define VBRI_SEEKER_H_
+
+#include "include/MP3Seeker.h"
+
+#include <utils/Vector.h>
+
+namespace android {
+
+struct DataSource;
+
+struct VBRISeeker : public MP3Seeker {
+ static sp<VBRISeeker> CreateFromSource(
+ const sp<DataSource> &source, off_t post_id3_pos);
+
+ virtual bool getDuration(int64_t *durationUs);
+ virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos);
+
+private:
+ off_t mBasePos;
+ int64_t mDurationUs;
+ Vector<uint32_t> mSegments;
+
+ VBRISeeker();
+
+ DISALLOW_EVIL_CONSTRUCTORS(VBRISeeker);
+};
+
+} // namespace android
+
+#endif // VBRI_SEEKER_H_
+
+
diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h
new file mode 100644
index 0000000..d4ff4e1
--- /dev/null
+++ b/media/libstagefright/include/XINGSeeker.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 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 XING_SEEKER_H_
+
+#define XING_SEEKER_H_
+
+#include "include/MP3Seeker.h"
+
+namespace android {
+
+struct DataSource;
+
+struct XINGSeeker : public MP3Seeker {
+ static sp<XINGSeeker> CreateFromSource(
+ const sp<DataSource> &source, off_t first_frame_pos);
+
+ virtual bool getDuration(int64_t *durationUs);
+ virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos);
+
+private:
+ int64_t mFirstFramePos;
+ int64_t mDurationUs;
+ int32_t mSizeBytes;
+
+ // TOC entries in XING header. Skip the first one since it's always 0.
+ char mTableOfContents[99];
+
+ XINGSeeker();
+
+ DISALLOW_EVIL_CONSTRUCTORS(XINGSeeker);
+};
+
+} // namespace android
+
+#endif // XING_SEEKER_H_
+