Merge "MediaPlayer2: allow reusing DataSourceDesc"
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index aca8dbe..e29bd00 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -44,6 +44,8 @@
private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
+ private int mCount = 0;
+ private boolean mClosed = false;
private FileDataSourceDesc() {
super();
@@ -55,23 +57,48 @@
@Override
void close() {
super.close();
- closeFD();
+ decCount();
}
/**
- * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+ * Decrements usage count by {@link MediaPlayer2}.
+ * If this is the last usage, also releases the file descriptor held by this
+ * {@code FileDataSourceDesc} object.
*/
- void closeFD() {
+ void decCount() {
synchronized (this) {
- if (mPFD != null) {
- try {
- mPFD.close();
- } catch (IOException e) {
- Log.e(TAG, "failed to close pfd: " + e);
- }
-
- mPFD = null;
+ --mCount;
+ if (mCount > 0) {
+ return;
}
+
+ try {
+ mPFD.close();
+ mClosed = true;
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close pfd: " + e);
+ }
+ }
+ }
+
+ /**
+ * Increments usage count by {@link MediaPlayer2} if PFD has not been closed.
+ */
+ void incCount() {
+ synchronized (this) {
+ if (!mClosed) {
+ ++mCount;
+ }
+ }
+ }
+
+ /**
+ * Return the status of underline ParcelFileDescriptor
+ * @return true if underline ParcelFileDescriptor is closed, false otherwise.
+ */
+ boolean isPFDClosed() {
+ synchronized (this) {
+ return mClosed;
}
}
@@ -150,6 +177,16 @@
* @return a new {@link FileDataSourceDesc} object
*/
public @NonNull FileDataSourceDesc build() {
+ if (mPFD == null) {
+ throw new IllegalStateException(
+ "underline ParcelFileDescriptor should not be null");
+ }
+ try {
+ mPFD.getFd();
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException("ParcelFileDescriptor has been closed");
+ }
+
FileDataSourceDesc dsd = new FileDataSourceDesc();
super.build(dsd);
dsd.mPFD = mPFD;
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index b137ce2..a3702d6 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -696,7 +696,7 @@
return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
void process() throws IOException {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkDataSourceDesc(dsd);
int state = getState();
try {
if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
@@ -729,7 +729,7 @@
return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkDataSourceDesc(dsd);
synchronized (mSrcLock) {
clearNextSourceInfos_l();
mNextSourceInfos.add(new SourceInfo(dsd));
@@ -755,15 +755,35 @@
if (dsds == null || dsds.size() == 0) {
throw new IllegalArgumentException("data source list cannot be null or empty.");
}
+ boolean hasError = false;
+ for (DataSourceDesc dsd : dsds) {
+ if (dsd != null) {
+ hasError = true;
+ continue;
+ }
+ if (dsd instanceof FileDataSourceDesc) {
+ FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+ if (fdsd.isPFDClosed()) {
+ hasError = true;
+ continue;
+ }
+
+ fdsd.incCount();
+ }
+ }
+ if (hasError) {
+ for (DataSourceDesc dsd : dsds) {
+ if (dsd != null) {
+ dsd.close();
+ }
+ }
+ throw new IllegalArgumentException("invalid data source list");
+ }
synchronized (mSrcLock) {
clearNextSourceInfos_l();
for (DataSourceDesc dsd : dsds) {
- if (dsd != null) {
- mNextSourceInfos.add(new SourceInfo(dsd));
- } else {
- Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
- }
+ mNextSourceInfos.add(new SourceInfo(dsd));
}
}
prepareNextDataSource();
@@ -771,6 +791,20 @@
});
}
+ // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed.
+ private void checkDataSourceDesc(DataSourceDesc dsd) {
+ if (dsd != null) {
+ throw new IllegalArgumentException("dsd is expected to be non null");
+ }
+ if (dsd instanceof FileDataSourceDesc) {
+ FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+ if (fdsd.isPFDClosed()) {
+ throw new IllegalArgumentException("the underline FileDescriptor has been closed");
+ }
+ fdsd.incCount();
+ }
+ }
+
/**
* Removes all data sources pending to be played.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.