OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/base/android/media_codec_bridge.h" | |
6 | |
7 #include <jni.h> | |
8 | |
9 #include "base/android/jni_android.h" | |
10 #include "base/android/jni_array.h" | |
11 #include "base/android/jni_string.h" | |
12 #include "base/basictypes.h" | |
13 #include "base/logging.h" | |
14 #include "base/stringprintf.h" | |
15 | |
16 #include "jni/MediaFormat_jni.h" | |
17 #include "jni/MediaCodec_jni.h" | |
18 | |
19 | |
20 using base::android::AttachCurrentThread; | |
21 using base::android::ConvertUTF8ToJavaString; | |
22 using base::android::ScopedJavaLocalRef; | |
23 | |
24 namespace { | |
25 | |
26 static jclass g_MediaCodecBufferInfo_clazz = NULL; | |
27 | |
28 static const char kMediaCodecBufferInfoClassPath[] = | |
29 "android/media/MediaCodec$BufferInfo"; | |
30 | |
31 static bool MediaCodecBufferInfo_RegisterNativesImpl(JNIEnv* env) { | |
32 g_MediaCodecBufferInfo_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( | |
33 base::android::GetUnscopedClass(env, kMediaCodecBufferInfoClassPath))); | |
34 base::android::CheckException(env); | |
35 return true; | |
36 } | |
37 | |
38 static void GetBufferInfo(JNIEnv* env, jobject buffer_info, int* offset, | |
39 int* size, int64* presentation_time, int* flags) { | |
40 static jfieldID offset_id = | |
41 env->GetFieldID(g_MediaCodecBufferInfo_clazz, "offset", "I"); | |
42 static jfieldID size_id = | |
43 env->GetFieldID(g_MediaCodecBufferInfo_clazz, "size", "I"); | |
44 static jfieldID presentation_time_id = | |
45 env->GetFieldID(g_MediaCodecBufferInfo_clazz, "presentationTimeUs", "J"); | |
46 static jfieldID flags_id = | |
47 env->GetFieldID(g_MediaCodecBufferInfo_clazz, "flags", "I"); | |
48 | |
49 *offset = env->GetIntField(buffer_info, offset_id); | |
50 *size = env->GetIntField(buffer_info, size_id); | |
51 *presentation_time = env->GetLongField(buffer_info, presentation_time_id); | |
52 *flags = env->GetIntField(buffer_info, flags_id); | |
53 } | |
54 | |
55 static base::subtle::AtomicWord g_MediaCodecBufferInfo_Constructor = 0; | |
56 static ScopedJavaLocalRef<jobject> Java_MediaCodecBufferInfo_Constructor( | |
57 JNIEnv* env) { | |
58 /* Must call RegisterNativesImpl() */ | |
59 DCHECK(g_MediaCodecBufferInfo_clazz); | |
60 jmethodID method_id = | |
61 base::android::MethodID::LazyGet<base::android::MethodID::TYPE_INSTANCE>( | |
62 env, g_MediaCodecBufferInfo_clazz, | |
63 "<init>", "()V", | |
64 &g_MediaCodecBufferInfo_Constructor); | |
65 | |
66 jobject ret = env->NewObject(g_MediaCodecBufferInfo_clazz, method_id); | |
67 base::android::CheckException(env); | |
68 return ScopedJavaLocalRef<jobject>(env, ret); | |
69 } | |
70 | |
71 void RegisterNativesIfNeeded(JNIEnv* env) { | |
72 static bool jni_initialized = | |
Ami GONE FROM CHROMIUM
2013/02/05 20:24:33
Thanks for asking about thread-safety of this appr
dwkang1
2013/02/07 14:01:36
Changed by using LazyInstance.
| |
73 JNI_MediaCodec::RegisterNativesImpl(env) | |
74 && MediaCodecBufferInfo_RegisterNativesImpl(env) | |
Ami GONE FROM CHROMIUM
2013/02/05 20:24:33
Operator (&&) belongs on previous line, here and e
dwkang1
2013/02/07 14:01:36
Done.
| |
75 && JNI_MediaFormat::RegisterNativesImpl(env); | |
76 DCHECK(jni_initialized); | |
77 } | |
78 } // namespace | |
79 | |
80 namespace media { | |
81 | |
82 static const char* CodecToMimeType(const MediaCodecBridge::Codec codec) { | |
83 switch (codec) { | |
84 case MediaCodecBridge::AUDIO_MPEG: return "audio/mpeg"; | |
85 case MediaCodecBridge::VIDEO_H264: return "video/avc"; | |
86 case MediaCodecBridge::VIDEO_VP8: return "video/x-vnd.on2.vp8"; | |
87 default: return NULL; | |
88 } | |
89 } | |
90 | |
91 // static | |
92 const base::TimeDelta MediaCodecBridge::kTimeOutInfinity = | |
93 base::TimeDelta::FromMicroseconds(-1); | |
94 | |
95 MediaCodecBridge::MediaCodecBridge(const Codec codec) { | |
96 JNIEnv* env = AttachCurrentThread(); | |
97 CHECK(env); | |
98 RegisterNativesIfNeeded(env); | |
99 DCHECK(CodecToMimeType(codec)); | |
100 | |
101 ScopedJavaLocalRef<jstring> j_type = | |
102 ConvertUTF8ToJavaString(env, CodecToMimeType(codec)); | |
103 | |
104 ScopedJavaLocalRef<jobject> tmp( | |
105 JNI_MediaCodec::Java_MediaCodec_createDecoderByType(env, j_type.obj())); | |
106 DCHECK(!tmp.is_null()); | |
107 j_media_codec_.Reset(tmp); | |
108 } | |
109 | |
110 MediaCodecBridge::~MediaCodecBridge() { | |
111 JNIEnv* env = AttachCurrentThread(); | |
112 CHECK(env); | |
113 | |
114 JNI_MediaCodec::Java_MediaCodec_release(env, j_media_codec_.obj()); | |
115 } | |
116 | |
117 void MediaCodecBridge::StartAudio( | |
118 const Codec codec, int sample_rate, int channel_count) { | |
119 JNIEnv* env = AttachCurrentThread(); | |
120 CHECK(env); | |
121 DCHECK(CodecToMimeType(codec)); | |
122 | |
123 ScopedJavaLocalRef<jstring> j_mime = | |
124 ConvertUTF8ToJavaString(env, CodecToMimeType(codec)); | |
125 ScopedJavaLocalRef<jobject> j_format( | |
126 JNI_MediaFormat::Java_MediaFormat_createAudioFormat( | |
127 env, j_mime.obj(), sample_rate, channel_count)); | |
128 DCHECK(!j_format.is_null()); | |
129 | |
130 JNI_MediaCodec::Java_MediaCodec_configure( | |
131 env, j_media_codec_.obj(), j_format.obj(), NULL, NULL, 0); | |
132 | |
133 Start(); | |
134 } | |
135 | |
136 void MediaCodecBridge::StartVideo( | |
137 const Codec codec, const gfx::Size& size, jobject surface) { | |
138 JNIEnv* env = AttachCurrentThread(); | |
139 CHECK(env); | |
140 DCHECK(CodecToMimeType(codec)); | |
141 | |
142 ScopedJavaLocalRef<jstring> j_mime = | |
143 ConvertUTF8ToJavaString(env, CodecToMimeType(codec)); | |
144 ScopedJavaLocalRef<jobject> j_format( | |
145 JNI_MediaFormat::Java_MediaFormat_createVideoFormat( | |
146 env, j_mime.obj(), size.width(), size.height())); | |
147 DCHECK(!j_format.is_null()); | |
148 | |
149 JNI_MediaCodec::Java_MediaCodec_configure( | |
150 env, j_media_codec_.obj(), j_format.obj(), surface, NULL, 0); | |
151 | |
152 Start(); | |
153 } | |
154 | |
155 void MediaCodecBridge::Start() { | |
156 JNIEnv* env = AttachCurrentThread(); | |
157 CHECK(env); | |
158 | |
159 JNI_MediaCodec::Java_MediaCodec_start(env, j_media_codec_.obj()); | |
160 } | |
161 | |
162 void MediaCodecBridge::Flush() { | |
163 JNIEnv* env = AttachCurrentThread(); | |
164 CHECK(env); | |
165 | |
166 JNI_MediaCodec::Java_MediaCodec_flush(env, j_media_codec_.obj()); | |
167 } | |
168 | |
169 void MediaCodecBridge::Stop() { | |
170 JNIEnv* env = AttachCurrentThread(); | |
171 CHECK(env); | |
172 | |
173 JNI_MediaCodec::Java_MediaCodec_stop(env, j_media_codec_.obj()); | |
174 } | |
175 | |
176 void MediaCodecBridge::GetOutputFormat(int* format, int* width, int* height) { | |
177 JNIEnv* env = AttachCurrentThread(); | |
178 CHECK(env); | |
179 | |
180 ScopedJavaLocalRef<jobject> j_format( | |
181 JNI_MediaCodec::Java_MediaCodec_getOutputFormat( | |
182 env, j_media_codec_.obj())); | |
183 if (!j_format.is_null()) { | |
184 ScopedJavaLocalRef<jstring> j_key_format = | |
185 ConvertUTF8ToJavaString(env, "color-format"); | |
186 *format = JNI_MediaFormat::Java_MediaFormat_getInteger( | |
187 env, j_format.obj(), j_key_format.obj()); | |
188 | |
189 ScopedJavaLocalRef<jstring> j_key_width = | |
190 ConvertUTF8ToJavaString(env, "width"); | |
191 *width = JNI_MediaFormat::Java_MediaFormat_getInteger( | |
192 env, j_format.obj(), j_key_width.obj()); | |
193 | |
194 ScopedJavaLocalRef<jstring> j_key_height = | |
195 ConvertUTF8ToJavaString(env, "height"); | |
196 *height = JNI_MediaFormat::Java_MediaFormat_getInteger( | |
197 env, j_format.obj(), j_key_height.obj()); | |
198 } | |
199 } | |
200 | |
201 void MediaCodecBridge::QueueInputBuffer( | |
202 int index, int offset, int size, base::TimeDelta presentation_time, | |
203 int flags) { | |
204 JNIEnv* env = AttachCurrentThread(); | |
205 CHECK(env); | |
206 | |
207 JNI_MediaCodec::Java_MediaCodec_queueInputBuffer( | |
208 env, j_media_codec_.obj(), | |
209 index, offset, size, presentation_time.InMicroseconds(), flags); | |
210 } | |
211 | |
212 int MediaCodecBridge::DequeueInputBuffer(base::TimeDelta timeout) { | |
213 JNIEnv* env = AttachCurrentThread(); | |
214 CHECK(env); | |
215 | |
216 return JNI_MediaCodec::Java_MediaCodec_dequeueInputBuffer( | |
217 env, j_media_codec_.obj(), timeout.InMicroseconds()); | |
218 } | |
219 | |
220 int MediaCodecBridge::DequeueOutputBuffer( | |
221 base::TimeDelta timeout, int* offset, int* size, | |
222 base::TimeDelta& presentation_time, int* flags) { | |
223 JNIEnv* env = AttachCurrentThread(); | |
224 CHECK(env); | |
225 | |
226 ScopedJavaLocalRef<jobject> j_info( | |
227 Java_MediaCodecBufferInfo_Constructor(env)); | |
228 jint j_buffer = JNI_MediaCodec::Java_MediaCodec_dequeueOutputBuffer( | |
229 env, j_media_codec_.obj(), j_info.obj(), timeout.InMicroseconds()); | |
230 | |
231 if (j_buffer >= 0) { | |
232 int64 presentation_time_us; | |
233 GetBufferInfo( | |
234 env, j_info.obj(), offset, size, &presentation_time_us, flags); | |
235 presentation_time = base::TimeDelta::FromMicroseconds(presentation_time_us); | |
236 } | |
237 return j_buffer; | |
238 } | |
239 | |
240 void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { | |
241 JNIEnv* env = AttachCurrentThread(); | |
242 CHECK(env); | |
243 | |
244 JNI_MediaCodec::Java_MediaCodec_releaseOutputBuffer( | |
245 env, j_media_codec_.obj(), index, static_cast<jboolean>(render)); | |
qinmin
2013/02/05 07:43:13
do we need static_cast for this? I think boolean a
dwkang1
2013/02/05 11:49:36
Done.
| |
246 } | |
247 | |
248 int MediaCodecBridge::GetInputBuffers() { | |
Ami GONE FROM CHROMIUM
2013/02/05 20:24:33
I think given the two unattractive facts:
1) An as
dwkang1
2013/02/07 14:01:36
Okay. I'll include this to Start().
| |
249 JNIEnv* env = AttachCurrentThread(); | |
250 CHECK(env); | |
251 | |
252 j_input_buffers_.Reset( | |
253 JNI_MediaCodec::Java_MediaCodec_getInputBuffers( | |
254 env, j_media_codec_.obj())); | |
255 | |
256 return env->GetArrayLength(j_input_buffers_.obj()); | |
257 } | |
258 | |
259 int MediaCodecBridge::GetOutputBuffers() { | |
260 JNIEnv* env = AttachCurrentThread(); | |
261 CHECK(env); | |
262 | |
263 j_output_buffers_.Reset( | |
264 JNI_MediaCodec::Java_MediaCodec_getOutputBuffers( | |
265 env, j_media_codec_.obj())); | |
266 | |
267 return env->GetArrayLength(j_output_buffers_.obj()); | |
268 } | |
269 | |
270 size_t MediaCodecBridge::PutToInputBuffer( | |
271 int index, const uint8* data, int size) { | |
272 JNIEnv* env = AttachCurrentThread(); | |
273 CHECK(env); | |
274 | |
275 ScopedJavaLocalRef<jobject> j_buffer( | |
276 env, env->GetObjectArrayElement(j_input_buffers_.obj(), index)); | |
277 | |
278 uint8* direct_buffer = | |
279 static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj())); | |
280 int64 buffer_capacity = env->GetDirectBufferCapacity(j_buffer.obj()); | |
281 | |
282 size_t size_to_copy = (buffer_capacity < size) ? buffer_capacity : size; | |
283 memcpy(direct_buffer, data, size_to_copy); | |
284 return size_to_copy; | |
285 } | |
286 | |
287 } // namespace media | |
288 | |
OLD | NEW |