diff options
author | apiccion@chromium.org <apiccion@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-04 06:05:21 +0000 |
---|---|---|
committer | apiccion@chromium.org <apiccion@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-04 06:05:21 +0000 |
commit | c6584ac8d2e57fa5193c3203e09c13fcf270474c (patch) | |
tree | f45dd69825bab98f01ec31d1796f9fe81370ffca | |
parent | e8072568409f3d3613d10342bb6273b71fa403f2 (diff) | |
download | chromium_src-c6584ac8d2e57fa5193c3203e09c13fcf270474c.zip chromium_src-c6584ac8d2e57fa5193c3203e09c13fcf270474c.tar.gz chromium_src-c6584ac8d2e57fa5193c3203e09c13fcf270474c.tar.bz2 |
Android can play data uri media items.
BUG=324713
Review URL: https://codereview.chromium.org/98563002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@238595 0039d316-1c4b-4281-b951-d872f2087c98
4 files changed, 143 insertions, 13 deletions
diff --git a/content/renderer/media/android/media_info_loader.cc b/content/renderer/media/android/media_info_loader.cc index ea93304..875265c 100644 --- a/content/renderer/media/android/media_info_loader.cc +++ b/content/renderer/media/android/media_info_loader.cc @@ -111,7 +111,11 @@ void MediaInfoLoader::didReceiveResponse( "Unknown") << " " << response.httpStatusCode(); DCHECK(active_loader_.get()); - if (response.httpStatusCode() == kHttpOK || url_.SchemeIsFile()) { + if (!url_.SchemeIs("http") && !url_.SchemeIs("https")) { + DidBecomeReady(kOk); + return; + } + if (response.httpStatusCode() == kHttpOK) { DidBecomeReady(kOk); return; } diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java index fe9407d..1a8f61a 100644 --- a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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. @@ -7,22 +7,26 @@ package org.chromium.media; import android.content.Context; import android.media.MediaPlayer; import android.net.Uri; +import android.os.AsyncTask; import android.text.TextUtils; +import android.util.Base64; import android.util.Log; import android.view.Surface; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; -import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; -import java.util.Map; -// A wrapper around android.media.MediaPlayer that allows the native code to use it. -// See media/base/android/media_player_bridge.cc for the corresponding native code. +/** +* A wrapper around android.media.MediaPlayer that allows the native code to use it. +* See media/base/android/media_player_bridge.cc for the corresponding native code. +*/ @JNINamespace("media") public class MediaPlayerBridge { @@ -30,11 +34,29 @@ public class MediaPlayerBridge { // Local player to forward this to. We don't initialize it here since the subclass might not // want it. + private LoadDataUriTask mLoadDataUriTask; private MediaPlayer mPlayer; + private long mNativeMediaPlayerBridge; @CalledByNative - private static MediaPlayerBridge create() { - return new MediaPlayerBridge(); + private static MediaPlayerBridge create(long nativeMediaPlayerBridge) { + return new MediaPlayerBridge(nativeMediaPlayerBridge); + } + + protected MediaPlayerBridge(long nativeMediaPlayerBridge) { + mNativeMediaPlayerBridge = nativeMediaPlayerBridge; + } + + protected MediaPlayerBridge() { + } + + @CalledByNative + protected void destroy() { + if (mLoadDataUriTask != null) { + mLoadDataUriTask.cancel(true); + mLoadDataUriTask = null; + } + mNativeMediaPlayerBridge = 0; } protected MediaPlayer getLocalPlayer() { @@ -115,10 +137,8 @@ public class MediaPlayerBridge { Context context, String url, String cookies, boolean hideUrlLog) { Uri uri = Uri.parse(url); HashMap<String, String> headersMap = new HashMap<String, String>(); - if (hideUrlLog) - headersMap.put("x-hide-urls-from-log", "true"); - if (!TextUtils.isEmpty(cookies)) - headersMap.put("Cookie", cookies); + if (hideUrlLog) headersMap.put("x-hide-urls-from-log", "true"); + if (!TextUtils.isEmpty(cookies)) headersMap.put("Cookie", cookies); try { getLocalPlayer().setDataSource(context, uri, headersMap); return true; @@ -127,6 +147,80 @@ public class MediaPlayerBridge { } } + @CalledByNative + protected boolean setDataUriDataSource(final Context context, final String url) { + if (mLoadDataUriTask != null) { + mLoadDataUriTask.cancel(true); + mLoadDataUriTask = null; + } + + if (!url.startsWith("data:")) return false; + int headerStop = url.indexOf(','); + if (headerStop == -1) return false; + String header = url.substring(0, headerStop); + final String data = url.substring(headerStop + 1); + + String headerContent = header.substring(5); + String headerInfo[] = headerContent.split(";"); + if (headerInfo.length != 2) return false; + if (!"base64".equals(headerInfo[1])) return false; + + mLoadDataUriTask = new LoadDataUriTask(context, data); + mLoadDataUriTask.execute(); + return true; + } + + private class LoadDataUriTask extends AsyncTask <Void, Void, Boolean> { + private final String mData; + private final Context mContext; + private File mTempFile; + + public LoadDataUriTask(Context context, String data) { + mData = data; + mContext = context; + } + + @Override + protected Boolean doInBackground(Void... params) { + try { + mTempFile = File.createTempFile("decoded", "mediadata"); + FileOutputStream fos = new FileOutputStream(mTempFile); + fos.write(Base64.decode(mData, Base64.DEFAULT)); + fos.close(); + return true; + } catch (IOException e) { + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + if (isCancelled()) { + deleteFile(); + return; + } + + try { + getLocalPlayer().setDataSource(mContext, Uri.fromFile(mTempFile)); + } catch (IOException e) { + result = false; + } + + deleteFile(); + assert (mNativeMediaPlayerBridge != 0); + nativeOnDidSetDataUriDataSource(mNativeMediaPlayerBridge, result); + } + + private void deleteFile() { + if (mTempFile == null) return; + if (!mTempFile.delete()) { + // File will be deleted when MediaPlayer releases its handler. + Log.e(TAG, "Failed to delete temporary file: " + mTempFile); + assert (false); + } + } + } + protected void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) { getLocalPlayer().setOnBufferingUpdateListener(listener); } @@ -218,4 +312,7 @@ public class MediaPlayerBridge { } return new AllowedOperations(canPause, canSeekForward, canSeekBackward); } + + private native void nativeOnDidSetDataUriDataSource(long nativeMediaPlayerBridge, + boolean success); } diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc index 64e2db8..0f79c13 100644 --- a/media/base/android/media_player_bridge.cc +++ b/media/base/android/media_player_bridge.cc @@ -9,6 +9,7 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/message_loop/message_loop_proxy.h" +#include "base/strings/string_util.h" #include "jni/MediaPlayerBridge_jni.h" #include "media/base/android/media_player_manager.h" #include "media/base/android/media_resource_getter.h" @@ -45,6 +46,11 @@ MediaPlayerBridge::MediaPlayerBridge( } MediaPlayerBridge::~MediaPlayerBridge() { + if (!j_media_player_bridge_.is_null()) { + JNIEnv* env = base::android::AttachCurrentThread(); + CHECK(env); + Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj()); + } Release(); } @@ -72,7 +78,8 @@ void MediaPlayerBridge::CreateJavaMediaPlayerBridge() { JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); - j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(env)); + j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create( + env, reinterpret_cast<intptr_t>(this))); SetMediaPlayerListener(); } @@ -144,6 +151,15 @@ void MediaPlayerBridge::SetDataSource(const std::string& url) { jobject j_context = base::android::GetApplicationContext(); DCHECK(j_context); + const std::string data_uri_prefix("data:"); + if (StartsWithASCII(url, data_uri_prefix, true)) { + if (!Java_MediaPlayerBridge_setDataUriDataSource( + env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) { + OnMediaError(MEDIA_ERROR_FORMAT); + } + return; + } + if (!Java_MediaPlayerBridge_setDataSource( env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(), j_cookies.obj(), hide_url_log_)) { @@ -156,6 +172,18 @@ void MediaPlayerBridge::SetDataSource(const std::string& url) { OnMediaError(MEDIA_ERROR_FORMAT); } +void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj, + jboolean success) { + if (!success) { + OnMediaError(MEDIA_ERROR_FORMAT); + return; + } + + manager()->RequestMediaResources(player_id()); + if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj())) + OnMediaError(MEDIA_ERROR_FORMAT); +} + void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) { cookies_ = cookies; ExtractMediaMetadata(url_.spec()); diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h index f47a50d..402cb49 100644 --- a/media/base/android/media_player_bridge.h +++ b/media/base/android/media_player_bridge.h @@ -78,6 +78,7 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid { void OnPlaybackComplete(); void OnMediaInterrupted(); void OnSeekComplete(); + void OnDidSetDataUriDataSource(JNIEnv* env, jobject obj, jboolean success); protected: void SetJavaMediaPlayerBridge(jobject j_media_player_bridge); |