Merge "implement part of [3094280] New animation for screen on and screen off add support for screen on animation" into gingerbread
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7242803..de8e3f4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5268,10 +5268,15 @@
}
first = false;
b.append("dat=");
- if (mData.getScheme().equalsIgnoreCase("tel")) {
- b.append("tel:xxx-xxx-xxxx");
- } else if (mData.getScheme().equalsIgnoreCase("smsto")) {
- b.append("smsto:xxx-xxx-xxxx");
+ String scheme = mData.getScheme();
+ if (scheme != null) {
+ if (scheme.equalsIgnoreCase("tel")) {
+ b.append("tel:xxx-xxx-xxxx");
+ } else if (scheme.equalsIgnoreCase("smsto")) {
+ b.append("smsto:xxx-xxx-xxxx");
+ } else {
+ b.append(mData);
+ }
} else {
b.append(mData);
}
diff --git a/core/java/android/os/storage/OnObbStateChangeListener.java b/core/java/android/os/storage/OnObbStateChangeListener.java
index 950195b..1fb1782 100644
--- a/core/java/android/os/storage/OnObbStateChangeListener.java
+++ b/core/java/android/os/storage/OnObbStateChangeListener.java
@@ -67,9 +67,9 @@
public static final int ERROR_ALREADY_MOUNTED = 24;
/**
- * The current application does not have permission to use this OBB because
- * the OBB indicates it's owned by a different package or the key used to
- * open it is incorrect. Returned in status messages from calls made via
+ * The current application does not have permission to use this OBB. This
+ * could be because the OBB indicates it's owned by a different package or
+ * some other error. Returned in status messages from calls made via
* {@link StorageManager}
*/
public static final int ERROR_PERMISSION_DENIED = 25;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 84aca60..8ee84dc 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3947,6 +3947,16 @@
// requestFormData, and it needs to have the correct nodePointer.
mWebTextView.setNodePointer(nodePointer);
mWebTextView.setType(nativeFocusCandidateType());
+ Rect paddingRect = nativeFocusCandidatePaddingRect();
+ if (paddingRect != null) {
+ // Use contentToViewDimension since these are the dimensions of
+ // the padding.
+ mWebTextView.setPadding(
+ contentToViewDimension(paddingRect.left),
+ contentToViewDimension(paddingRect.top),
+ contentToViewDimension(paddingRect.right),
+ contentToViewDimension(paddingRect.bottom));
+ }
if (null == text) {
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "rebuildWebTextView null == text");
@@ -7841,6 +7851,13 @@
/* package */ native int nativeFocusCandidateMaxLength();
/* package */ native String nativeFocusCandidateName();
private native Rect nativeFocusCandidateNodeBounds();
+ /**
+ * @return A Rect with left, top, right, bottom set to the corresponding
+ * padding values in the focus candidate, if it is a textfield/textarea with
+ * a style. Otherwise return null. This is not actually a rectangle; Rect
+ * is being used to pass four integers.
+ */
+ private native Rect nativeFocusCandidatePaddingRect();
/* package */ native int nativeFocusCandidatePointer();
private native String nativeFocusCandidateText();
private native int nativeFocusCandidateTextSize();
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index d1aff2a..714b259 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -33,6 +33,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.Vibrator;
import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver;
@@ -60,6 +61,9 @@
private static boolean mReboot;
private static String mRebootReason;
+ // Provides shutdown assurance in case the system_server is killed
+ public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
+
// static instance of this thread
private static final ShutdownThread sInstance = new ShutdownThread();
@@ -195,7 +199,17 @@
actionDone();
}
};
-
+
+ /*
+ * Write a system property in case the system_server reboots before we
+ * get to the actual hardware restart. If that happens, we'll retry at
+ * the beginning of the SystemServer startup.
+ */
+ {
+ String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
+ SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
+ }
+
Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
@@ -325,10 +339,21 @@
}
}
- if (mReboot) {
- Log.i(TAG, "Rebooting, reason: " + mRebootReason);
+ rebootOrShutdown(mReboot, mRebootReason);
+ }
+
+ /**
+ * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
+ * or {@link #shutdown(Context, boolean)} instead.
+ *
+ * @param reboot true to reboot or false to shutdown
+ * @param reason reason for reboot
+ */
+ public static void rebootOrShutdown(boolean reboot, String reason) {
+ if (reboot) {
+ Log.i(TAG, "Rebooting, reason: " + reason);
try {
- Power.reboot(mRebootReason);
+ Power.reboot(reason);
} catch (Exception e) {
Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java
index 4632794..5ff348a 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java
@@ -96,6 +96,10 @@
*/
public native void enableDiscovery(int mode);
+ public native void disableDiscovery();
+
+ public native void readerDiscovery();
+
/**
* Disables an NFCManager mode of operation. Allows to disable tag reader,
* peer to peer initiator or target modes.
diff --git a/docs/html/guide/appendix/install-location.jd b/docs/html/guide/appendix/install-location.jd
index be89caf..914aa66 100644
--- a/docs/html/guide/appendix/install-location.jd
+++ b/docs/html/guide/appendix/install-location.jd
@@ -111,7 +111,7 @@
<p class="caution"><strong>Caution:</strong> Although XML markup such as this will be ignored by
older platforms, you must be careful not to use programming APIs introduced in API Level 8
while your {@code minSdkVersion} is less than "8", unless you perform the work necessary to
-provide backward compatiblity in your code. For information about building
+provide backward compatibility in your code. For information about building
backward compatibility in your application code, see the <a
href="{@docRoot}resources/articles/backward-compatibility.html">Backward Compatibility</a>
article.</p>
@@ -167,6 +167,10 @@
<dd>Your {@link android.app.admin.DeviceAdminReceiver} and all its admin capabilities will
be disabled, which can have unforeseeable consequences for the device functionality, which may
persist after external storage is remounted.</dd>
+ <dt>Broadcast Receivers listening for "boot completed"</dt>
+ <dd>The system delivers the {@link android.content.Intent#ACTION_BOOT_COMPLETED} broadcast
+before the external storage is mounted to the device. If your application is installed on the
+external storage, it can never receive this broadcast.</dd>
</dl>
<p>If your application uses any of the features listed above, you <strong>should not</strong> allow
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 291b18a..5ab1640 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -76,6 +76,9 @@
/* Stream over a socket, limited to a single stream */
OUTPUT_FORMAT_RTP_AVP = 7,
+ /* H.264/AAC data encapsulated in MPEG2/TS */
+ OUTPUT_FORMAT_MPEG2TS = 8,
+
OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
};
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 9d2cff6..a3da3ed 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -36,6 +36,7 @@
enum Flags {
kWantsPrefetching = 1,
kStreamedFromLocalHost = 2,
+ kIsCachingDataSource = 4,
};
static sp<DataSource> CreateFromURI(
diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h
index 551ca01..f2c6505 100644
--- a/include/media/stagefright/MPEG2TSWriter.h
+++ b/include/media/stagefright/MPEG2TSWriter.h
@@ -25,7 +25,10 @@
namespace android {
+struct ABuffer;
+
struct MPEG2TSWriter : public MediaWriter {
+ MPEG2TSWriter(int fd);
MPEG2TSWriter(const char *filename);
virtual status_t addSource(const sp<MediaSource> &source);
@@ -59,6 +62,8 @@
int64_t mNumTSPacketsWritten;
int64_t mNumTSPacketsBeforeMeta;
+ void init();
+
void writeTS();
void writeProgramAssociationTable();
void writeProgramMap();
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 34a86ec..b38124e 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -191,6 +191,9 @@
/** @hide Stream over a socket, limited to a single stream */
public static final int OUTPUT_FORMAT_RTP_AVP = 7;
+
+ /** @hide H.264/AAC data encapsulated in MPEG2/TS */
+ public static final int OUTPUT_FORMAT_MPEG2TS = 8;
};
/**
diff --git a/media/libmedia/Metadata.cpp b/media/libmedia/Metadata.cpp
index 35ec6b3..aec96f1 100644
--- a/media/libmedia/Metadata.cpp
+++ b/media/libmedia/Metadata.cpp
@@ -32,7 +32,7 @@
// All these constants below must be kept in sync with Metadata.java.
enum MetadataId {
FIRST_SYSTEM_ID = 1,
- LAST_SYSTEM_ID = 31,
+ LAST_SYSTEM_ID = 32,
FIRST_CUSTOM_ID = 8192
};
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 9d53c25..e20e3ba 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -181,7 +181,7 @@
LOGE("setOutputFormat called in an invalid state: %d", mCurrentState);
return INVALID_OPERATION;
}
- if (mIsVideoSourceSet && of >= OUTPUT_FORMAT_AUDIO_ONLY_START && of != OUTPUT_FORMAT_RTP_AVP) { //first non-video output format
+ if (mIsVideoSourceSet && of >= OUTPUT_FORMAT_AUDIO_ONLY_START && of != OUTPUT_FORMAT_RTP_AVP && of != OUTPUT_FORMAT_MPEG2TS) { //first non-video output format
LOGE("output format (%d) is meant for audio recording only and incompatible with video recording", of);
return INVALID_OPERATION;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index cf01ff6..d37d83d 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -24,6 +24,7 @@
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/AMRWriter.h>
#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MPEG2TSWriter.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
@@ -632,6 +633,9 @@
case OUTPUT_FORMAT_RTP_AVP:
return startRTPRecording();
+ case OUTPUT_FORMAT_MPEG2TS:
+ return startMPEG2TSRecording();
+
default:
LOGE("Unsupported output file format: %d", mOutputFormat);
return UNKNOWN_ERROR;
@@ -799,6 +803,52 @@
return mWriter->start();
}
+status_t StagefrightRecorder::startMPEG2TSRecording() {
+ CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS);
+
+ sp<MediaWriter> writer = new MPEG2TSWriter(dup(mOutputFd));
+
+ if (mAudioSource != AUDIO_SOURCE_LIST_END) {
+ if (mAudioEncoder != AUDIO_ENCODER_AAC) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ status_t err = setupAudioEncoder(writer);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (mVideoSource == VIDEO_SOURCE_DEFAULT
+ || mVideoSource == VIDEO_SOURCE_CAMERA) {
+ if (mVideoEncoder != VIDEO_ENCODER_H264) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sp<MediaSource> encoder;
+ status_t err = setupVideoEncoder(&encoder);
+
+ if (err != OK) {
+ return err;
+ }
+
+ writer->addSource(encoder);
+ }
+
+ if (mMaxFileDurationUs != 0) {
+ writer->setMaxFileDuration(mMaxFileDurationUs);
+ }
+
+ if (mMaxFileSizeBytes != 0) {
+ writer->setMaxFileSize(mMaxFileSizeBytes);
+ }
+
+ mWriter = writer;
+
+ return mWriter->start();
+}
+
void StagefrightRecorder::clipVideoFrameRate() {
LOGV("clipVideoFrameRate: encoder %d", mVideoEncoder);
int minFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 216f6bc..ad0dfa0 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -102,6 +102,7 @@
status_t startAMRRecording();
status_t startAACRecording();
status_t startRTPRecording();
+ status_t startMPEG2TSRecording();
sp<MediaSource> createAudioSource();
status_t setupCameraSource();
status_t setupAudioEncoder(const sp<MediaWriter>& writer);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index ff28f3b..57bea8c 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -26,6 +26,7 @@
#include "include/SoftwareRenderer.h"
#include "include/NuCachedSource2.h"
#include "include/ThrottledSource.h"
+#include "include/MPEG2TSExtractor.h"
#include "ARTPSession.h"
#include "APacketSource.h"
@@ -302,6 +303,28 @@
}
status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
+ // Attempt to approximate overall stream bitrate by summing all
+ // tracks' individual bitrates, if not all of them advertise bitrate,
+ // we have to fail.
+
+ int64_t totalBitRate = 0;
+
+ for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+ int32_t bitrate;
+ if (!meta->findInt32(kKeyBitRate, &bitrate)) {
+ totalBitRate = -1;
+ break;
+ }
+
+ totalBitRate += bitrate;
+ }
+
+ mBitrate = totalBitRate;
+
+ LOGV("mBitrate = %lld bits/sec", mBitrate);
+
bool haveAudio = false;
bool haveVideo = false;
for (size_t i = 0; i < extractor->countTracks(); ++i) {
@@ -440,6 +463,8 @@
delete mSuspensionState;
mSuspensionState = NULL;
+
+ mBitrate = -1;
}
void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
@@ -452,17 +477,32 @@
}
}
+bool AwesomePlayer::getBitrate(int64_t *bitrate) {
+ off_t size;
+ if (mDurationUs >= 0 && mCachedSource != NULL
+ && mCachedSource->getSize(&size) == OK) {
+ *bitrate = size * 8000000ll / mDurationUs; // in bits/sec
+ return true;
+ }
+
+ if (mBitrate >= 0) {
+ *bitrate = mBitrate;
+ return true;
+ }
+
+ *bitrate = 0;
+
+ return false;
+}
+
// Returns true iff cached duration is available/applicable.
bool AwesomePlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) {
- off_t totalSize;
+ int64_t bitrate;
if (mRTSPController != NULL) {
*durationUs = mRTSPController->getQueueDurationUs(eos);
return true;
- } else if (mCachedSource != NULL && mDurationUs >= 0
- && mCachedSource->getSize(&totalSize) == OK) {
- int64_t bitrate = totalSize * 8000000ll / mDurationUs; // in bits/sec
-
+ } else if (mCachedSource != NULL && getBitrate(&bitrate)) {
size_t cachedDataRemaining = mCachedSource->approxDataRemaining(eos);
*durationUs = cachedDataRemaining * 8000000ll / bitrate;
return true;
@@ -489,10 +529,8 @@
finishAsyncPrepare_l();
}
} else {
- off_t size;
- if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
- int64_t bitrate = size * 8000000ll / mDurationUs; // in bits/sec
-
+ int64_t bitrate;
+ if (getBitrate(&bitrate)) {
size_t cachedSize = mCachedSource->cachedSize();
int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
@@ -506,8 +544,8 @@
// We don't know the bitrate of the stream, use absolute size
// limits to maintain the cache.
- const size_t kLowWaterMarkBytes = 400000;
- const size_t kHighWaterMarkBytes = 1000000;
+ const size_t kLowWaterMarkBytes = 40000;
+ const size_t kHighWaterMarkBytes = 200000;
if ((mFlags & PLAYING) && !eos
&& (cachedDataRemaining < kLowWaterMarkBytes)) {
@@ -1343,14 +1381,17 @@
String8 uri("http://");
uri.append(mUri.string() + 11);
- dataSource = new LiveSource(uri.string());
+ sp<LiveSource> liveSource = new LiveSource(uri.string());
- mCachedSource = new NuCachedSource2(dataSource);
+ mCachedSource = new NuCachedSource2(liveSource);
dataSource = mCachedSource;
sp<MediaExtractor> extractor =
MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
+ static_cast<MPEG2TSExtractor *>(extractor.get())
+ ->setLiveSource(liveSource);
+
return setDataSource_l(extractor);
} else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) {
if (mLooper == NULL) {
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index ee74b88..b3daf67 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -63,6 +63,8 @@
sp<ALooper> mLooper;
sp<AMessage> mNotify;
+ sp<ABuffer> mAACCodecSpecificData;
+
sp<ABuffer> mAACBuffer;
unsigned mStreamType;
@@ -125,6 +127,8 @@
void MPEG2TSWriter::SourceInfo::stop() {
mLooper->unregisterHandler(id());
mLooper->stop();
+
+ mSource->stop();
}
void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() {
@@ -133,18 +137,48 @@
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!meta->findData(kKeyESDS, &type, &data, &size)) {
+ // Codec specific data better be in the first data buffer.
+ return;
+ }
+
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+ const uint8_t *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ (const void **)&codec_specific_data, &codec_specific_data_size);
+
+ CHECK_GE(codec_specific_data_size, 2u);
+
+ mAACCodecSpecificData = new ABuffer(codec_specific_data_size);
+
+ memcpy(mAACCodecSpecificData->data(), codec_specific_data,
+ codec_specific_data_size);
+
+ return;
+ }
+
if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
return;
}
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!meta->findData(kKeyAVCC, &type, &data, &size)) {
+ // Codec specific data better be part of the data stream then.
+ return;
+ }
+
sp<ABuffer> out = new ABuffer(1024);
out->setRange(0, 0);
- uint32_t type;
- const void *data;
- size_t size;
- CHECK(meta->findData(kKeyAVCC, &type, &data, &size));
-
const uint8_t *ptr = (const uint8_t *)data;
size_t numSeqParameterSets = ptr[5] & 31;
@@ -250,21 +284,7 @@
mAACBuffer->setRange(0, 0);
}
- sp<MetaData> meta = mSource->getFormat();
- uint32_t type;
- const void *data;
- size_t size;
- CHECK(meta->findData(kKeyESDS, &type, &data, &size));
-
- ESDS esds((const char *)data, size);
- CHECK_EQ(esds.InitCheck(), (status_t)OK);
-
- const uint8_t *codec_specific_data;
- size_t codec_specific_data_size;
- esds.getCodecSpecificInfo(
- (const void **)&codec_specific_data, &codec_specific_data_size);
-
- CHECK_GE(codec_specific_data_size, 2u);
+ const uint8_t *codec_specific_data = mAACCodecSpecificData->data();
unsigned profile = (codec_specific_data[0] >> 3) - 1;
@@ -355,7 +375,18 @@
}
if (err == OK) {
- if (buffer->range_length() > 0) {
+ if (mStreamType == 0x0f && mAACCodecSpecificData == NULL) {
+ // The first buffer contains codec specific data.
+
+ CHECK_GE(buffer->range_length(), 2u);
+
+ mAACCodecSpecificData = new ABuffer(buffer->range_length());
+
+ memcpy(mAACCodecSpecificData->data(),
+ (const uint8_t *)buffer->data()
+ + buffer->range_offset(),
+ buffer->range_length());
+ } else if (buffer->range_length() > 0) {
if (mStreamType == 0x0f) {
appendAACFrames(buffer);
} else {
@@ -378,12 +409,25 @@
////////////////////////////////////////////////////////////////////////////////
+MPEG2TSWriter::MPEG2TSWriter(int fd)
+ : mFile(fdopen(fd, "wb")),
+ mStarted(false),
+ mNumSourcesDone(0),
+ mNumTSPacketsWritten(0),
+ mNumTSPacketsBeforeMeta(0) {
+ init();
+}
+
MPEG2TSWriter::MPEG2TSWriter(const char *filename)
: mFile(fopen(filename, "wb")),
mStarted(false),
mNumSourcesDone(0),
mNumTSPacketsWritten(0),
mNumTSPacketsBeforeMeta(0) {
+ init();
+}
+
+void MPEG2TSWriter::init() {
CHECK(mFile != NULL);
mLooper = new ALooper;
@@ -396,6 +440,10 @@
}
MPEG2TSWriter::~MPEG2TSWriter() {
+ if (mStarted) {
+ stop();
+ }
+
mLooper->unregisterHandler(mReflector->id());
mLooper->stop();
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 3a0fc41..b67002d 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -179,7 +179,8 @@
mFinalStatus(OK),
mLastAccessPos(0),
mFetching(true),
- mLastFetchTimeUs(-1) {
+ mLastFetchTimeUs(-1),
+ mSuspended(false) {
mLooper->setName("NuCachedSource2");
mLooper->registerHandler(mReflector);
mLooper->start();
@@ -205,7 +206,7 @@
}
uint32_t NuCachedSource2::flags() {
- return mSource->flags();
+ return (mSource->flags() & ~kWantsPrefetching) | kIsCachingDataSource;
}
void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) {
@@ -222,6 +223,12 @@
break;
}
+ case kWhatSuspend:
+ {
+ onSuspend();
+ break;
+ }
+
default:
TRESPASS();
}
@@ -263,6 +270,7 @@
bool keepAlive =
!mFetching
+ && !mSuspended
&& mFinalStatus == OK
&& ALooper::GetNowUs() >= mLastFetchTimeUs + kKeepAliveIntervalUs;
@@ -279,7 +287,7 @@
LOGI("Cache full, done prefetching for now");
mFetching = false;
}
- } else {
+ } else if (!mSuspended) {
Mutex::Autolock autoLock(mLock);
restartPrefetcherIfNecessary_l();
}
@@ -468,5 +476,39 @@
return OK;
}
+void NuCachedSource2::clearCacheAndResume() {
+ LOGV("clearCacheAndResume");
+
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK(mSuspended);
+
+ mCacheOffset = 0;
+ mFinalStatus = OK;
+ mLastAccessPos = 0;
+ mLastFetchTimeUs = -1;
+
+ size_t totalSize = mCache->totalSize();
+ CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
+
+ mFetching = true;
+ mSuspended = false;
+}
+
+void NuCachedSource2::suspend() {
+ (new AMessage(kWhatSuspend, mReflector->id()))->post();
+
+ while (!mSuspended) {
+ usleep(10000);
+ }
+}
+
+void NuCachedSource2::onSuspend() {
+ Mutex::Autolock autoLock(mLock);
+
+ mFetching = false;
+ mSuspended = true;
+}
+
} // namespace android
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
index 943a0fc..4124571 100644
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ b/media/libstagefright/httplive/LiveSource.cpp
@@ -31,6 +31,7 @@
LiveSource::LiveSource(const char *url)
: mMasterURL(url),
mInitCheck(NO_INIT),
+ mDurationUs(-1),
mPlaylistIndex(0),
mLastFetchTimeUs(-1),
mSource(new NuHTTPDataSource),
@@ -40,6 +41,8 @@
mPrevBandwidthIndex(-1) {
if (switchToNext()) {
mInitCheck = OK;
+
+ determineSeekability();
}
}
@@ -139,7 +142,7 @@
}
#else
// Stay on the lowest bandwidth available.
- size_t index = 0; // Lowest bandwidth stream
+ size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream
#endif
mURL = mBandwidthItems.editItemAt(index).mURI;
@@ -336,4 +339,69 @@
return OK;
}
+bool LiveSource::seekTo(int64_t seekTimeUs) {
+ LOGV("seek to %lld us", seekTimeUs);
+
+ if (!mPlaylist->isComplete()) {
+ return false;
+ }
+
+ int32_t targetDuration;
+ if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
+ return false;
+ }
+
+ int64_t seekTimeSecs = (seekTimeUs + 500000ll) / 1000000ll;
+
+ int64_t index = seekTimeSecs / targetDuration;
+
+ if (index < 0 || index >= mPlaylist->size()) {
+ return false;
+ }
+
+ size_t newPlaylistIndex = mFirstItemSequenceNumber + index;
+
+ if (newPlaylistIndex == mPlaylistIndex) {
+ return false;
+ }
+
+ mPlaylistIndex = newPlaylistIndex;
+
+ switchToNext();
+ mOffsetBias = 0;
+
+ LOGV("seeking to index %lld", index);
+
+ return true;
+}
+
+bool LiveSource::getDuration(int64_t *durationUs) const {
+ if (mDurationUs >= 0) {
+ *durationUs = mDurationUs;
+ return true;
+ }
+
+ *durationUs = 0;
+ return false;
+}
+
+bool LiveSource::isSeekable() const {
+ return mDurationUs >= 0;
+}
+
+void LiveSource::determineSeekability() {
+ mDurationUs = -1;
+
+ if (!mPlaylist->isComplete()) {
+ return;
+ }
+
+ int32_t targetDuration;
+ if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
+ return;
+ }
+
+ mDurationUs = targetDuration * 1000000ll * mPlaylist->size();
+}
+
} // namespace android
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 0d7daa9..a68c641 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -27,7 +27,8 @@
: mInitCheck(NO_INIT),
mBaseURI(baseURI),
mIsExtM3U(false),
- mIsVariantPlaylist(false) {
+ mIsVariantPlaylist(false),
+ mIsComplete(false) {
mInitCheck = parse(data, size);
}
@@ -46,6 +47,10 @@
return mIsVariantPlaylist;
}
+bool M3UParser::isComplete() const {
+ return mIsComplete;
+}
+
sp<AMessage> M3UParser::meta() {
return mMeta;
}
@@ -153,6 +158,8 @@
return ERROR_MALFORMED;
}
err = parseMetaData(line, &mMeta, "media-sequence");
+ } else if (line.startsWith("#EXT-X-ENDLIST")) {
+ mIsComplete = true;
} else if (line.startsWith("#EXTINF")) {
if (mIsVariantPlaylist) {
return ERROR_MALFORMED;
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 079adca..46a0c65 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -152,6 +152,8 @@
bool mSeekNotificationSent;
int64_t mSeekTimeUs;
+ int64_t mBitrate; // total bitrate of the file (in bps) or -1 if unknown.
+
bool mWatchForAudioSeekComplete;
bool mWatchForAudioEOS;
@@ -254,6 +256,8 @@
static void OnRTSPSeekDoneWrapper(void *cookie);
void onRTSPSeekDone();
+ bool getBitrate(int64_t *bitrate);
+
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
};
diff --git a/media/libstagefright/include/LiveSource.h b/media/libstagefright/include/LiveSource.h
index 5e89581..55dd45e 100644
--- a/media/libstagefright/include/LiveSource.h
+++ b/media/libstagefright/include/LiveSource.h
@@ -40,6 +40,11 @@
return kWantsPrefetching;
}
+ bool getDuration(int64_t *durationUs) const;
+
+ bool isSeekable() const;
+ bool seekTo(int64_t seekTimeUs);
+
protected:
virtual ~LiveSource();
@@ -53,6 +58,7 @@
AString mMasterURL;
AString mURL;
status_t mInitCheck;
+ int64_t mDurationUs;
sp<M3UParser> mPlaylist;
int32_t mFirstItemSequenceNumber;
@@ -72,6 +78,7 @@
bool switchToNext();
bool loadPlaylist(bool fetchMaster);
+ void determineSeekability();
DISALLOW_EVIL_CONSTRUCTORS(LiveSource);
};
diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h
index 69199ab..bd9eebe 100644
--- a/media/libstagefright/include/M3UParser.h
+++ b/media/libstagefright/include/M3UParser.h
@@ -32,6 +32,7 @@
bool isExtM3U() const;
bool isVariantPlaylist() const;
+ bool isComplete() const;
sp<AMessage> meta();
@@ -52,6 +53,7 @@
AString mBaseURI;
bool mIsExtM3U;
bool mIsVariantPlaylist;
+ bool mIsComplete;
sp<AMessage> mMeta;
Vector<Item> mItems;
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index 1bf4cd1..d83b538 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -15,6 +15,7 @@
struct DataSource;
struct MPEG2TSSource;
struct String8;
+struct LiveSource;
struct MPEG2TSExtractor : public MediaExtractor {
MPEG2TSExtractor(const sp<DataSource> &source);
@@ -25,16 +26,19 @@
virtual sp<MetaData> getMetaData();
- virtual uint32_t flags() const {
- return CAN_PAUSE;
- }
+ virtual uint32_t flags() const;
+
+ void setLiveSource(const sp<LiveSource> &liveSource);
+ void seekTo(int64_t seekTimeUs);
private:
friend struct MPEG2TSSource;
- Mutex mLock;
+ mutable Mutex mLock;
sp<DataSource> mDataSource;
+ sp<LiveSource> mLiveSource;
+
sp<ATSParser> mParser;
Vector<sp<AnotherPacketSource> > mSourceImpls;
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 3a20c16..1fb2088 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -42,6 +42,9 @@
size_t cachedSize();
size_t approxDataRemaining(bool *eos);
+ void suspend();
+ void clearCacheAndResume();
+
protected:
virtual ~NuCachedSource2();
@@ -61,6 +64,7 @@
enum {
kWhatFetchMore = 'fetc',
kWhatRead = 'read',
+ kWhatSuspend = 'susp',
};
sp<DataSource> mSource;
@@ -78,10 +82,12 @@
sp<AMessage> mAsyncResult;
bool mFetching;
int64_t mLastFetchTimeUs;
+ bool mSuspended;
void onMessageReceived(const sp<AMessage> &msg);
void onFetch();
void onRead(const sp<AMessage> &msg);
+ void onSuspend();
void fetchInternal();
ssize_t readInternal(off_t offset, void *data, size_t size);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 7c9b83a..c88c6c1 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -49,13 +49,17 @@
unsigned pid, unsigned payload_unit_start_indicator,
ABitReader *br);
- void signalDiscontinuity();
+ void signalDiscontinuity(bool isASeek);
sp<MediaSource> getSource(SourceType type);
+ int64_t convertPTSToTimestamp(uint64_t PTS);
+
private:
unsigned mProgramMapPID;
KeyedVector<unsigned, sp<Stream> > mStreams;
+ bool mFirstPTSValid;
+ uint64_t mFirstPTS;
void parseProgramMap(ABitReader *br);
@@ -63,13 +67,13 @@
};
struct ATSParser::Stream : public RefBase {
- Stream(unsigned elementaryPID, unsigned streamType);
+ Stream(Program *program, unsigned elementaryPID, unsigned streamType);
void parse(
unsigned payload_unit_start_indicator,
ABitReader *br);
- void signalDiscontinuity();
+ void signalDiscontinuity(bool isASeek);
sp<MediaSource> getSource(SourceType type);
@@ -77,6 +81,7 @@
virtual ~Stream();
private:
+ Program *mProgram;
unsigned mElementaryPID;
unsigned mStreamType;
@@ -101,7 +106,9 @@
////////////////////////////////////////////////////////////////////////////////
ATSParser::Program::Program(unsigned programMapPID)
- : mProgramMapPID(programMapPID) {
+ : mProgramMapPID(programMapPID),
+ mFirstPTSValid(false),
+ mFirstPTS(0) {
}
bool ATSParser::Program::parsePID(
@@ -128,9 +135,9 @@
return true;
}
-void ATSParser::Program::signalDiscontinuity() {
+void ATSParser::Program::signalDiscontinuity(bool isASeek) {
for (size_t i = 0; i < mStreams.size(); ++i) {
- mStreams.editValueAt(i)->signalDiscontinuity();
+ mStreams.editValueAt(i)->signalDiscontinuity(isASeek);
}
}
@@ -213,10 +220,12 @@
ssize_t index = mStreams.indexOfKey(elementaryPID);
#if 0 // XXX revisit
CHECK_LT(index, 0);
- mStreams.add(elementaryPID, new Stream(elementaryPID, streamType));
+ mStreams.add(elementaryPID,
+ new Stream(this, elementaryPID, streamType));
#else
if (index < 0) {
- mStreams.add(elementaryPID, new Stream(elementaryPID, streamType));
+ mStreams.add(elementaryPID,
+ new Stream(this, elementaryPID, streamType));
}
#endif
@@ -239,10 +248,26 @@
return NULL;
}
+int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) {
+ if (!mFirstPTSValid) {
+ mFirstPTSValid = true;
+ mFirstPTS = PTS;
+ PTS = 0;
+ } else if (PTS < mFirstPTS) {
+ PTS = 0;
+ } else {
+ PTS -= mFirstPTS;
+ }
+
+ return (PTS * 100) / 9;
+}
+
////////////////////////////////////////////////////////////////////////////////
-ATSParser::Stream::Stream(unsigned elementaryPID, unsigned streamType)
- : mElementaryPID(elementaryPID),
+ATSParser::Stream::Stream(
+ Program *program, unsigned elementaryPID, unsigned streamType)
+ : mProgram(program),
+ mElementaryPID(elementaryPID),
mStreamType(streamType),
mBuffer(new ABuffer(128 * 1024)),
mPayloadStarted(false),
@@ -281,13 +306,21 @@
mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
}
-void ATSParser::Stream::signalDiscontinuity() {
+void ATSParser::Stream::signalDiscontinuity(bool isASeek) {
LOGV("Stream discontinuity");
mPayloadStarted = false;
mBuffer->setRange(0, 0);
mQueue.clear();
+ if (isASeek) {
+ // This is only a "minor" discontinuity, we stay within the same
+ // bitstream.
+
+ mSource->clear();
+ return;
+ }
+
if (mStreamType == 0x1b && mSource != NULL) {
// Don't signal discontinuities on audio streams.
mSource->queueDiscontinuity();
@@ -467,7 +500,7 @@
LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
- int64_t timeUs = (PTS * 100) / 9;
+ int64_t timeUs = mProgram->convertPTSToTimestamp(PTS);
status_t err = mQueue.appendData(data, size, timeUs);
CHECK_EQ(err, (status_t)OK);
@@ -515,9 +548,9 @@
parseTS(&br);
}
-void ATSParser::signalDiscontinuity() {
+void ATSParser::signalDiscontinuity(bool isASeek) {
for (size_t i = 0; i < mPrograms.size(); ++i) {
- mPrograms.editItemAt(i)->signalDiscontinuity();
+ mPrograms.editItemAt(i)->signalDiscontinuity(isASeek);
}
}
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 9ec6d7b..11b1de4 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -33,7 +33,7 @@
ATSParser();
void feedTSPacket(const void *data, size_t size);
- void signalDiscontinuity();
+ void signalDiscontinuity(bool isASeek = false);
enum SourceType {
AVC_VIDEO,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 3f76820..ea747c8 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -91,6 +91,10 @@
return;
}
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("time", &timeUs));
+ LOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", timeUs, timeUs / 1E6);
+
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
@@ -101,10 +105,17 @@
buffer->meta()->setInt32("discontinuity", true);
Mutex::Autolock autoLock(mLock);
+
mBuffers.push_back(buffer);
mCondition.signal();
}
+void AnotherPacketSource::clear() {
+ Mutex::Autolock autoLock(mLock);
+ mBuffers.clear();
+ mEOSResult = OK;
+}
+
void AnotherPacketSource::signalEOS(status_t result) {
CHECK(result != OK);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 6b43c4e..6999175 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -43,6 +43,8 @@
void queueDiscontinuity();
void signalEOS(status_t result);
+ void clear();
+
protected:
virtual ~AnotherPacketSource();
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index a13287e..b0b9e66 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -42,6 +42,7 @@
void ElementaryStreamQueue::clear() {
mBuffer->setRange(0, 0);
+ mTimestamps.clear();
mFormat.clear();
}
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 0d96bd1..3176810 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -19,8 +19,11 @@
#include <utils/Log.h>
#include "include/MPEG2TSExtractor.h"
+#include "include/LiveSource.h"
+#include "include/NuCachedSource2.h"
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -37,7 +40,8 @@
struct MPEG2TSSource : public MediaSource {
MPEG2TSSource(
const sp<MPEG2TSExtractor> &extractor,
- const sp<AnotherPacketSource> &impl);
+ const sp<AnotherPacketSource> &impl,
+ bool seekable);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -50,14 +54,20 @@
sp<MPEG2TSExtractor> mExtractor;
sp<AnotherPacketSource> mImpl;
+ // If there are both audio and video streams, only the video stream
+ // will be seekable, otherwise the single stream will be seekable.
+ bool mSeekable;
+
DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSSource);
};
MPEG2TSSource::MPEG2TSSource(
const sp<MPEG2TSExtractor> &extractor,
- const sp<AnotherPacketSource> &impl)
+ const sp<AnotherPacketSource> &impl,
+ bool seekable)
: mExtractor(extractor),
- mImpl(impl) {
+ mImpl(impl),
+ mSeekable(seekable) {
}
status_t MPEG2TSSource::start(MetaData *params) {
@@ -69,13 +79,27 @@
}
sp<MetaData> MPEG2TSSource::getFormat() {
- return mImpl->getFormat();
+ sp<MetaData> meta = mImpl->getFormat();
+
+ int64_t durationUs;
+ if (mExtractor->mLiveSource != NULL
+ && mExtractor->mLiveSource->getDuration(&durationUs)) {
+ meta->setInt64(kKeyDuration, durationUs);
+ }
+
+ return meta;
}
status_t MPEG2TSSource::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode seekMode;
+ if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+ mExtractor->seekTo(seekTimeUs);
+ }
+
status_t finalResult;
while (!mImpl->hasBufferAvailable(&finalResult)) {
if (finalResult != OK) {
@@ -109,7 +133,20 @@
return NULL;
}
- return new MPEG2TSSource(this, mSourceImpls.editItemAt(index));
+ bool seekable = true;
+ if (mSourceImpls.size() > 1) {
+ CHECK_EQ(mSourceImpls.size(), 2u);
+
+ sp<MetaData> meta = mSourceImpls.editItemAt(index)->getFormat();
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!strncasecmp("audio/", mime, 6)) {
+ seekable = false;
+ }
+ }
+
+ return new MPEG2TSSource(this, mSourceImpls.editItemAt(index), seekable);
}
sp<MetaData> MPEG2TSExtractor::getTrackMetaData(
@@ -189,6 +226,46 @@
return OK;
}
+void MPEG2TSExtractor::setLiveSource(const sp<LiveSource> &liveSource) {
+ Mutex::Autolock autoLock(mLock);
+
+ mLiveSource = liveSource;
+}
+
+void MPEG2TSExtractor::seekTo(int64_t seekTimeUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mLiveSource == NULL) {
+ return;
+ }
+
+ if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+ static_cast<NuCachedSource2 *>(mDataSource.get())->suspend();
+ }
+
+ if (mLiveSource->seekTo(seekTimeUs)) {
+ mParser->signalDiscontinuity(true /* isSeek */);
+ mOffset = 0;
+ }
+
+ if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+ static_cast<NuCachedSource2 *>(mDataSource.get())
+ ->clearCacheAndResume();
+ }
+}
+
+uint32_t MPEG2TSExtractor::flags() const {
+ Mutex::Autolock autoLock(mLock);
+
+ uint32_t flags = CAN_PAUSE;
+
+ if (mLiveSource != NULL && mLiveSource->isSeekable()) {
+ flags |= CAN_SEEK_FORWARD | CAN_SEEK_BACKWARD | CAN_SEEK;
+ }
+
+ return flags;
+}
+
////////////////////////////////////////////////////////////////////////////////
bool SniffMPEG2TS(
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index ded3b24..5a1ea5c 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -59,7 +59,8 @@
sp<AMessage> mNotifyMsg;
KeyedVector<uint32_t, sp<ARTPSource> > mSources;
- int32_t mNumRTCPPacketsReceived;
+ int64_t mNumRTCPPacketsReceived;
+ int64_t mNumRTPPacketsReceived;
struct sockaddr_in mRemoteRTCPAddr;
bool mIsInjected;
@@ -168,6 +169,12 @@
break;
}
+ case kWhatFakeTimestamps:
+ {
+ onFakeTimestamps();
+ break;
+ }
+
default:
{
TRESPASS();
@@ -199,6 +206,7 @@
CHECK(msg->findMessage("notify", &info->mNotifyMsg));
info->mNumRTCPPacketsReceived = 0;
+ info->mNumRTPPacketsReceived = 0;
memset(&info->mRemoteRTCPAddr, 0, sizeof(info->mRemoteRTCPAddr));
if (!injected) {
@@ -373,6 +381,12 @@
}
status_t ARTPConnection::parseRTP(StreamInfo *s, const sp<ABuffer> &buffer) {
+ if (s->mNumRTPPacketsReceived++ == 0) {
+ sp<AMessage> notify = s->mNotifyMsg->dup();
+ notify->setInt32("first-rtp", true);
+ notify->post();
+ }
+
size_t size = buffer->size();
if (size < 12) {
@@ -638,5 +652,27 @@
}
}
+void ARTPConnection::fakeTimestamps() {
+ (new AMessage(kWhatFakeTimestamps, id()))->post();
+}
+
+void ARTPConnection::onFakeTimestamps() {
+ List<StreamInfo>::iterator it = mStreams.begin();
+ while (it != mStreams.end()) {
+ StreamInfo &info = *it++;
+
+ for (size_t j = 0; j < info.mSources.size(); ++j) {
+ sp<ARTPSource> source = info.mSources.valueAt(j);
+
+ if (!source->timeEstablished()) {
+ source->timeUpdate(0, 0);
+ source->timeUpdate(0 + 90000, 0x100000000ll);
+
+ mFlags |= kFakeTimestamps;
+ }
+ }
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h
index 77f81fa..a17b382 100644
--- a/media/libstagefright/rtsp/ARTPConnection.h
+++ b/media/libstagefright/rtsp/ARTPConnection.h
@@ -51,6 +51,8 @@
static void MakePortPair(
int *rtpSocket, int *rtcpSocket, unsigned *rtpPort);
+ void fakeTimestamps();
+
protected:
virtual ~ARTPConnection();
virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -61,6 +63,7 @@
kWhatRemoveStream,
kWhatPollStreams,
kWhatInjectPacket,
+ kWhatFakeTimestamps,
};
static const int64_t kSelectTimeoutUs;
@@ -78,6 +81,7 @@
void onPollStreams();
void onInjectPacket(const sp<AMessage> &msg);
void onSendReceiverReports();
+ void onFakeTimestamps();
status_t receive(StreamInfo *info, bool receiveRTP);
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 0f4c1f3..1bc9925 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -105,7 +105,9 @@
mCheckPending(false),
mCheckGeneration(0),
mTryTCPInterleaving(false),
+ mTryFakeRTCP(false),
mReceivedFirstRTCPPacket(false),
+ mReceivedFirstRTPPacket(false),
mSeekable(false) {
mNetLooper->setName("rtsp net");
mNetLooper->start(false /* runOnCallingThread */,
@@ -534,6 +536,7 @@
mFirstAccessUnitNTP = 0;
mNumAccessUnitsReceived = 0;
mReceivedFirstRTCPPacket = false;
+ mReceivedFirstRTPPacket = false;
mSeekable = false;
sp<AMessage> reply = new AMessage('tear', id());
@@ -611,12 +614,17 @@
case 'accu':
{
- int32_t firstRTCP;
- if (msg->findInt32("first-rtcp", &firstRTCP)) {
+ int32_t first;
+ if (msg->findInt32("first-rtcp", &first)) {
mReceivedFirstRTCPPacket = true;
break;
}
+ if (msg->findInt32("first-rtp", &first)) {
+ mReceivedFirstRTPPacket = true;
+ break;
+ }
+
++mNumAccessUnitsReceived;
postAccessUnitTimeoutCheck();
@@ -839,9 +847,17 @@
case 'tiou':
{
if (!mReceivedFirstRTCPPacket) {
- if (mTryTCPInterleaving) {
+ if (mTryFakeRTCP) {
LOGW("Never received any data, disconnecting.");
(new AMessage('abor', id()))->post();
+ } else if (mTryTCPInterleaving && mReceivedFirstRTPPacket) {
+ LOGW("We received RTP packets but no RTCP packets, "
+ "using fake timestamps.");
+
+ mTryFakeRTCP = true;
+
+ mReceivedFirstRTCPPacket = true;
+ mRTPConn->fakeTimestamps();
} else {
LOGW("Never received any data, switching transports.");
@@ -987,7 +1003,9 @@
bool mCheckPending;
int32_t mCheckGeneration;
bool mTryTCPInterleaving;
+ bool mTryFakeRTCP;
bool mReceivedFirstRTCPPacket;
+ bool mReceivedFirstRTPPacket;
bool mSeekable;
struct TrackInfo {
diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h
index c202693..bad24913 100644
--- a/native/include/android/storage_manager.h
+++ b/native/include/android/storage_manager.h
@@ -74,10 +74,10 @@
AOBB_STATE_ERROR_ALREADY_MOUNTED = 24,
/*
- * The current application does not have permission to use this OBB
- * because the OBB indicates it's owned by a different package or the
- * key used to open it is incorrect. Can be returned as the status for
- * callbacks made during asynchronous OBB actions.
+ * The current application does not have permission to use this OBB.
+ * This could be because the OBB indicates it's owned by a different
+ * package. Can be returned as the status for callbacks made during
+ * asynchronous OBB actions.
*/
AOBB_STATE_ERROR_PERMISSION_DENIED = 25,
};
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1df583a..9d7c58e 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -5156,9 +5156,6 @@
// we don't consider this to be a failure of the core package deletion
}
}
- if (libraryPath != null) {
- NativeLibraryHelper.removeNativeBinariesLI(libraryPath);
- }
}
private boolean setPermissions() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 26071ae..a2a5e67 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,6 +17,7 @@
package com.android.server;
import com.android.server.am.ActivityManagerService;
+import com.android.internal.app.ShutdownThread;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
import com.trustedlogic.trustednfc.android.server.NfcService;
@@ -89,6 +90,24 @@
BinderInternal.disableBackgroundScheduling(true);
android.os.Process.setCanSelfBackground(false);
+ // Check whether we failed to shut down last time we tried.
+ {
+ final String shutdownAction = SystemProperties.get(
+ ShutdownThread.SHUTDOWN_ACTION_PROPERTY, "");
+ if (shutdownAction != null && shutdownAction.length() > 0) {
+ boolean reboot = (shutdownAction.charAt(0) == '1');
+
+ final String reason;
+ if (shutdownAction.length() > 1) {
+ reason = shutdownAction.substring(1, shutdownAction.length());
+ } else {
+ reason = null;
+ }
+
+ ShutdownThread.rebootOrShutdown(reboot, reason);
+ }
+ }
+
String factoryTestStr = SystemProperties.get("ro.factorytest");
int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF
: Integer.parseInt(factoryTestStr);
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index 43e8467..1d93f82 100755
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -590,9 +590,7 @@
jboolean connected, int type, jboolean roaming, jstring extraInfo)
{
const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
- if (interface &&
- (interface->size > ((char *)&interface->update_network_state - (char *)&interface)) &&
- interface->update_network_state) {
+ if (interface && interface->update_network_state) {
if (extraInfo) {
const char *extraInfoStr = env->GetStringUTFChars(extraInfo, NULL);
interface->update_network_state(connected, type, roaming, extraInfoStr);
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index f71ebb9..873ebac 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -871,6 +871,15 @@
error.string());
goto bail;
}
+ } else if (tag == "uses-gl-texture") {
+ String8 name = getAttribute(tree, NAME_ATTR, &error);
+ if (name != "" && error == "") {
+ printf("uses-gl-texture:'%s'\n", name.string());
+ } else {
+ fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
+ error.string());
+ goto bail;
+ }
}
} else if (depth == 3 && withinApplication) {
withinActivity = false;