Commit b625d909 authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

Remove MediaResourceGetter

Before playing media using the MediaPlayerBridge, we try to parse the
metadata, using the MediaResourceGetter. This class uses the Android
MediaMetadataRetriever API, which can be be resource intensive. We
eventually get all of the metadata we need from the Android
MediaPlayer, and pre-parsing the metadata sometimes does nothing but
delay the start of playback.

This CL removes the java MediaResourceGetter, and removes metadata
extraction capabilities from the C++ portion of the
MediaResourceGetter interface.

NB: This is unlikely to have a performance effect on HLS playback,
since the MediaResourceGetter did not support parsing HLS.

Bug: 739914
Change-Id: I460d5aea02bf1a0bf099f66a781a40ca6374d4d0
Reviewed-on: https://chromium-review.googlesource.com/988681Reviewed-by: default avatarPeter Wen <wnwen@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548221}
parent b5db77f5
...@@ -267,9 +267,6 @@ Still reading? ...@@ -267,9 +267,6 @@ Still reading?
<issue id="RtlCompat" severity="ignore"/> <issue id="RtlCompat" severity="ignore"/>
<issue id="RtlEnabled" severity="ignore"/> <issue id="RtlEnabled" severity="ignore"/>
<issue id="RtlSymmetry" severity="ignore"/> <issue id="RtlSymmetry" severity="ignore"/>
<issue id="SdCardPath">
<ignore regexp="content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java"/>
</issue>
<issue id="SetJavaScriptEnabled" severity="ignore"/> <issue id="SetJavaScriptEnabled" severity="ignore"/>
<issue id="SignatureOrSystemPermissions" severity="ignore"/> <issue id="SignatureOrSystemPermissions" severity="ignore"/>
<issue id="SpUsage" severity="Error"> <issue id="SpUsage" severity="Error">
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
#include "content/browser/media/android/media_resource_getter_impl.h" #include "content/browser/media/android/media_resource_getter_impl.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/path_service.h" #include "base/path_service.h"
...@@ -20,7 +17,6 @@ ...@@ -20,7 +17,6 @@
#include "content/public/browser/storage_partition.h" #include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h" #include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h" #include "content/public/common/url_constants.h"
#include "jni/MediaResourceGetter_jni.h"
#include "media/base/android/media_url_interceptor.h" #include "media/base/android/media_url_interceptor.h"
#include "net/base/auth.h" #include "net/base/auth.h"
#include "net/cookies/canonical_cookie.h" #include "net/cookies/canonical_cookie.h"
...@@ -31,10 +27,6 @@ ...@@ -31,10 +27,6 @@
#include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h" #include "url/gurl.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
namespace content { namespace content {
namespace { namespace {
...@@ -112,57 +104,6 @@ static void RequestPlaformPathFromFileSystemURL( ...@@ -112,57 +104,6 @@ static void RequestPlaformPathFromFileSystemURL(
ReturnResultOnUIThread(std::move(callback), std::string()); ReturnResultOnUIThread(std::move(callback), std::string());
} }
// Posts a task to the UI thread to run the callback function.
static void PostMediaMetadataCallbackTask(
media::MediaResourceGetter::ExtractMediaMetadataCB callback,
JNIEnv* env,
ScopedJavaLocalRef<jobject>& j_metadata) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(
std::move(callback),
base::TimeDelta::FromMilliseconds(
Java_MediaMetadata_getDurationInMilliseconds(env, j_metadata)),
Java_MediaMetadata_getWidth(env, j_metadata),
Java_MediaMetadata_getHeight(env, j_metadata),
Java_MediaMetadata_isSuccess(env, j_metadata)));
}
// Gets the metadata from a media URL. When finished, a task is posted to the UI
// thread to run the callback function.
static void GetMediaMetadata(
const std::string& url,
const std::string& cookies,
const std::string& user_agent,
media::MediaResourceGetter::ExtractMediaMetadataCB callback) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url);
ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(env, cookies);
ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
env, user_agent);
ScopedJavaLocalRef<jobject> j_metadata =
Java_MediaResourceGetter_extractMediaMetadata(env, j_url_string,
j_cookies, j_user_agent);
PostMediaMetadataCallbackTask(std::move(callback), env, j_metadata);
}
// Gets the metadata from a file descriptor. When finished, a task is posted to
// the UI thread to run the callback function.
static void GetMediaMetadataFromFd(
const int fd,
const int64_t offset,
const int64_t size,
media::MediaResourceGetter::ExtractMediaMetadataCB callback) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_metadata =
Java_MediaResourceGetter_extractMediaMetadataFromFd(
env, fd, offset, size);
PostMediaMetadataCallbackTask(std::move(callback), env, j_metadata);
}
// The task object that retrieves media resources on the IO thread. // The task object that retrieves media resources on the IO thread.
// TODO(qinmin): refactor this class to make the code reusable by others as // TODO(qinmin): refactor this class to make the code reusable by others as
// there are lots of duplicated functionalities elsewhere. // there are lots of duplicated functionalities elsewhere.
...@@ -310,28 +251,4 @@ void MediaResourceGetterImpl::GetPlatformPathCallback( ...@@ -310,28 +251,4 @@ void MediaResourceGetterImpl::GetPlatformPathCallback(
std::move(callback).Run(platform_path); std::move(callback).Run(platform_path);
} }
void MediaResourceGetterImpl::ExtractMediaMetadata(
const std::string& url,
const std::string& cookies,
const std::string& user_agent,
ExtractMediaMetadataCB callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::PostTaskWithTraits(FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&GetMediaMetadata, url, cookies,
user_agent, std::move(callback)));
}
void MediaResourceGetterImpl::ExtractMediaMetadata(
const int fd,
const int64_t offset,
const int64_t size,
ExtractMediaMetadataCB callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::PostTaskWithTraits(FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&GetMediaMetadataFromFd, fd, offset,
size, std::move(callback)));
}
} // namespace content } // namespace content
...@@ -49,14 +49,6 @@ class MediaResourceGetterImpl : public media::MediaResourceGetter { ...@@ -49,14 +49,6 @@ class MediaResourceGetterImpl : public media::MediaResourceGetter {
GetCookieCB callback) override; GetCookieCB callback) override;
void GetPlatformPathFromURL(const GURL& url, void GetPlatformPathFromURL(const GURL& url,
GetPlatformPathCB callback) override; GetPlatformPathCB callback) override;
void ExtractMediaMetadata(const std::string& url,
const std::string& cookies,
const std::string& user_agent,
ExtractMediaMetadataCB callback) override;
void ExtractMediaMetadata(const int fd,
const int64_t offset,
const int64_t size,
ExtractMediaMetadataCB callback) override;
private: private:
// Called when GetAuthCredentials() finishes. // Called when GetAuthCredentials() finishes.
......
...@@ -131,7 +131,6 @@ android_library("content_java") { ...@@ -131,7 +131,6 @@ android_library("content_java") {
"java/src/org/chromium/content/browser/JavascriptInterface.java", "java/src/org/chromium/content/browser/JavascriptInterface.java",
"java/src/org/chromium/content/browser/JoystickHandler.java", "java/src/org/chromium/content/browser/JoystickHandler.java",
"java/src/org/chromium/content/browser/LauncherThread.java", "java/src/org/chromium/content/browser/LauncherThread.java",
"java/src/org/chromium/content/browser/MediaResourceGetter.java",
"java/src/org/chromium/content/browser/MediaSessionImpl.java", "java/src/org/chromium/content/browser/MediaSessionImpl.java",
"java/src/org/chromium/content/browser/MemoryMonitorAndroid.java", "java/src/org/chromium/content/browser/MemoryMonitorAndroid.java",
"java/src/org/chromium/content/browser/MotionEventSynthesizer.java", "java/src/org/chromium/content/browser/MotionEventSynthesizer.java",
...@@ -366,7 +365,6 @@ generate_jni("content_jni_headers") { ...@@ -366,7 +365,6 @@ generate_jni("content_jni_headers") {
"java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java", "java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java",
"java/src/org/chromium/content/browser/JavascriptInjectorImpl.java", "java/src/org/chromium/content/browser/JavascriptInjectorImpl.java",
"java/src/org/chromium/content/browser/LauncherThread.java", "java/src/org/chromium/content/browser/LauncherThread.java",
"java/src/org/chromium/content/browser/MediaResourceGetter.java",
"java/src/org/chromium/content/browser/MediaSessionImpl.java", "java/src/org/chromium/content/browser/MediaSessionImpl.java",
"java/src/org/chromium/content/browser/MemoryMonitorAndroid.java", "java/src/org/chromium/content/browser/MemoryMonitorAndroid.java",
"java/src/org/chromium/content/browser/NfcHost.java", "java/src/org/chromium/content/browser/NfcHost.java",
...@@ -470,7 +468,6 @@ android_library("content_javatests") { ...@@ -470,7 +468,6 @@ android_library("content_javatests") {
"javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java", "javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java",
"javatests/src/org/chromium/content/browser/JavaBridgeFieldsTest.java", "javatests/src/org/chromium/content/browser/JavaBridgeFieldsTest.java",
"javatests/src/org/chromium/content/browser/JavaBridgeReturnValuesTest.java", "javatests/src/org/chromium/content/browser/JavaBridgeReturnValuesTest.java",
"javatests/src/org/chromium/content/browser/MediaResourceGetterTest.java",
"javatests/src/org/chromium/content/browser/MediaSessionTest.java", "javatests/src/org/chromium/content/browser/MediaSessionTest.java",
"javatests/src/org/chromium/content/browser/NavigationTest.java", "javatests/src/org/chromium/content/browser/NavigationTest.java",
"javatests/src/org/chromium/content/browser/PopupZoomerTest.java", "javatests/src/org/chromium/content/browser/PopupZoomerTest.java",
......
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.MediaMetadataRetriever;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.PathUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Java counterpart of android MediaResourceGetter.
*/
@JNINamespace("content")
class MediaResourceGetter {
private static final String TAG = "cr_MediaResource";
private static final MediaMetadata EMPTY_METADATA = new MediaMetadata(0, 0, 0, false);
private final MediaMetadataRetriever mRetriever = new MediaMetadataRetriever();
@VisibleForTesting
static class MediaMetadata {
private final int mDurationInMilliseconds;
private final int mWidth;
private final int mHeight;
private final boolean mSuccess;
MediaMetadata(int durationInMilliseconds, int width, int height, boolean success) {
mDurationInMilliseconds = durationInMilliseconds;
mWidth = width;
mHeight = height;
mSuccess = success;
}
// TODO(andrewhayden): according to the spec, if duration is unknown
// then we must return NaN. If it is unbounded, then positive infinity.
// http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html
@CalledByNative("MediaMetadata")
int getDurationInMilliseconds() {
return mDurationInMilliseconds;
}
@CalledByNative("MediaMetadata")
int getWidth() {
return mWidth;
}
@CalledByNative("MediaMetadata")
int getHeight() {
return mHeight;
}
@CalledByNative("MediaMetadata")
boolean isSuccess() {
return mSuccess;
}
@Override
public String toString() {
return "MediaMetadata["
+ "durationInMilliseconds=" + mDurationInMilliseconds
+ ", width=" + mWidth
+ ", height=" + mHeight
+ ", success=" + mSuccess
+ "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mDurationInMilliseconds;
result = prime * result + mHeight;
result = prime * result + (mSuccess ? 1231 : 1237);
result = prime * result + mWidth;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
MediaMetadata other = (MediaMetadata) obj;
if (mDurationInMilliseconds != other.mDurationInMilliseconds) return false;
if (mHeight != other.mHeight) return false;
if (mSuccess != other.mSuccess) return false;
if (mWidth != other.mWidth) return false;
return true;
}
}
@CalledByNative
private static MediaMetadata extractMediaMetadata(
final String url, final String cookies, final String userAgent) {
return new MediaResourceGetter().extract(
ContextUtils.getApplicationContext(), url, cookies, userAgent);
}
@CalledByNative
private static MediaMetadata extractMediaMetadataFromFd(int fd,
long offset,
long length) {
return new MediaResourceGetter().extract(fd, offset, length);
}
@VisibleForTesting
MediaMetadata extract(int fd, long offset, long length) {
configure(fd, offset, length);
return doExtractMetadata();
}
@VisibleForTesting
MediaMetadata extract(final Context context, final String url,
final String cookies, final String userAgent) {
if (!configure(context, url, cookies, userAgent)) {
Log.e(TAG, "Unable to configure metadata extractor");
return EMPTY_METADATA;
}
return doExtractMetadata();
}
private MediaMetadata doExtractMetadata() {
try {
String durationString = extractMetadata(
MediaMetadataRetriever.METADATA_KEY_DURATION);
if (durationString == null) {
Log.w(TAG, "missing duration metadata");
return EMPTY_METADATA;
}
int durationMillis = 0;
try {
durationMillis = Integer.parseInt(durationString);
} catch (NumberFormatException e) {
Log.w(TAG, "non-numeric duration: %s", durationString);
return EMPTY_METADATA;
}
int width = 0;
int height = 0;
boolean hasVideo = "yes".equals(extractMetadata(
MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO));
Log.d(TAG, (hasVideo ? "resource has video" : "resource doesn't have video"));
if (hasVideo) {
String widthString = extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
if (widthString == null) {
Log.w(TAG, "missing video width metadata");
return EMPTY_METADATA;
}
try {
width = Integer.parseInt(widthString);
} catch (NumberFormatException e) {
Log.w(TAG, "non-numeric width: %s", widthString);
return EMPTY_METADATA;
}
String heightString = extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
if (heightString == null) {
Log.w(TAG, "missing video height metadata");
return EMPTY_METADATA;
}
try {
height = Integer.parseInt(heightString);
} catch (NumberFormatException e) {
Log.w(TAG, "non-numeric height: %s", heightString);
return EMPTY_METADATA;
}
}
MediaMetadata result = new MediaMetadata(durationMillis, width, height, true);
Log.d(TAG, "extracted valid metadata: %s", result);
return result;
} catch (RuntimeException e) {
Log.e(TAG, "Unable to extract metadata: %s", e);
return EMPTY_METADATA;
}
}
@VisibleForTesting
boolean configure(Context context, String url, String cookies, String userAgent) {
URI uri;
try {
uri = URI.create(url);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Cannot parse uri: %s", e);
return false;
}
String scheme = uri.getScheme();
if (scheme == null || scheme.equals("file")) {
File file = uriToFile(uri.getPath());
if (!file.exists()) {
Log.e(TAG, "File does not exist.");
return false;
}
if (!filePathAcceptable(file, context)) {
Log.e(TAG, "Refusing to read from unsafe file location.");
return false;
}
try {
configure(file.getAbsolutePath());
return true;
} catch (RuntimeException e) {
Log.e(TAG, "Error configuring data source: %s", e);
return false;
}
}
if (scheme.equals("content")) {
try {
configure(context, Uri.parse(uri.toString()));
return true;
} catch (RuntimeException e) {
Log.e(TAG, "Error configuring data source: %s", e);
return false;
}
}
if (uri.getPath() != null && uri.getPath().endsWith(".m3u8")) {
// MediaMetadataRetriever does not work with HLS correctly.
return false;
}
final String host = uri.getHost();
if (!isLoopbackAddress(host) && !isNetworkReliable(context)) {
Log.w(TAG, "non-file URI can't be read due to unsuitable network conditions");
return false;
}
Map<String, String> headersMap = new HashMap<String, String>();
if (!TextUtils.isEmpty(cookies)) {
headersMap.put("Cookie", cookies);
}
if (!TextUtils.isEmpty(userAgent)) {
headersMap.put("User-Agent", userAgent);
}
try {
configure(url, headersMap);
return true;
} catch (RuntimeException e) {
Log.e(TAG, "Error configuring data source: %s", e);
return false;
}
}
/**
* @return true if the device is on an ethernet or wifi network.
* If anything goes wrong (e.g., permission denied while trying to access
* the network state), returns false.
*/
@VisibleForTesting
boolean isNetworkReliable(Context context) {
if (context.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "permission denied to access network state");
return false;
}
Integer networkType = getNetworkType(context);
if (networkType == null) {
return false;
}
switch (networkType.intValue()) {
case ConnectivityManager.TYPE_ETHERNET:
case ConnectivityManager.TYPE_WIFI:
Log.d(TAG, "ethernet/wifi connection detected");
return true;
case ConnectivityManager.TYPE_WIMAX:
case ConnectivityManager.TYPE_MOBILE:
default:
Log.d(TAG, "no ethernet/wifi connection detected");
return false;
}
}
// This method covers only typcial expressions for the loopback address
// to resolve the hostname without a DNS loopup.
private boolean isLoopbackAddress(String host) {
return host != null && (host.equalsIgnoreCase("localhost") // typical hostnames
|| host.equalsIgnoreCase("localhost.localdomain")
|| host.equalsIgnoreCase("localhost6")
|| host.equalsIgnoreCase("localhost6.localdomain6")
|| host.toLowerCase(Locale.US).endsWith(".localhost")
|| host.equals("127.0.0.1") // typical IP v4 expression
|| host.equals("[::1]")); // typical IP v6 expression
}
/**
* @param file the file whose path should be checked
* @return true if and only if the file is in a location that we consider
* safe to read from, such as /mnt/sdcard.
*/
@VisibleForTesting
boolean filePathAcceptable(File file, Context context) {
final String path;
try {
path = file.getCanonicalPath();
} catch (IOException e) {
// Canonicalization has failed. Assume malicious, give up.
Log.w(TAG, "canonicalization of file path failed");
return false;
}
// In order to properly match the roots we must also canonicalize the
// well-known paths we are matching against. If we don't, then we can
// get unusual results in testing systems or possibly on rooted devices.
// Note that canonicalized directory paths always end with '/'.
List<String> acceptablePaths = canonicalize(getRawAcceptableDirectories(context));
acceptablePaths.add(getExternalStorageDirectory());
Log.d(TAG, "canonicalized file path: %s", path);
for (String acceptablePath : acceptablePaths) {
if (path.startsWith(acceptablePath)) {
return true;
}
}
return false;
}
// The methods below can be used by unit tests to fake functionality.
@VisibleForTesting
File uriToFile(String path) {
return new File(path);
}
@VisibleForTesting
Integer getNetworkType(Context context) {
// TODO(qinmin): use ConnectionTypeObserver to listen to the network type change.
ConnectivityManager mConnectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (mConnectivityManager == null) {
Log.w(TAG, "no connectivity manager available");
return null;
}
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
if (info == null) {
Log.d(TAG, "no active network");
return null;
}
return info.getType();
}
@SuppressLint("SdCardPath")
private List<String> getRawAcceptableDirectories(Context context) {
List<String> result = new ArrayList<String>();
result.add("/mnt/sdcard/");
result.add("/sdcard/");
result.add("/data/data/" + context.getPackageName() + "/cache/");
return result;
}
private List<String> canonicalize(List<String> paths) {
List<String> result = new ArrayList<String>(paths.size());
try {
for (String path : paths) {
result.add(new File(path).getCanonicalPath());
}
return result;
} catch (IOException e) {
// Canonicalization has failed. Assume malicious, give up.
Log.w(TAG, "canonicalization of file path failed");
}
return result;
}
@VisibleForTesting
String getExternalStorageDirectory() {
return PathUtils.getExternalStorageDirectory();
}
@VisibleForTesting
void configure(int fd, long offset, long length) {
ParcelFileDescriptor parcelFd = ParcelFileDescriptor.adoptFd(fd);
try {
mRetriever.setDataSource(parcelFd.getFileDescriptor(),
offset, length);
} finally {
try {
parcelFd.close();
} catch (IOException e) {
Log.e(TAG, "Failed to close file descriptor: %s", e);
}
}
}
@VisibleForTesting
void configure(String url, Map<String, String> headers) {
mRetriever.setDataSource(url, headers);
}
@VisibleForTesting
void configure(String path) {
mRetriever.setDataSource(path);
}
@VisibleForTesting
void configure(Context context, Uri uri) {
mRetriever.setDataSource(context, uri);
}
@VisibleForTesting
String extractMetadata(int key) {
return mRetriever.extractMetadata(key);
}
}
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.MediaMetadataRetriever;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.support.test.filters.SmallTest;
import android.test.mock.MockContext;
import android.util.SparseArray;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.content.browser.MediaResourceGetter.MediaMetadata;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Tests for MediaResourceGetter.
*/
@RunWith(BaseJUnit4ClassRunner.class)
@SuppressLint("SdCardPath")
public class MediaResourceGetterTest {
private static final String TEST_HTTP_URL = "http://example.com";
private static final String TEST_USER_AGENT = // Anything, really
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 "
+ "(KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36";
private static final String TEST_FILE_PATH = "/mnt/sdcard/test";
private static final String TEST_FILE_URL = "file://" + TEST_FILE_PATH;
private static final String TEST_CONTENT_URI =
"content://com.android.providers.media.documents/document/video:113";
private static final String TEST_COOKIES = "yum yum yum!";
private static final MediaMetadata sEmptyMetadata = new MediaMetadata(0, 0, 0, false);
private static final String sExternalStorageDirectory = "/test_external_storage";
private static final Map<String, String> sHeadersCookieOnly;
private static final Map<String, String> sHeadersCookieAndUA;
private static final Map<String, String> sHeadersUAOnly;
static {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Cookie", TEST_COOKIES);
sHeadersCookieOnly = Collections.unmodifiableMap(headers);
headers = new HashMap<String, String>();
headers.put("User-Agent", TEST_USER_AGENT);
sHeadersUAOnly = Collections.unmodifiableMap(headers);
headers = new HashMap<String, String>();
headers.putAll(sHeadersCookieOnly);
headers.putAll(sHeadersUAOnly);
sHeadersCookieAndUA = Collections.unmodifiableMap(headers);
}
private static class FakeFile extends File {
final boolean mExists;
public FakeFile(String path, boolean exists) {
super(path);
mExists = exists;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + (mExists ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
FakeFile other = (FakeFile) obj;
if (mExists != other.mExists) return false;
return true;
}
@Override
public boolean exists() {
return mExists;
}
}
/**
* Helper class that fakes functionality we cannot perform without real
* media resources. The class being faked here has been architected
* carefully to allow most of the functionality to be tested. What remains
* here is overrides of trivial functionality.
*/
private static class FakeMediaResourceGetter extends MediaResourceGetter {
// Read these back in tests to ensure proper values passed through
String mUri = null;
Map<String, String> mHeaders = null;
String mPath = null;
String mContentUri = null;
int mFd;
long mOffset;
long mLength;
// Write these before tests to configure functionality
SparseArray<String> mMetadata = null;
Integer mNetworkType = null;
boolean mThrowExceptionInConfigure = false;
boolean mThrowExceptionInExtract = false;
boolean mFileExists = false;
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public void configure(int fd, long offset, long length) {
if (mThrowExceptionInConfigure) {
throw new RuntimeException("test exception");
}
mFd = fd;
mOffset = offset;
mLength = length;
}
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public void configure(String uri, Map<String, String> headers) {
if (mThrowExceptionInConfigure) {
throw new RuntimeException("test exception");
}
mUri = uri;
mHeaders = headers;
}
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public void configure(String path) {
if (mThrowExceptionInConfigure) {
throw new RuntimeException("test exception");
}
mPath = path;
}
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public void configure(Context context, Uri uri) {
if (mThrowExceptionInConfigure) {
throw new RuntimeException("test exception");
}
mContentUri = uri.toString();
}
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public String extractMetadata(int key) {
if (mThrowExceptionInExtract) {
throw new RuntimeException("test exception");
}
if (mMetadata == null) {
return null;
}
return mMetadata.get(key);
}
// Can't use a real NetworkInfo object because there is no way to
// mock the ConnectivityManager in Android.
@Override
Integer getNetworkType(Context context) {
return mNetworkType;
}
// Can't use Environment.getExternalStorageDirectory because it could
// be inconsistent depending upon the state of the real or emulated
// device upon which we are testing.
@Override
String getExternalStorageDirectory() {
return sExternalStorageDirectory;
}
// Can't use regular File because we need to control the return from
// File.exists() on arbitrary paths.
@Override
File uriToFile(String path) {
FakeFile result = new FakeFile(path, mFileExists);
return result;
}
/**
* Convenience method to bind a metadata value to a key.
* @param key the key
* @param value the value
*/
void bind(int key, String value) {
if (mMetadata == null) {
mMetadata = new SparseArray<String>();
}
mMetadata.put(key, value);
}
}
/**
* Helper class to control the result of permission checks.
*/
private static class InternalMockContext extends MockContext {
boolean mAllowPermission = false;
@Override
public int checkCallingOrSelfPermission(String permission) {
Assert.assertEquals(android.Manifest.permission.ACCESS_NETWORK_STATE, permission);
return mAllowPermission ? PackageManager.PERMISSION_GRANTED :
PackageManager.PERMISSION_DENIED;
}
@Override
public String getPackageName() {
return "org.some.app.package";
}
}
// Our test objects.
private FakeMediaResourceGetter mFakeMRG;
private InternalMockContext mMockContext;
@Before
public void setUp() throws Exception {
mFakeMRG = new FakeMediaResourceGetter();
mMockContext = new InternalMockContext();
}
@Test
@SmallTest
public void testMediaMetadataEquals() {
Assert.assertEquals(sEmptyMetadata, sEmptyMetadata);
Assert.assertEquals(sEmptyMetadata, new MediaMetadata(0, 0, 0, false));
Assert.assertFalse(sEmptyMetadata.equals(null));
Assert.assertFalse(sEmptyMetadata.equals("test"));
Assert.assertFalse(sEmptyMetadata.equals(new MediaMetadata(1, 0, 0, false)));
Assert.assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 1, 0, false)));
Assert.assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 1, false)));
Assert.assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 0, true)));
}
@Test
@SmallTest
public void testMediaMetadataHashCode() {
Assert.assertEquals(sEmptyMetadata.hashCode(), sEmptyMetadata.hashCode());
Assert.assertEquals(
sEmptyMetadata.hashCode(), new MediaMetadata(0, 0, 0, false).hashCode());
Assert.assertFalse(
sEmptyMetadata.hashCode() == new MediaMetadata(1, 0, 0, false).hashCode());
Assert.assertFalse(
sEmptyMetadata.hashCode() == new MediaMetadata(0, 1, 0, false).hashCode());
Assert.assertFalse(
sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 1, false).hashCode());
Assert.assertFalse(
sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 0, true).hashCode());
}
@Test
@SmallTest
public void testMediaMetadataGetters() {
MediaMetadata data = new MediaMetadata(1, 2, 3, true);
Assert.assertEquals(1, data.getDurationInMilliseconds());
Assert.assertEquals(2, data.getWidth());
Assert.assertEquals(3, data.getHeight());
Assert.assertTrue(data.isSuccess());
// Validate no overlap of test values with defaults
data = new MediaMetadata(4, 5, 6, false);
Assert.assertEquals(4, data.getDurationInMilliseconds());
Assert.assertEquals(5, data.getWidth());
Assert.assertEquals(6, data.getHeight());
Assert.assertFalse(data.isSuccess());
}
@Test
@SmallTest
public void testConfigure_Net_NoPermissions() {
mMockContext.mAllowPermission = false;
Assert.assertFalse(
mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT));
}
@Test
@SmallTest
public void testConfigure_Net_NoActiveNetwork() {
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = null;
Assert.assertFalse(
mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT));
}
@Test
@SmallTest
public void testConfigure_Net_Disallowed_Mobile() {
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_MOBILE;
Assert.assertFalse(
mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT));
}
@Test
@SmallTest
public void testConfigure_Net_Disallowed_Wimax() {
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIMAX;
Assert.assertFalse(
mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT));
}
@Test
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_Cookies_NoUA() {
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
Assert.assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, null));
Assert.assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
Assert.assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders);
Assert.assertNull(mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mContentUri);
}
@Test
@SmallTest
public void testConfigure_Net_Allowed_Wifi_Cookies_NoUA() {
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIFI;
Assert.assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, null));
Assert.assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
Assert.assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders);
Assert.assertNull(mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mContentUri);
}
@Test
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_NoCookies_NoUA() {
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
Assert.assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, "", null));
Assert.assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
Assert.assertEquals(Collections.emptyMap(), mFakeMRG.mHeaders);
Assert.assertNull(mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mContentUri);
}
@Test
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_Cookies_WithUA() {
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
Assert.assertTrue(
mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT));
Assert.assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
Assert.assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders);
Assert.assertNull(mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mContentUri);
}
@Test
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_NoCookies_WithUA() {
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
Assert.assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, "", TEST_USER_AGENT));
Assert.assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
Assert.assertEquals(sHeadersUAOnly, mFakeMRG.mHeaders);
Assert.assertNull(mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mContentUri);
}
@Test
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_Exception() {
mMockContext.mAllowPermission = true;
mFakeMRG.mThrowExceptionInConfigure = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
Assert.assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, "", TEST_USER_AGENT));
Assert.assertNull(mFakeMRG.mUri);
Assert.assertNull(mFakeMRG.mHeaders);
}
@Test
@SmallTest
public void testConfigure_Net_Allowed_LocalHost_WithNoNetwork() {
String[] localHostUrls = {
"http://LocalHost",
"http://LocalHost6",
"http://helloworld.localhost:8000",
"https://127.0.0.1/",
"http://[::1]:8888/",
};
mMockContext.mAllowPermission = true;
mFakeMRG.mNetworkType = null;
for (String localHostUrl : localHostUrls) {
Assert.assertTrue(
mFakeMRG.configure(mMockContext, localHostUrl, TEST_COOKIES, TEST_USER_AGENT));
Assert.assertEquals(localHostUrl, mFakeMRG.mUri);
Assert.assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders);
Assert.assertNull(mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mContentUri);
}
}
@Test
@SmallTest
public void testConfigure_File_Allowed_MntSdcard() {
final String path = "/mnt/sdcard/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
Assert.assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertEquals(path, mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mUri);
Assert.assertNull(mFakeMRG.mContentUri);
Assert.assertNull(mFakeMRG.mHeaders);
}
@Test
@SmallTest
public void testConfigure_File_Allowed_Sdcard() {
final String path = "/sdcard/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
Assert.assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertEquals(path, mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mUri);
Assert.assertNull(mFakeMRG.mContentUri);
Assert.assertNull(mFakeMRG.mHeaders);
}
@Test
@SmallTest
public void testConfigure_File_Allowed_Sdcard_DoesntExist() {
final String path = "/sdcard/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = false;
Assert.assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertNull(mFakeMRG.mPath);
}
@Test
@SmallTest
public void testConfigure_File_Allowed_ExternalStorage() {
final String path = sExternalStorageDirectory + "/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
Assert.assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertEquals(path, mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mUri);
Assert.assertNull(mFakeMRG.mContentUri);
Assert.assertNull(mFakeMRG.mHeaders);
}
@Test
@SmallTest
public void testConfigure_File_Disallowed_Innocent() {
final String path = "/malicious/path";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
Assert.assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertNull(mFakeMRG.mPath);
}
@Test
@SmallTest
public void testConfigure_File_Disallowed_Malicious() {
final String path = "/mnt/sdcard/../../data";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
Assert.assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertNull(mFakeMRG.mPath);
}
@Test
@SmallTest
public void testConfigure_File_Allowed_Exception() {
final String path = "/mnt/sdcard/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
mFakeMRG.mThrowExceptionInConfigure = true;
Assert.assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertNull(mFakeMRG.mPath);
}
@Test
@SmallTest
public void testConfigure_Blob_Disallow_Null_Cache() {
final String path = "/data/data/" + null + "/cache/";
final String url = path;
mFakeMRG.mFileExists = true;
mFakeMRG.mThrowExceptionInConfigure = true;
Assert.assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertNull(mFakeMRG.mPath);
}
@Test
@SmallTest
public void testConfigure_Blob_Disallowed_Incomplete_Path() {
final String path = "/data/data/";
final String url = path;
mFakeMRG.mFileExists = true;
mFakeMRG.mThrowExceptionInConfigure = true;
Assert.assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertNull(mFakeMRG.mPath);
}
@Test
@SmallTest
public void testConfigure_Blob_Disallowed_Unknown_Path() {
final String path = "/unknown/path/";
final String url = path;
mFakeMRG.mFileExists = true;
mFakeMRG.mThrowExceptionInConfigure = true;
Assert.assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertNull(mFakeMRG.mPath);
}
@Test
@SmallTest
public void testConfigure_Blob_Disallowed_Other_Application() {
final String path = "/data/data/org.some.other.app/cache/";
final String url = path;
mFakeMRG.mFileExists = true;
mFakeMRG.mThrowExceptionInConfigure = true;
Assert.assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
Assert.assertNull(mFakeMRG.mPath);
}
@Test
@SmallTest
public void testConfigure_Content_Uri_Allowed() {
Assert.assertTrue(mFakeMRG.configure(mMockContext, TEST_CONTENT_URI, "", null));
Assert.assertNull(mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mUri);
Assert.assertEquals(TEST_CONTENT_URI, mFakeMRG.mContentUri);
}
@Test
@SmallTest
public void testConfigure_Content_Uri_Disallowed() {
mFakeMRG.mThrowExceptionInConfigure = true;
Assert.assertFalse(mFakeMRG.configure(mMockContext, TEST_CONTENT_URI, "", null));
Assert.assertNull(mFakeMRG.mPath);
Assert.assertNull(mFakeMRG.mUri);
Assert.assertNull(mFakeMRG.mContentUri);
}
@Test
@SmallTest
public void testExtract_NoMetadata() {
mFakeMRG.mFileExists = true;
Assert.assertEquals(
sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
Assert.assertEquals("configured successfully when we shouldn't have", TEST_FILE_PATH,
mFakeMRG.mPath); // tricky
}
@Test
@SmallTest
public void testExtract_WithMetadata_ValidDuration() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
final MediaMetadata expected = new MediaMetadata(1, 0, 0, true);
Assert.assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@Test
@SmallTest
public void testExtract_WithMetadata_InvalidDuration() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "i am not an integer");
Assert.assertEquals(
sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@Test
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_NoWidth_NoHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
Assert.assertEquals(
sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@Test
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_NoHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1");
Assert.assertEquals(
sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@Test
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_InvalidWidth_NoHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "i am not an integer");
Assert.assertEquals(
sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@Test
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_ValidHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3");
final MediaMetadata expected = new MediaMetadata(1, 2, 3, true);
Assert.assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@Test
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_InvalidHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "i am not an integer");
Assert.assertEquals(
sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@Test
@SmallTest
public void testExtract_WithMetadata_ValidDuration_ExceptionInExtract() {
mFakeMRG.mFileExists = true;
mFakeMRG.mThrowExceptionInExtract = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
Assert.assertEquals(
sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@Test
@SmallTest
public void testExtractFromFileDescriptor_ValidMetadata() {
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3");
final MediaMetadata expected = new MediaMetadata(1, 2, 3, true);
int fd = 1234;
long offset = 1000;
long length = 9000;
Assert.assertEquals(expected, mFakeMRG.extract(fd, offset, length));
Assert.assertEquals(fd, mFakeMRG.mFd);
Assert.assertEquals(offset, mFakeMRG.mOffset);
Assert.assertEquals(length, mFakeMRG.mLength);
}
}
...@@ -90,30 +90,15 @@ void MediaPlayerBridge::Initialize() { ...@@ -90,30 +90,15 @@ void MediaPlayerBridge::Initialize() {
return; return;
} }
if (url_.SchemeIsFile() || url_.SchemeIs("data")) { if (allow_credentials_) {
ExtractMediaMetadata(url_.spec());
return;
}
media::MediaResourceGetter* resource_getter = media::MediaResourceGetter* resource_getter =
manager()->GetMediaResourceGetter(); manager()->GetMediaResourceGetter();
if (url_.SchemeIsFileSystem()) {
resource_getter->GetPlatformPathFromURL(
url_, base::BindOnce(&MediaPlayerBridge::ExtractMediaMetadata,
weak_factory_.GetWeakPtr()));
return;
}
// Start extracting the metadata immediately if the request is anonymous. resource_getter->GetCookies(
// Otherwise, wait for user credentials to be retrieved first. url_, site_for_cookies_,
if (!allow_credentials_) { base::BindOnce(&MediaPlayerBridge::OnCookiesRetrieved,
ExtractMediaMetadata(url_.spec());
return;
}
resource_getter->GetCookies(url_, site_for_cookies_,
base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
}
} }
void MediaPlayerBridge::CreateJavaMediaPlayerBridge() { void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
...@@ -263,42 +248,6 @@ void MediaPlayerBridge::OnAuthCredentialsRetrieved( ...@@ -263,42 +248,6 @@ void MediaPlayerBridge::OnAuthCredentialsRetrieved(
replacements.SetPasswordStr(password); replacements.SetPasswordStr(password);
url_ = url_.ReplaceComponents(replacements); url_ = url_.ReplaceComponents(replacements);
} }
ExtractMediaMetadata(url_.spec());
}
void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
if (url.empty()) {
OnMediaError(MEDIA_ERROR_FORMAT);
on_decoder_resources_released_cb_.Run(player_id());
return;
}
int fd;
int64_t offset;
int64_t size;
if (InterceptMediaUrl(url, &fd, &offset, &size)) {
manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
fd, offset, size,
base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
weak_factory_.GetWeakPtr()));
} else {
manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
url, cookies_, user_agent_,
base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
weak_factory_.GetWeakPtr()));
}
}
void MediaPlayerBridge::OnMediaMetadataExtracted(
base::TimeDelta duration, int width, int height, bool success) {
if (success) {
duration_ = duration;
width_ = width;
height_ = height;
}
manager()->OnMediaMetadataChanged(
player_id(), duration_, width_, height_, success);
on_decoder_resources_released_cb_.Run(player_id());
} }
void MediaPlayerBridge::Start() { void MediaPlayerBridge::Start() {
......
...@@ -49,20 +49,6 @@ class MEDIA_EXPORT MediaResourceGetter { ...@@ -49,20 +49,6 @@ class MEDIA_EXPORT MediaResourceGetter {
// Method for getting the platform path from a file system URL. // Method for getting the platform path from a file system URL.
virtual void GetPlatformPathFromURL(const GURL& url, virtual void GetPlatformPathFromURL(const GURL& url,
GetPlatformPathCB callback) = 0; GetPlatformPathCB callback) = 0;
// Extracts the metadata from a media URL. Once completed, the provided
// callback function will be run.
virtual void ExtractMediaMetadata(const std::string& url,
const std::string& cookies,
const std::string& user_agent,
ExtractMediaMetadataCB callback) = 0;
// Extracts the metadata from a file descriptor. Once completed, the
// provided callback function will be run.
virtual void ExtractMediaMetadata(const int fd,
const int64_t offset,
const int64_t size,
ExtractMediaMetadataCB callback) = 0;
}; };
} // namespace media } // namespace media
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment