diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-05-09 18:35:53 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-05-13 13:57:14 +0100 |
commit | c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d (patch) | |
tree | 1dbdbb0624cc869ab25ee7f46971984c6fee3e7a /media/base/android | |
parent | 2d519ce2457219605d4f472da8d2ffd469796035 (diff) | |
download | chromium_org-c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d.tar.gz |
Merge from Chromium at DEPS revision r198571
This commit was generated by merge_to_master.py.
Change-Id: I951118a03836157090561764dd2627f0add8118f
Diffstat (limited to 'media/base/android')
18 files changed, 834 insertions, 148 deletions
diff --git a/media/base/android/OWNERS b/media/base/android/OWNERS index 25a5e6b4e2..e604870cbb 100644 --- a/media/base/android/OWNERS +++ b/media/base/android/OWNERS @@ -1,3 +1,3 @@ bulach@chromium.org jcivelli@chromium.org -yfriedman@chromium.org +qinmin@chromium.org diff --git a/media/base/android/demuxer_stream_player_params.cc b/media/base/android/demuxer_stream_player_params.cc new file mode 100644 index 0000000000..ab9fcafaf5 --- /dev/null +++ b/media/base/android/demuxer_stream_player_params.cc @@ -0,0 +1,32 @@ +// Copyright (c) 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. + +#include "media/base/android/demuxer_stream_player_params.h" + +namespace media { + +MediaPlayerHostMsg_DemuxerReady_Params:: + MediaPlayerHostMsg_DemuxerReady_Params() + : audio_channels(0), + audio_sampling_rate(0), + is_audio_encrypted(false), + is_video_encrypted(false), + duration_ms(0) {} + +MediaPlayerHostMsg_DemuxerReady_Params:: + ~MediaPlayerHostMsg_DemuxerReady_Params() {} + +MediaPlayerHostMsg_ReadFromDemuxerAck_Params:: + MediaPlayerHostMsg_ReadFromDemuxerAck_Params() + : type(DemuxerStream::UNKNOWN) {} + +MediaPlayerHostMsg_ReadFromDemuxerAck_Params:: + ~MediaPlayerHostMsg_ReadFromDemuxerAck_Params() {} + +MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit::AccessUnit() + : end_of_stream(false) {} + +MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit::~AccessUnit() {} + +} // namespace media diff --git a/media/base/android/demuxer_stream_player_params.h b/media/base/android/demuxer_stream_player_params.h new file mode 100644 index 0000000000..face665b57 --- /dev/null +++ b/media/base/android/demuxer_stream_player_params.h @@ -0,0 +1,61 @@ +// Copyright (c) 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. + +#ifndef MEDIA_BASE_ANDROID_DEMUXER_STREAM_PLAYER_PARAMS_H_ +#define MEDIA_BASE_ANDROID_DEMUXER_STREAM_PLAYER_PARAMS_H_ + +#include <string> +#include <vector> + +#include "media/base/audio_decoder_config.h" +#include "media/base/decrypt_config.h" +#include "media/base/demuxer_stream.h" +#include "media/base/media_export.h" +#include "media/base/video_decoder_config.h" +#include "ui/gfx/size.h" + +namespace media { + +struct MEDIA_EXPORT MediaPlayerHostMsg_DemuxerReady_Params { + MediaPlayerHostMsg_DemuxerReady_Params(); + ~MediaPlayerHostMsg_DemuxerReady_Params(); + + AudioCodec audio_codec; + int audio_channels; + int audio_sampling_rate; + bool is_audio_encrypted; + + VideoCodec video_codec; + gfx::Size video_size; + bool is_video_encrypted; + + int duration_ms; + std::string key_system; +}; + +struct MEDIA_EXPORT MediaPlayerHostMsg_ReadFromDemuxerAck_Params { + struct MEDIA_EXPORT AccessUnit { + AccessUnit(); + ~AccessUnit(); + + DemuxerStream::Status status; + bool end_of_stream; + // TODO(ycheo): Use the shared memory to transfer the block data. + std::vector<unsigned char> data; + base::TimeDelta timestamp; + std::vector<char> key_id; + std::vector<char> iv; + std::vector<media::SubsampleEntry> subsamples; + }; + + MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); + ~MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); + + DemuxerStream::Type type; + std::vector<AccessUnit> access_units; +}; + +}; // namespace media + +#endif // MEDIA_BASE_ANDROID_DEMUXER_STREAM_PLAYER_PARAMS_H_ diff --git a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java index ee6f46ea9e..9066380d62 100644 --- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java +++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java @@ -9,6 +9,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; +import android.os.Build; +import android.util.Log; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; @@ -25,7 +27,12 @@ class AudioManagerAndroid { @CalledByNative public void setMode(int mode) { - mAudioManager.setMode(mode); + try { + mAudioManager.setMode(mode); + } catch (SecurityException e) { + Log.e(TAG, "setMode exception: " + e.getMessage()); + logDeviceInfo(); + } } @CalledByNative @@ -51,8 +58,13 @@ class AudioManagerAndroid { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) { - mAudioManager.setSpeakerphoneOn( - intent.getIntExtra("state", 0) == 0); + try { + mAudioManager.setSpeakerphoneOn( + intent.getIntExtra("state", 0) == 0); + } catch (SecurityException e) { + Log.e(TAG, "setMode exception: " + e.getMessage()); + logDeviceInfo(); + } } } }; @@ -65,4 +77,10 @@ class AudioManagerAndroid { mReceiver = null; mAudioManager.setSpeakerphoneOn(mOriginalSpeakerStatus); } + + private void logDeviceInfo() { + Log.i(TAG, "Manufacturer:" + Build.MANUFACTURER + + " Board: " + Build.BOARD + " Device: " + Build.DEVICE + + " Model: " + Build.MODEL + " PRODUCT: " + Build.PRODUCT); + } } diff --git a/media/base/android/java/src/org/chromium/media/VideoCapture.java b/media/base/android/java/src/org/chromium/media/VideoCapture.java index 0266558ca7..150f3b6946 100644 --- a/media/base/android/java/src/org/chromium/media/VideoCapture.java +++ b/media/base/android/java/src/org/chromium/media/VideoCapture.java @@ -251,8 +251,8 @@ public class VideoCapture implements PreviewCallback, OnFrameAvailableListener { stopCapture(); try { mCamera.setPreviewTexture(null); - mSurfaceTexture.setOnFrameAvailableListener(null); - GLES20.glDeleteTextures(1, mGlTextures, 0); + if (mGlTextures != null) + GLES20.glDeleteTextures(1, mGlTextures, 0); mCurrentCapability = null; mCamera.release(); mCamera = null; diff --git a/media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java new file mode 100644 index 0000000000..25596933b1 --- /dev/null +++ b/media/base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java @@ -0,0 +1,165 @@ +// Copyright (c) 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.media; + +import android.content.Context; +import android.media.AudioFormat; +import android.media.MediaCodec; +import android.media.MediaCodec.BufferInfo; +import android.media.MediaExtractor; +import android.media.MediaFormat; +import android.util.Log; + +import java.nio.ByteBuffer; +import android.os.ParcelFileDescriptor; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +@JNINamespace("media") +class WebAudioMediaCodecBridge { + private static final boolean DEBUG = true; + static final String LOG_TAG = "WebAudioMediaCodec"; + // TODO(rtoy): What is the correct timeout value for reading + // from a file in memory? + static final long TIMEOUT_MICROSECONDS = 500; + @CalledByNative + private static boolean decodeAudioFile(Context ctx, + int nativeMediaCodecBridge, + int inputFD, + long dataSize) { + + if (dataSize < 0 || dataSize > 0x7fffffff) + return false; + + MediaExtractor extractor = new MediaExtractor(); + + ParcelFileDescriptor encodedFD; + encodedFD = ParcelFileDescriptor.adoptFd(inputFD); + try { + extractor.setDataSource(encodedFD.getFileDescriptor(), 0, dataSize); + } catch (Exception e) { + e.printStackTrace(); + encodedFD.detachFd(); + return false; + } + + if (extractor.getTrackCount() <= 0) { + encodedFD.detachFd(); + return false; + } + + MediaFormat format = extractor.getTrackFormat(0); + + int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); + int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); + String mime = format.getString(MediaFormat.KEY_MIME); + + long durationMicroseconds = 0; + if (format.containsKey(MediaFormat.KEY_DURATION)) { + try { + durationMicroseconds = format.getLong(MediaFormat.KEY_DURATION); + } catch (Exception e) { + Log.d(LOG_TAG, "Cannot get duration"); + } + } + + if (DEBUG) { + Log.d(LOG_TAG, "Tracks: " + extractor.getTrackCount() + + " Rate: " + sampleRate + + " Channels: " + channelCount + + " Mime: " + mime + + " Duration: " + durationMicroseconds + " microsec"); + } + + nativeInitializeDestination(nativeMediaCodecBridge, + channelCount, + sampleRate, + durationMicroseconds); + + // Create decoder + MediaCodec codec = MediaCodec.createDecoderByType(mime); + codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); + codec.start(); + + ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); + ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers(); + + // A track must be selected and will be used to read samples. + extractor.selectTrack(0); + + boolean sawInputEOS = false; + boolean sawOutputEOS = false; + + // Keep processing until the output is done. + while (!sawOutputEOS) { + if (!sawInputEOS) { + // Input side + int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_MICROSECONDS); + + if (inputBufIndex >= 0) { + ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; + int sampleSize = extractor.readSampleData(dstBuf, 0); + long presentationTimeMicroSec = 0; + + if (sampleSize < 0) { + sawInputEOS = true; + sampleSize = 0; + } else { + presentationTimeMicroSec = extractor.getSampleTime(); + } + + codec.queueInputBuffer(inputBufIndex, + 0, /* offset */ + sampleSize, + presentationTimeMicroSec, + sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); + + if (!sawInputEOS) { + extractor.advance(); + } + } + } + + // Output side + MediaCodec.BufferInfo info = new BufferInfo(); + final int outputBufIndex = codec.dequeueOutputBuffer(info, TIMEOUT_MICROSECONDS); + + if (outputBufIndex >= 0) { + ByteBuffer buf = codecOutputBuffers[outputBufIndex]; + + if (info.size > 0) { + nativeOnChunkDecoded(nativeMediaCodecBridge, buf, info.size); + } + + buf.clear(); + codec.releaseOutputBuffer(outputBufIndex, false /* render */); + + if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { + sawOutputEOS = true; + } + } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { + codecOutputBuffers = codec.getOutputBuffers(); + } + } + + encodedFD.detachFd(); + + codec.stop(); + codec.release(); + codec = null; + + return true; + } + + private static native void nativeOnChunkDecoded( + int nativeWebAudioMediaCodecBridge, ByteBuffer buf, int size); + + private static native void nativeInitializeDestination( + int nativeWebAudioMediaCodecBridge, + int channelCount, + int sampleRate, + long durationMicroseconds); +} diff --git a/media/base/android/media_codec_bridge.cc b/media/base/android/media_codec_bridge.cc index d66430da8a..e466654c41 100644 --- a/media/base/android/media_codec_bridge.cc +++ b/media/base/android/media_codec_bridge.cc @@ -32,7 +32,7 @@ static const char kMediaCodecBufferInfoClassPath[] = static bool MediaCodecBufferInfo_RegisterNativesImpl(JNIEnv* env) { g_MediaCodecBufferInfo_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetUnscopedClass(env, kMediaCodecBufferInfoClassPath))); + base::android::GetClass(env, kMediaCodecBufferInfoClassPath).obj())); base::android::CheckException(env); return true; } @@ -97,12 +97,27 @@ namespace media { enum { kBufferFlagEndOfStream = 4 }; -static const char* CodecToMimeType(const MediaCodecBridge::Codec codec) { +static const char* AudioCodecToMimeType(const AudioCodec codec) { switch (codec) { - case MediaCodecBridge::AUDIO_MPEG: return "audio/mpeg"; - case MediaCodecBridge::VIDEO_H264: return "video/avc"; - case MediaCodecBridge::VIDEO_VP8: return "video/x-vnd.on2.vp8"; - default: return NULL; + case kCodecMP3: + return "audio/mpeg"; + case kCodecVorbis: + return "audio/vorbis"; + case kCodecAAC: + return "audio/mp4a-latm"; + default: + return NULL; + } +} + +static const char* VideoCodecToMimeType(const VideoCodec codec) { + switch (codec) { + case kCodecH264: + return "video/avc"; + case kCodecVP8: + return "video/x-vnd.on2.vp8"; + default: + return NULL; } } @@ -120,14 +135,14 @@ bool MediaCodecBridge::IsAvailable() { return base::android::BuildInfo::GetInstance()->sdk_int() >= 16; } -MediaCodecBridge::MediaCodecBridge(const Codec codec) { +MediaCodecBridge::MediaCodecBridge(const char* mime) { JNIEnv* env = AttachCurrentThread(); CHECK(env); CHECK(g_native_registerer.Pointer()->IsRegistered()); - DCHECK(CodecToMimeType(codec)); + DCHECK(mime); ScopedJavaLocalRef<jstring> j_type = - ConvertUTF8ToJavaString(env, CodecToMimeType(codec)); + ConvertUTF8ToJavaString(env, mime); ScopedJavaLocalRef<jobject> tmp( JNI_MediaCodec::Java_MediaCodec_createDecoderByType(env, j_type.obj())); @@ -142,43 +157,7 @@ MediaCodecBridge::~MediaCodecBridge() { JNI_MediaCodec::Java_MediaCodec_release(env, j_media_codec_.obj()); } -void MediaCodecBridge::StartAudio( - const Codec codec, int sample_rate, int channel_count) { - JNIEnv* env = AttachCurrentThread(); - DCHECK(CodecToMimeType(codec)); - - ScopedJavaLocalRef<jstring> j_mime = - ConvertUTF8ToJavaString(env, CodecToMimeType(codec)); - ScopedJavaLocalRef<jobject> j_format( - JNI_MediaFormat::Java_MediaFormat_createAudioFormat( - env, j_mime.obj(), sample_rate, channel_count)); - DCHECK(!j_format.is_null()); - - JNI_MediaCodec::Java_MediaCodec_configure( - env, j_media_codec_.obj(), j_format.obj(), NULL, NULL, 0); - - Start(); -} - -void MediaCodecBridge::StartVideo( - const Codec codec, const gfx::Size& size, jobject surface) { - JNIEnv* env = AttachCurrentThread(); - DCHECK(CodecToMimeType(codec)); - - ScopedJavaLocalRef<jstring> j_mime = - ConvertUTF8ToJavaString(env, CodecToMimeType(codec)); - ScopedJavaLocalRef<jobject> j_format( - JNI_MediaFormat::Java_MediaFormat_createVideoFormat( - env, j_mime.obj(), size.width(), size.height())); - DCHECK(!j_format.is_null()); - - JNI_MediaCodec::Java_MediaCodec_configure( - env, j_media_codec_.obj(), j_format.obj(), surface, NULL, 0); - - Start(); -} - -void MediaCodecBridge::Start() { +void MediaCodecBridge::StartInternal() { JNIEnv* env = AttachCurrentThread(); JNI_MediaCodec::Java_MediaCodec_start(env, j_media_codec_.obj()); GetInputBuffers(); @@ -298,5 +277,45 @@ int MediaCodecBridge::GetOutputBuffers() { return env->GetArrayLength(j_output_buffers_.obj()); } +AudioCodecBridge::AudioCodecBridge(const AudioCodec codec) + : MediaCodecBridge(AudioCodecToMimeType(codec)) { +} + +void AudioCodecBridge::Start( + const AudioCodec codec, int sample_rate, int channel_count) { + JNIEnv* env = AttachCurrentThread(); + DCHECK(AudioCodecToMimeType(codec)); + + ScopedJavaLocalRef<jstring> j_mime = + ConvertUTF8ToJavaString(env, AudioCodecToMimeType(codec)); + ScopedJavaLocalRef<jobject> j_format( + JNI_MediaFormat::Java_MediaFormat_createAudioFormat( + env, j_mime.obj(), sample_rate, channel_count)); + DCHECK(!j_format.is_null()); + JNI_MediaCodec::Java_MediaCodec_configure( + env, media_codec(), j_format.obj(), NULL, NULL, 0); + StartInternal(); +} + +VideoCodecBridge::VideoCodecBridge(const VideoCodec codec) + : MediaCodecBridge(VideoCodecToMimeType(codec)) { +} + +void VideoCodecBridge::Start( + const VideoCodec codec, const gfx::Size& size, jobject surface) { + JNIEnv* env = AttachCurrentThread(); + DCHECK(VideoCodecToMimeType(codec)); + + ScopedJavaLocalRef<jstring> j_mime = + ConvertUTF8ToJavaString(env, VideoCodecToMimeType(codec)); + ScopedJavaLocalRef<jobject> j_format( + JNI_MediaFormat::Java_MediaFormat_createVideoFormat( + env, j_mime.obj(), size.width(), size.height())); + DCHECK(!j_format.is_null()); + JNI_MediaCodec::Java_MediaCodec_configure( + env, media_codec(), j_format.obj(), surface, NULL, 0); + StartInternal(); +} + } // namespace media diff --git a/media/base/android/media_codec_bridge.h b/media/base/android/media_codec_bridge.h index 402b78796d..c291f827c6 100644 --- a/media/base/android/media_codec_bridge.h +++ b/media/base/android/media_codec_bridge.h @@ -10,6 +10,8 @@ #include "base/android/scoped_java_ref.h" #include "base/time.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/video_decoder_config.h" #include "ui/gfx/size.h" namespace media { @@ -18,14 +20,10 @@ namespace media { // Android MediaCodec class. For more information on Android MediaCodec, check // http://developer.android.com/reference/android/media/MediaCodec.html // Note: MediaCodec is only available on JB and greater. +// Use AudioCodecBridge or VideoCodecBridge to create an instance of this +// object. class MediaCodecBridge { public: - enum Codec { - AUDIO_MPEG, - VIDEO_H264, - VIDEO_VP8, - }; - enum DequeueBufferInfo { INFO_OUTPUT_BUFFERS_CHANGED = -3, INFO_OUTPUT_FORMAT_CHANGED = -2, @@ -38,15 +36,7 @@ class MediaCodecBridge { // Returns true if MediaCodec is available on the device. static bool IsAvailable(); - explicit MediaCodecBridge(const Codec codec); - - ~MediaCodecBridge(); - - // Starts decoding with the given codec params. - void StartAudio( - const Codec codec, int sample_rate, int channel_count); - void StartVideo( - const Codec codec, const gfx::Size& size, jobject surface); + virtual ~MediaCodecBridge(); // Resets both input and output, all indices previously returned in calls to // DequeueInputBuffer() and DequeueOutputBuffer() become invalid. @@ -95,12 +85,16 @@ class MediaCodecBridge { // To access them, use DequeueOutputBuffer() and GetFromOutputBuffer(). int GetOutputBuffers(); - private: + protected: + explicit MediaCodecBridge(const char* mime); // Calls start() against the media codec instance. Used in StartXXX() after // configuring media codec. - void Start(); + void StartInternal(); + + jobject media_codec() { return j_media_codec_.obj(); } + private: // Gets input buffers from media codec and keeps them inside this class. // To access them, use DequeueInputBuffer(), PutToInputBuffer() and // QueueInputBuffer(). @@ -118,6 +112,24 @@ class MediaCodecBridge { DISALLOW_COPY_AND_ASSIGN(MediaCodecBridge); }; +class AudioCodecBridge : public MediaCodecBridge { + public: + explicit AudioCodecBridge(const AudioCodec codec); + + // Start the audio codec bridge. + void Start( + const AudioCodec codec, int sample_rate, int channel_count); +}; + +class VideoCodecBridge : public MediaCodecBridge { + public: + explicit VideoCodecBridge(const VideoCodec codec); + + // Start the video codec bridge. + void Start( + const VideoCodec codec, const gfx::Size& size, jobject surface); +}; + } // namespace media #endif // MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_ diff --git a/media/base/android/media_codec_bridge_unittest.cc b/media/base/android/media_codec_bridge_unittest.cc index 998aa2bf6d..d77041cf7a 100644 --- a/media/base/android/media_codec_bridge_unittest.cc +++ b/media/base/android/media_codec_bridge_unittest.cc @@ -97,17 +97,17 @@ TEST(MediaCodecBridgeTest, Initialize) { return; scoped_ptr<media::MediaCodecBridge> media_codec; - media_codec.reset(new MediaCodecBridge(MediaCodecBridge::VIDEO_H264)); + media_codec.reset(new VideoCodecBridge(kCodecH264)); } TEST(MediaCodecBridgeTest, DoNormal) { if (!MediaCodecBridge::IsAvailable()) return; - scoped_ptr<media::MediaCodecBridge> media_codec; - media_codec.reset(new MediaCodecBridge(MediaCodecBridge::AUDIO_MPEG)); + scoped_ptr<media::AudioCodecBridge> media_codec; + media_codec.reset(new AudioCodecBridge(kCodecMP3)); - media_codec->StartAudio(MediaCodecBridge::AUDIO_MPEG, 44100, 2); + media_codec->Start(kCodecMP3, 44100, 2); ASSERT_GT(media_codec->GetOutputBuffers(), 0); diff --git a/media/base/android/media_jni_registrar.cc b/media/base/android/media_jni_registrar.cc index 3a4ac82d11..9cedfac7e0 100644 --- a/media/base/android/media_jni_registrar.cc +++ b/media/base/android/media_jni_registrar.cc @@ -8,22 +8,25 @@ #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" -#include "media/audio/audio_manager_base.h" +#include "media/audio/android/audio_manager_android.h" #include "media/base/android/media_player_bridge.h" #include "media/base/android/media_player_listener.h" +#include "media/base/android/webaudio_media_codec_bridge.h" #include "media/video/capture/android/video_capture_device_android.h" namespace media { static base::android::RegistrationMethod kMediaRegisteredMethods[] = { - { "AudioManagerBase", - AudioManagerBase::RegisterAudioManager }, + { "AudioManagerAndroid", + AudioManagerAndroid::RegisterAudioManager }, { "MediaPlayerBridge", MediaPlayerBridge::RegisterMediaPlayerBridge }, { "MediaPlayerListener", MediaPlayerListener::RegisterMediaPlayerListener }, { "VideoCaptureDevice", VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice }, + { "WebAudioMediaCodecBridge", + WebAudioMediaCodecBridge::RegisterWebAudioMediaCodecBridge }, }; bool RegisterJni(JNIEnv* env) { diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc index bbf497b5c7..f08d3a758a 100644 --- a/media/base/android/media_player_bridge.cc +++ b/media/base/android/media_player_bridge.cc @@ -8,11 +8,10 @@ #include "base/android/jni_string.h" #include "base/basictypes.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "base/message_loop_proxy.h" #include "jni/MediaPlayerBridge_jni.h" #include "jni/MediaPlayer_jni.h" -#include "media/base/android/media_player_bridge_manager.h" +#include "media/base/android/media_player_manager.h" #include "media/base/android/media_resource_getter.h" using base::android::ConvertUTF8ToJavaString; @@ -21,28 +20,60 @@ using base::android::ScopedJavaLocalRef; // Time update happens every 250ms. static const int kTimeUpdateInterval = 250; -// Because we create the media player lazily on android, the duration of the -// media is initially unknown to us. This makes the user unable to perform +// Android MediaMetadataRetriever may fail to extract the metadata from the +// media under some circumstances. This makes the user unable to perform // seek. To solve this problem, we use a temporary duration of 100 seconds when // the duration is unknown. And we scale the seek position later when duration // is available. -// TODO(qinmin): create a thread and use android MediaMetadataRetriever -// class to extract the duration. static const int kTemporaryDuration = 100; namespace media { +#if !defined(GOOGLE_TV) +// static +MediaPlayerBridge* MediaPlayerBridge::Create( + int player_id, + const GURL& url, + bool is_media_source, + const GURL& first_party_for_cookies, + bool hide_url_log, + MediaPlayerManager* manager, + const MediaErrorCB& media_error_cb, + const VideoSizeChangedCB& video_size_changed_cb, + const BufferingUpdateCB& buffering_update_cb, + const MediaMetadataChangedCB& media_prepared_cb, + const PlaybackCompleteCB& playback_complete_cb, + const SeekCompleteCB& seek_complete_cb, + const TimeUpdateCB& time_update_cb, + const MediaInterruptedCB& media_interrupted_cb) { + LOG_IF(WARNING, is_media_source) << "MSE is not supported"; + return new MediaPlayerBridge( + player_id, + url, + first_party_for_cookies, + hide_url_log, + manager, + media_error_cb, + video_size_changed_cb, + buffering_update_cb, + media_prepared_cb, + playback_complete_cb, + seek_complete_cb, + time_update_cb, + media_interrupted_cb); +} +#endif + MediaPlayerBridge::MediaPlayerBridge( int player_id, const GURL& url, const GURL& first_party_for_cookies, - MediaResourceGetter* resource_getter, bool hide_url_log, - MediaPlayerBridgeManager* manager, + MediaPlayerManager* manager, const MediaErrorCB& media_error_cb, const VideoSizeChangedCB& video_size_changed_cb, const BufferingUpdateCB& buffering_update_cb, - const MediaPreparedCB& media_prepared_cb, + const MediaMetadataChangedCB& media_metadata_changed_cb, const PlaybackCompleteCB& playback_complete_cb, const SeekCompleteCB& seek_complete_cb, const TimeUpdateCB& time_update_cb, @@ -50,7 +81,7 @@ MediaPlayerBridge::MediaPlayerBridge( : media_error_cb_(media_error_cb), video_size_changed_cb_(video_size_changed_cb), buffering_update_cb_(buffering_update_cb), - media_prepared_cb_(media_prepared_cb), + media_metadata_changed_cb_(media_metadata_changed_cb), playback_complete_cb_(playback_complete_cb), seek_complete_cb_(seek_complete_cb), media_interrupted_cb_(media_interrupted_cb), @@ -68,23 +99,54 @@ MediaPlayerBridge::MediaPlayerBridge( can_seek_forward_(true), can_seek_backward_(true), manager_(manager), - resource_getter_(resource_getter), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), + weak_this_(this), listener_(base::MessageLoopProxy::current(), weak_this_.GetWeakPtr()) { - has_cookies_ = url_.SchemeIsFileSystem() || url_.SchemeIsFile(); + Initialize(); } MediaPlayerBridge::~MediaPlayerBridge() { Release(); } -void MediaPlayerBridge::InitializePlayer() { +void MediaPlayerBridge::Initialize() { + if (url_.SchemeIsFile()) { + cookies_.clear(); + ExtractMediaMetadata(url_.spec()); + return; + } + + media::MediaResourceGetter* resource_getter = + manager_->GetMediaResourceGetter(); + + if (url_.SchemeIsFileSystem()) { + cookies_.clear(); + resource_getter->GetPlatformPathFromFileSystemURL(url_, base::Bind( + &MediaPlayerBridge::ExtractMediaMetadata, weak_this_.GetWeakPtr())); + return; + } + + resource_getter->GetCookies(url_, first_party_for_cookies_, base::Bind( + &MediaPlayerBridge::OnCookiesRetrieved, weak_this_.GetWeakPtr())); +} + +void MediaPlayerBridge::CreateMediaPlayer() { JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); j_media_player_.Reset(JNI_MediaPlayer::Java_MediaPlayer_Constructor(env)); + SetMediaPlayerListener(); +} + +void MediaPlayerBridge::SetMediaPlayer(jobject j_media_player) { + JNIEnv* env = base::android::AttachCurrentThread(); + CHECK(env); + + j_media_player_.Reset(env, j_media_player); +} + +void MediaPlayerBridge::SetMediaPlayerListener() { jobject j_context = base::android::GetApplicationContext(); DCHECK(j_context); @@ -92,8 +154,11 @@ void MediaPlayerBridge::InitializePlayer() { } void MediaPlayerBridge::SetVideoSurface(jobject surface) { - if (j_media_player_.is_null() && surface != NULL) + if (j_media_player_.is_null()) { + if (surface == NULL) + return; Prepare(); + } JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); @@ -104,26 +169,11 @@ void MediaPlayerBridge::SetVideoSurface(jobject surface) { void MediaPlayerBridge::Prepare() { if (j_media_player_.is_null()) - InitializePlayer(); - - if (has_cookies_) { - GetCookiesCallback(cookies_); - } else { - resource_getter_->GetCookies(url_, first_party_for_cookies_, base::Bind( - &MediaPlayerBridge::GetCookiesCallback, weak_this_.GetWeakPtr())); - } -} - -void MediaPlayerBridge::GetCookiesCallback(const std::string& cookies) { - cookies_ = cookies; - has_cookies_ = true; - - if (j_media_player_.is_null()) - return; - + CreateMediaPlayer(); if (url_.SchemeIsFileSystem()) { - resource_getter_->GetPlatformPathFromFileSystemURL(url_, base::Bind( - &MediaPlayerBridge::SetDataSource, weak_this_.GetWeakPtr())); + manager_->GetMediaResourceGetter()->GetPlatformPathFromFileSystemURL( + url_, base::Bind(&MediaPlayerBridge::SetDataSource, + weak_this_.GetWeakPtr())); } else { SetDataSource(url_.spec()); } @@ -147,8 +197,7 @@ void MediaPlayerBridge::SetDataSource(const std::string& url) { if (Java_MediaPlayerBridge_setDataSource( env, j_media_player_.obj(), j_context, j_url_string.obj(), j_cookies.obj(), hide_url_log_)) { - if (manager_) - manager_->RequestMediaResources(this); + RequestMediaResourcesFromManager(); JNI_MediaPlayer::Java_MediaPlayer_prepareAsync( env, j_media_player_.obj()); } else { @@ -156,6 +205,33 @@ void MediaPlayerBridge::SetDataSource(const std::string& url) { } } +void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) { + cookies_ = cookies; + ExtractMediaMetadata(url_.spec()); +} + +void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) { + manager_->GetMediaResourceGetter()->ExtractMediaMetadata( + url, cookies_, base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted, + weak_this_.GetWeakPtr())); +} + +void MediaPlayerBridge::OnMediaMetadataExtracted( + base::TimeDelta duration, int width, int height, bool success) { + if (success) { + duration_ = duration; + width_ = width; + height_ = height; + } + media_metadata_changed_cb_.Run(player_id_, duration_, width_, height_, + success); +} + +void MediaPlayerBridge::RequestMediaResourcesFromManager() { + if (manager_) + manager_->RequestMediaResources(this); +} + void MediaPlayerBridge::Start() { if (j_media_player_.is_null()) { pending_play_ = true; @@ -315,19 +391,18 @@ void MediaPlayerBridge::OnMediaPrepared() { // If media player was recovered from a saved state, consume all the pending // events. - SeekInternal(pending_seek_); + PendingSeekInternal(pending_seek_); if (pending_play_) { StartInternal(); pending_play_ = false; } - GetMetadata(); - - media_prepared_cb_.Run(player_id_, duration_); + GetAllowedOperations(); + media_metadata_changed_cb_.Run(player_id_, duration_, width_, height_, true); } -void MediaPlayerBridge::GetMetadata() { +void MediaPlayerBridge::GetAllowedOperations() { JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); @@ -357,6 +432,10 @@ void MediaPlayerBridge::PauseInternal() { time_update_timer_.Stop(); } +void MediaPlayerBridge::PendingSeekInternal(base::TimeDelta time) { + SeekInternal(time); +} + void MediaPlayerBridge::SeekInternal(base::TimeDelta time) { JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); @@ -374,4 +453,16 @@ bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) { return ret; } +#if defined(GOOGLE_TV) +void MediaPlayerBridge::DemuxerReady( + const MediaPlayerHostMsg_DemuxerReady_Params& params) { + NOTREACHED() << "Unexpected ipc received"; +} + +void MediaPlayerBridge::ReadFromDemuxerAck( + const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) { + NOTREACHED() << "Unexpected ipc received"; +} +#endif + } // namespace media diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h index db32d94223..6733ec6598 100644 --- a/media/base/android/media_player_bridge.h +++ b/media/base/android/media_player_bridge.h @@ -17,12 +17,14 @@ #include "base/timer.h" #include "googleurl/src/gurl.h" #include "media/base/media_export.h" +#if defined(GOOGLE_TV) +#include "media/base/android/demuxer_stream_player_params.h" +#endif #include "media/base/android/media_player_listener.h" namespace media { -class MediaResourceGetter; -class MediaPlayerBridgeManager; +class MediaPlayerManager; // This class serves as a bridge for native code to call java functions inside // android mediaplayer class. For more information on android mediaplayer, check @@ -52,7 +54,8 @@ class MEDIA_EXPORT MediaPlayerBridge { typedef base::Callback<void(int, int)> BufferingUpdateCB; // Callback when player got prepared. Args: player ID, duration of the media. - typedef base::Callback<void(int, base::TimeDelta)> MediaPreparedCB; + typedef base::Callback<void(int, base::TimeDelta, int, int, bool)> + MediaMetadataChangedCB; // Callbacks when seek completed. Args: player ID, current time. typedef base::Callback<void(int, base::TimeDelta)> SeekCompleteCB; @@ -67,8 +70,33 @@ class MEDIA_EXPORT MediaPlayerBridge { // current time. typedef base::Callback<void(int, base::TimeDelta)> TimeUpdateCB; +#if defined(GOOGLE_TV) + // Callback when DemuxerStreamPlayer wants to read data from the demuxer. + typedef base::Callback<void(int, DemuxerStream::Type, bool)> + ReadFromDemuxerCB; +#endif + static bool RegisterMediaPlayerBridge(JNIEnv* env); + static MediaPlayerBridge* Create( + int player_id, + const GURL& url, + bool is_media_source, + const GURL& first_party_for_cookies, + bool hide_url_log, + MediaPlayerManager* manager, +#if defined(GOOGLE_TV) + const ReadFromDemuxerCB read_from_demuxer_cb, +#endif + const MediaErrorCB& media_error_cb, + const VideoSizeChangedCB& video_size_changed_cb, + const BufferingUpdateCB& buffering_update_cb, + const MediaMetadataChangedCB& media_prepared_cb, + const PlaybackCompleteCB& playback_complete_cb, + const SeekCompleteCB& seek_complete_cb, + const TimeUpdateCB& time_update_cb, + const MediaInterruptedCB& media_interrupted_cb); + // Construct a MediaPlayerBridge object with all the needed media player // callbacks. This object needs to call |manager|'s RequestMediaResources() // before decoding the media stream. This allows |manager| to track @@ -77,18 +105,17 @@ class MEDIA_EXPORT MediaPlayerBridge { MediaPlayerBridge(int player_id, const GURL& url, const GURL& first_party_for_cookies, - MediaResourceGetter* resource_getter, bool hide_url_log, - MediaPlayerBridgeManager* manager, + MediaPlayerManager* manager, const MediaErrorCB& media_error_cb, const VideoSizeChangedCB& video_size_changed_cb, const BufferingUpdateCB& buffering_update_cb, - const MediaPreparedCB& media_prepared_cb, + const MediaMetadataChangedCB& media_prepared_cb, const PlaybackCompleteCB& playback_complete_cb, const SeekCompleteCB& seek_complete_cb, const TimeUpdateCB& time_update_cb, const MediaInterruptedCB& media_interrupted_cb); - ~MediaPlayerBridge(); + virtual ~MediaPlayerBridge(); typedef std::map<std::string, std::string> HeadersMap; @@ -105,7 +132,7 @@ class MEDIA_EXPORT MediaPlayerBridge { void SeekTo(base::TimeDelta time); // Release the player resources. - void Release(); + virtual void Release(); // Set the player volume. void SetVolume(float leftVolume, float rightVolume); @@ -117,8 +144,18 @@ class MEDIA_EXPORT MediaPlayerBridge { base::TimeDelta GetDuration(); bool IsPlaying(); - // Get metadata from the media. - void GetMetadata(); + // Get allowed operations from the player. + void GetAllowedOperations(); + +#if defined(GOOGLE_TV) + // Methods for DeumxerStreamPlayer. + // Informs DemuxerStreamPlayer that the demuxer is ready. + virtual void DemuxerReady( + const MediaPlayerHostMsg_DemuxerReady_Params& params); + // Called when the requested data is received from the demuxer. + virtual void ReadFromDemuxerAck( + const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params); +#endif // Called by the timer to check for current time routinely and generates // time update events. @@ -137,10 +174,11 @@ class MEDIA_EXPORT MediaPlayerBridge { // Prepare the player for playback, asynchronously. When succeeds, // OnMediaPrepared() will be called. Otherwise, OnMediaError() will // be called with an error type. - void Prepare(); + virtual void Prepare(); - // Callback function passed to |resource_getter_|. - void GetCookiesCallback(const std::string& cookies); + // Callback function passed to |resource_getter_|. Called when the cookies + // are retrieved. + void OnCookiesRetrieved(const std::string& cookies); int player_id() { return player_id_; } bool can_pause() { return can_pause_; } @@ -148,9 +186,19 @@ class MEDIA_EXPORT MediaPlayerBridge { bool can_seek_backward() { return can_seek_backward_; } bool prepared() { return prepared_; } + protected: + void SetMediaPlayer(jobject j_media_player); + void SetMediaPlayerListener(); + void RequestMediaResourcesFromManager(); + + virtual void PendingSeekInternal(base::TimeDelta time); + private: + // Initialize this object and extract the metadata from the media. + void Initialize(); + // Create the actual android media player. - void InitializePlayer(); + void CreateMediaPlayer(); // Set the data source for the media player. void SetDataSource(const std::string& url); @@ -160,11 +208,17 @@ class MEDIA_EXPORT MediaPlayerBridge { void PauseInternal(); void SeekInternal(base::TimeDelta time); + // Extract the media metadata from a url, asynchronously. + // OnMediaMetadataExtracted() will be called when this call finishes. + void ExtractMediaMetadata(const std::string& url); + void OnMediaMetadataExtracted(base::TimeDelta duration, int width, int height, + bool success); + // Callbacks when events are received. MediaErrorCB media_error_cb_; VideoSizeChangedCB video_size_changed_cb_; BufferingUpdateCB buffering_update_cb_; - MediaPreparedCB media_prepared_cb_; + MediaMetadataChangedCB media_metadata_changed_cb_; PlaybackCompleteCB playback_complete_cb_; SeekCompleteCB seek_complete_cb_; MediaInterruptedCB media_interrupted_cb_; @@ -190,9 +244,6 @@ class MEDIA_EXPORT MediaPlayerBridge { // First party url for cookies. GURL first_party_for_cookies_; - // Whether cookies are available. - bool has_cookies_; - // Hide url log from media player. bool hide_url_log_; @@ -210,10 +261,7 @@ class MEDIA_EXPORT MediaPlayerBridge { std::string cookies_; // Resource manager for all the media players. - MediaPlayerBridgeManager* manager_; - - // Object for retrieving resources for this media player. - scoped_ptr<MediaResourceGetter> resource_getter_; + MediaPlayerManager* manager_; // Java MediaPlayer instance. base::android::ScopedJavaGlobalRef<jobject> j_media_player_; diff --git a/media/base/android/media_player_bridge_manager.cc b/media/base/android/media_player_manager.cc index 26f2219693..45e8fa4c92 100644 --- a/media/base/android/media_player_bridge_manager.cc +++ b/media/base/android/media_player_manager.cc @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/base/android/media_player_bridge_manager.h" +#include "media/base/android/media_player_manager.h" namespace media { -MediaPlayerBridgeManager::~MediaPlayerBridgeManager() {} +MediaPlayerManager::~MediaPlayerManager() {} } // namespace media diff --git a/media/base/android/media_player_bridge_manager.h b/media/base/android/media_player_manager.h index 93bd99cbd2..c4ae120a8c 100644 --- a/media/base/android/media_player_bridge_manager.h +++ b/media/base/android/media_player_manager.h @@ -2,21 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_MANAGER_H_ -#define MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_MANAGER_H_ +#ifndef MEDIA_BASE_ANDROID_MEDIA_PLAYER_MANAGER_H_ +#define MEDIA_BASE_ANDROID_MEDIA_PLAYER_MANAGER_H_ #include "media/base/media_export.h" namespace media { class MediaPlayerBridge; +class MediaResourceGetter; // This class is responsible for managing active MediaPlayerBridge objects. -// It is implemented by webkit_media::MediaPlayerBridgeManagerImpl and -// content::MediaPlayerManagerAndroid. -class MEDIA_EXPORT MediaPlayerBridgeManager { +// It is implemented by content::MediaPlayerManagerImpl. +class MEDIA_EXPORT MediaPlayerManager { public: - virtual ~MediaPlayerBridgeManager(); + virtual ~MediaPlayerManager(); // Called by a MediaPlayerBridge object when it is going to decode // media streams. This helps the manager object maintain an array @@ -27,8 +27,11 @@ class MEDIA_EXPORT MediaPlayerBridgeManager { // Called when a MediaPlayerBridge object releases all its decoding // resources. virtual void ReleaseMediaResources(MediaPlayerBridge* player) = 0; + + // Return a pointer to the MediaResourceGetter object. + virtual MediaResourceGetter* GetMediaResourceGetter() = 0; }; } // namespace media -#endif // MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_MANAGER_H_ +#endif // MEDIA_BASE_ANDROID_MEDIA_PLAYER_MANAGER_H_ diff --git a/media/base/android/media_resource_getter.h b/media/base/android/media_resource_getter.h index ae4a6e52c5..abeee36cdd 100644 --- a/media/base/android/media_resource_getter.h +++ b/media/base/android/media_resource_getter.h @@ -9,6 +9,7 @@ #include "base/callback.h" #include "base/files/file_path.h" +#include "base/time.h" #include "googleurl/src/gurl.h" #include "media/base/media_export.h" @@ -20,6 +21,8 @@ class MEDIA_EXPORT MediaResourceGetter { public: typedef base::Callback<void(const std::string&)> GetCookieCB; typedef base::Callback<void(const std::string&)> GetPlatformPathCB; + typedef base::Callback<void(base::TimeDelta, int, int, bool)> + ExtractMediaMetadataCB; virtual ~MediaResourceGetter(); // Method for getting the cookies for a given URL. @@ -31,6 +34,13 @@ class MEDIA_EXPORT MediaResourceGetter { virtual void GetPlatformPathFromFileSystemURL( const GURL& url, const GetPlatformPathCB& callback) = 0; + + // Extract 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 ExtractMediaMetadataCB& callback) = 0; }; } // namespace media diff --git a/media/base/android/webaudio_media_codec_bridge.cc b/media/base/android/webaudio_media_codec_bridge.cc new file mode 100644 index 0000000000..7a21261b13 --- /dev/null +++ b/media/base/android/webaudio_media_codec_bridge.cc @@ -0,0 +1,131 @@ +// Copyright (c) 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. + +#include "media/base/android/webaudio_media_codec_bridge.h" + +#include <errno.h> +#include <unistd.h> +#include <vector> + +#include "base/android/jni_android.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "jni/WebAudioMediaCodecBridge_jni.h" +#include "media/base/android/webaudio_media_codec_info.h" + + +using base::android::AttachCurrentThread; + +namespace media { + +void WebAudioMediaCodecBridge::RunWebAudioMediaCodec( + base::SharedMemoryHandle encoded_audio_handle, + base::FileDescriptor pcm_output, + size_t data_size) { + WebAudioMediaCodecBridge bridge(encoded_audio_handle, pcm_output, data_size); + + bridge.DecodeInMemoryAudioFile(); +} + +WebAudioMediaCodecBridge::WebAudioMediaCodecBridge( + base::SharedMemoryHandle encoded_audio_handle, + base::FileDescriptor pcm_output, + size_t data_size) + : encoded_audio_handle_(encoded_audio_handle.fd), + pcm_output_(pcm_output.fd), + data_size_(data_size) { + DVLOG(1) << "WebAudioMediaCodecBridge start **********************" + << "input fd = " << encoded_audio_handle_ + << " output fd = " << pcm_output.fd; +} + +WebAudioMediaCodecBridge::~WebAudioMediaCodecBridge() { + if (close(pcm_output_)) { + DVLOG(1) << "Couldn't close output fd " << pcm_output_ + << ": " << strerror(errno); + } + + if (close(encoded_audio_handle_)) { + DVLOG(1) << "Couldn't close shared mem fd " << encoded_audio_handle_ + << ": " << strerror(errno); + } +} + +bool WebAudioMediaCodecBridge::DecodeInMemoryAudioFile() { + // Process the encoded data from |encoded_data_handle_|. + + JNIEnv* env = AttachCurrentThread(); + CHECK(env); + jboolean decoded = Java_WebAudioMediaCodecBridge_decodeAudioFile( + env, + base::android::GetApplicationContext(), + reinterpret_cast<intptr_t>(this), + encoded_audio_handle_, + data_size_); + + DVLOG(1) << "decoded = " << decoded; + return decoded; +} + +void WebAudioMediaCodecBridge::InitializeDestination( + JNIEnv* env, + jobject /*java object*/, + jint channel_count, + jint sample_rate, + jlong duration_microsec) { + // Send information about this audio file: number of channels, + // sample rate (Hz), and the number of frames. + struct WebAudioMediaCodecInfo info = { + static_cast<unsigned long>(channel_count), + static_cast<unsigned long>(sample_rate), + // The number of frames is the duration of the file + // (in microseconds) times the sample rate. + static_cast<unsigned long>( + 0.5 + (duration_microsec * 0.000001 * + sample_rate)) + }; + + DVLOG(1) << "InitializeDestination:" + << " channel count = " << channel_count + << " rate = " << sample_rate + << " duration = " << duration_microsec << " microsec"; + + HANDLE_EINTR(write(pcm_output_, &info, sizeof(info))); +} + +void WebAudioMediaCodecBridge::OnChunkDecoded( + JNIEnv* env, + jobject /*java object*/, + jobject buf, + jint buf_size) { + + if (buf_size <= 0 || !buf) + return; + + int8_t* buffer = + static_cast<int8_t*>(env->GetDirectBufferAddress(buf)); + + size_t count = static_cast<size_t>(buf_size); + + // Write out the data to the pipe in small chunks if necessary. + while (count > 0) { + int bytes_to_write = (count >= PIPE_BUF) ? PIPE_BUF : count; + ssize_t bytes_written = HANDLE_EINTR(write(pcm_output_, + buffer, + bytes_to_write)); + if (bytes_written == -1) + break; + count -= bytes_written; + buffer += bytes_written; + } +} + +bool WebAudioMediaCodecBridge::RegisterWebAudioMediaCodecBridge(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace diff --git a/media/base/android/webaudio_media_codec_bridge.h b/media/base/android/webaudio_media_codec_bridge.h new file mode 100644 index 0000000000..76de6677e2 --- /dev/null +++ b/media/base/android/webaudio_media_codec_bridge.h @@ -0,0 +1,73 @@ +// Copyright (c) 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. + +#ifndef MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_ +#define MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_ + +#include <jni.h> + +#include "base/file_descriptor_posix.h" +#include "base/shared_memory.h" +#include "media/base/media_export.h" + +namespace media { + +// This class serves as a bridge for native code to call Java +// functions in the Android MediaCodec class. See +// http://developer.android.com/reference/android/media/MediaCodec.html. +class MEDIA_EXPORT WebAudioMediaCodecBridge { + public: + // Create the bridge with the given file descriptors. We read from + // |encoded_audio_handle| to get the encoded audio data. Audio file + // information and decoded PCM samples are written to |pcm_output|. + // We also take ownership of |pcm_output|. + WebAudioMediaCodecBridge(base::SharedMemoryHandle encoded_audio_handle, + base::FileDescriptor pcm_output, + size_t data_size); + ~WebAudioMediaCodecBridge(); + + // Inform JNI about this bridge. Returns true if registration + // succeeded. + static bool RegisterWebAudioMediaCodecBridge(JNIEnv* env); + + // Start MediaCodec to process the encoded data in + // |encoded_audio_handle|. The PCM samples are sent to |pcm_output|. + static void RunWebAudioMediaCodec( + base::SharedMemoryHandle encoded_audio_handle, + base::FileDescriptor pcm_output, + size_t data_size); + + void OnChunkDecoded(JNIEnv* env, + jobject /*java object*/, + jobject buf, + jint buf_size); + + void InitializeDestination(JNIEnv* env, + jobject /*java object*/, + jint channel_count, + jint sample_rate, + jlong duration_us); + + private: + // Handles MediaCodec processing of the encoded data in + // |encoded_audio_handle_| and sends the pcm data to |pcm_output_|. + // Returns true if decoding was successful. + bool DecodeInMemoryAudioFile(); + + // The encoded audio data is read from this file descriptor for the + // shared memory that holds the encoded data. + int encoded_audio_handle_; + + // The audio file information and decoded pcm data are written to + // this file descriptor. We take ownership of this descriptor. + int pcm_output_; + + // The length of the encoded data. + size_t data_size_; + + DISALLOW_COPY_AND_ASSIGN(WebAudioMediaCodecBridge); +}; + +} // namespace media +#endif // MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_ diff --git a/media/base/android/webaudio_media_codec_info.h b/media/base/android/webaudio_media_codec_info.h new file mode 100644 index 0000000000..423af91e8c --- /dev/null +++ b/media/base/android/webaudio_media_codec_info.h @@ -0,0 +1,20 @@ +// 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. + +#ifndef MEDIA_BASE_ANDROID_WEBAUDIO_MEDIA_CODEC_INFO_H_ +#define MEDIA_BASE_ANDROID_WEBAUDIO_MEDIA_CODEC_INFO_H_ + +namespace media { + +// This structure holds the information about the audio file +// determined by MediaCodec that is needed by the audio decoder to +// create the necessary destination bus. +struct WebAudioMediaCodecInfo { + unsigned long channel_count; + unsigned long sample_rate; + unsigned long number_of_frames; +}; + +} // namespace media +#endif // MEDIA_BASE_ANDROID_WEBAUDIO_MEDIA_CODEC_INFO_H_ |