media_codec_bridge.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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/build_info.h"
10#include "base/android/jni_android.h"
11#include "base/android/jni_array.h"
12#include "base/android/jni_string.h"
13#include "base/basictypes.h"
14#include "base/lazy_instance.h"
15#include "base/logging.h"
16#include "base/safe_numerics.h"
17#include "base/strings/stringprintf.h"
18#include "jni/MediaCodecBridge_jni.h"
19#include "media/base/bit_reader.h"
20
21using base::android::AttachCurrentThread;
22using base::android::ConvertUTF8ToJavaString;
23using base::android::ScopedJavaLocalRef;
24
25namespace media {
26
27enum { kBufferFlagEndOfStream = 4 };
28
29static const char* AudioCodecToMimeType(const AudioCodec codec) {
30  switch (codec) {
31    case kCodecMP3:
32      return "audio/mpeg";
33    case kCodecVorbis:
34      return "audio/vorbis";
35    case kCodecAAC:
36      return "audio/mp4a-latm";
37    default:
38      return NULL;
39  }
40}
41
42static const char* VideoCodecToMimeType(const VideoCodec codec) {
43  switch (codec) {
44    case kCodecH264:
45      return "video/avc";
46    case kCodecVP8:
47      return "video/x-vnd.on2.vp8";
48    default:
49      return NULL;
50  }
51}
52
53// static
54const base::TimeDelta MediaCodecBridge::kTimeOutInfinity =
55    base::TimeDelta::FromMicroseconds(-1);
56
57// static
58const base::TimeDelta MediaCodecBridge::kTimeOutNoWait =
59    base::TimeDelta::FromMicroseconds(0);
60
61// static
62bool MediaCodecBridge::IsAvailable() {
63  // MediaCodec is only available on JB and greater.
64  return base::android::BuildInfo::GetInstance()->sdk_int() >= 16;
65}
66
67MediaCodecBridge::MediaCodecBridge(const char* mime) {
68  JNIEnv* env = AttachCurrentThread();
69  CHECK(env);
70  DCHECK(mime);
71
72  ScopedJavaLocalRef<jstring> j_type = ConvertUTF8ToJavaString(env, mime);
73  j_media_codec_.Reset(Java_MediaCodecBridge_create(
74      env, j_type.obj()));
75}
76
77MediaCodecBridge::~MediaCodecBridge() {
78  JNIEnv* env = AttachCurrentThread();
79  CHECK(env);
80  Java_MediaCodecBridge_release(env, j_media_codec_.obj());
81}
82
83void MediaCodecBridge::StartInternal() {
84  JNIEnv* env = AttachCurrentThread();
85  Java_MediaCodecBridge_start(env, j_media_codec_.obj());
86  GetOutputBuffers();
87}
88
89void MediaCodecBridge::Reset() {
90  JNIEnv* env = AttachCurrentThread();
91  Java_MediaCodecBridge_flush(env, j_media_codec_.obj());
92}
93
94void MediaCodecBridge::Stop() {
95  JNIEnv* env = AttachCurrentThread();
96  Java_MediaCodecBridge_stop(env, j_media_codec_.obj());
97}
98
99void MediaCodecBridge::GetOutputFormat(int* width, int* height) {
100  JNIEnv* env = AttachCurrentThread();
101
102  *width = Java_MediaCodecBridge_getOutputWidth(env, j_media_codec_.obj());
103  *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj());
104}
105
106size_t MediaCodecBridge::QueueInputBuffer(
107    int index, const uint8* data, int size,
108    const base::TimeDelta& presentation_time) {
109  JNIEnv* env = AttachCurrentThread();
110
111  ScopedJavaLocalRef<jobject> j_buffer(
112      Java_MediaCodecBridge_getInputBuffer(env, j_media_codec_.obj(), index));
113
114  uint8* direct_buffer =
115      static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj()));
116  int64 buffer_capacity = env->GetDirectBufferCapacity(j_buffer.obj());
117
118  size_t size_to_copy = (buffer_capacity < size) ? buffer_capacity : size;
119
120  if (size_to_copy > 0)
121    memcpy(direct_buffer, data, size_to_copy);
122  Java_MediaCodecBridge_queueInputBuffer(
123      env, j_media_codec_.obj(),
124      index, 0, size_to_copy, presentation_time.InMicroseconds(), 0);
125  return size_to_copy;
126}
127
128void MediaCodecBridge::QueueEOS(int input_buffer_index) {
129  JNIEnv* env = AttachCurrentThread();
130  Java_MediaCodecBridge_queueInputBuffer(
131      env, j_media_codec_.obj(),
132      input_buffer_index, 0, 0, 0, kBufferFlagEndOfStream);
133}
134
135int MediaCodecBridge::DequeueInputBuffer(base::TimeDelta timeout) {
136  JNIEnv* env = AttachCurrentThread();
137  return Java_MediaCodecBridge_dequeueInputBuffer(
138      env, j_media_codec_.obj(), timeout.InMicroseconds());
139}
140
141int MediaCodecBridge::DequeueOutputBuffer(
142    base::TimeDelta timeout, size_t* offset, size_t* size,
143    base::TimeDelta* presentation_time, bool* end_of_stream) {
144  JNIEnv* env = AttachCurrentThread();
145
146  ScopedJavaLocalRef<jobject> result =
147      Java_MediaCodecBridge_dequeueOutputBuffer(env, j_media_codec_.obj(),
148                                                timeout.InMicroseconds());
149
150  int j_buffer = Java_DequeueOutputResult_index(env, result.obj());
151  if (j_buffer >= 0) {
152    int64 presentation_time_us =
153        Java_DequeueOutputResult_presentationTimeMicroseconds(
154            env, result.obj());
155    int flags = Java_DequeueOutputResult_flags(env, result.obj());
156    *offset = base::checked_numeric_cast<size_t>(
157        Java_DequeueOutputResult_offset(env, result.obj()));
158    *size = base::checked_numeric_cast<size_t>(
159        Java_DequeueOutputResult_numBytes(env, result.obj()));
160    *presentation_time =
161        base::TimeDelta::FromMicroseconds(presentation_time_us);
162    *end_of_stream = flags & kBufferFlagEndOfStream;
163  }
164  return j_buffer;
165}
166
167void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) {
168  JNIEnv* env = AttachCurrentThread();
169  CHECK(env);
170
171  Java_MediaCodecBridge_releaseOutputBuffer(
172      env, j_media_codec_.obj(), index, render);
173}
174
175void MediaCodecBridge::GetOutputBuffers() {
176  JNIEnv* env = AttachCurrentThread();
177  Java_MediaCodecBridge_getOutputBuffers(env, j_media_codec_.obj());
178}
179
180AudioCodecBridge::AudioCodecBridge(const char* mime)
181    : MediaCodecBridge(mime) {
182}
183
184bool AudioCodecBridge::Start(
185    const AudioCodec codec, int sample_rate, int channel_count,
186    const uint8* extra_data, size_t extra_data_size, bool play_audio) {
187  JNIEnv* env = AttachCurrentThread();
188  DCHECK(AudioCodecToMimeType(codec));
189
190  ScopedJavaLocalRef<jstring> j_mime =
191      ConvertUTF8ToJavaString(env, AudioCodecToMimeType(codec));
192  ScopedJavaLocalRef<jobject> j_format(
193      Java_MediaCodecBridge_createAudioFormat(
194          env, j_mime.obj(), sample_rate, channel_count));
195  DCHECK(!j_format.is_null());
196
197  if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size))
198    return false;
199
200  if (!Java_MediaCodecBridge_configureAudio(
201      env, media_codec(), j_format.obj(), NULL, 0, play_audio)) {
202    return false;
203  }
204  StartInternal();
205  return true;
206}
207
208bool AudioCodecBridge::ConfigureMediaFormat(
209    jobject j_format, const AudioCodec codec, const uint8* extra_data,
210    size_t extra_data_size) {
211  if (extra_data_size == 0)
212    return true;
213
214  JNIEnv* env = AttachCurrentThread();
215  switch(codec) {
216    case kCodecVorbis:
217    {
218      if (extra_data[0] != 2) {
219        LOG(ERROR) << "Invalid number of vorbis headers before the codec "
220                   << "header: " << extra_data[0];
221        return false;
222      }
223
224      size_t header_length[2];
225      // |total_length| keeps track of the total number of bytes before the last
226      // header.
227      size_t total_length = 1;
228      const uint8* current_pos = extra_data;
229      // Calculate the length of the first 2 headers.
230      for (int i = 0; i < 2; ++i) {
231        header_length[i] = 0;
232        while (total_length < extra_data_size) {
233          size_t size = *(++current_pos);
234          total_length += 1 + size;
235          if (total_length > 0x80000000) {
236            LOG(ERROR) << "Vorbis header size too large";
237            return false;
238          }
239          header_length[i] += size;
240          if (size < 0xFF)
241            break;
242        }
243        if (total_length >= extra_data_size) {
244          LOG(ERROR) << "Invalid vorbis header size in the extra data";
245          return false;
246        }
247      }
248      current_pos++;
249      // The first header is identification header.
250      jobject identification_header = env->NewDirectByteBuffer(
251          const_cast<uint8*>(current_pos), header_length[0]);
252      Java_MediaCodecBridge_setCodecSpecificData(
253          env, j_format, 0, identification_header);
254      // The last header is codec header.
255      jobject codec_header = env->NewDirectByteBuffer(
256          const_cast<uint8*>(extra_data + total_length),
257          extra_data_size - total_length);
258      Java_MediaCodecBridge_setCodecSpecificData(
259          env, j_format, 1, codec_header);
260      env->DeleteLocalRef(codec_header);
261      env->DeleteLocalRef(identification_header);
262      break;
263    }
264    case kCodecAAC:
265    {
266      media::BitReader reader(extra_data, extra_data_size);
267
268      // The following code is copied from aac.cc
269      // TODO(qinmin): refactor the code in aac.cc to make it more reusable.
270      uint8 profile = 0;
271      uint8 frequency_index = 0;
272      uint8 channel_config = 0;
273      if (!reader.ReadBits(5, &profile) ||
274          !reader.ReadBits(4, &frequency_index)) {
275        LOG(ERROR) << "Unable to parse AAC header";
276        return false;
277      }
278      if (0xf == frequency_index && !reader.SkipBits(24)) {
279        LOG(ERROR) << "Unable to parse AAC header";
280        return false;
281      }
282      if (!reader.ReadBits(4, &channel_config)) {
283        LOG(ERROR) << "Unable to parse AAC header";
284        return false;
285      }
286
287      if (profile < 1 || profile > 4 || frequency_index == 0xf ||
288          channel_config > 7) {
289        LOG(ERROR) << "Invalid AAC header";
290        return false;
291      }
292      uint8 csd[2];
293      csd[0] = profile << 3 | frequency_index >> 1;
294      csd[1] = (frequency_index & 0x01) << 7 | channel_config << 3;
295      jobject header = env->NewDirectByteBuffer(csd, 2);
296      Java_MediaCodecBridge_setCodecSpecificData(
297          env, j_format, 0, header);
298      // TODO(qinmin): pass an extra variable to this function to determine
299      // whether we need to call this.
300      Java_MediaCodecBridge_setFrameHasADTSHeader(env, j_format);
301      env->DeleteLocalRef(header);
302      break;
303    }
304    default:
305      LOG(ERROR) << "Invalid header encountered for codec: "
306                 << AudioCodecToMimeType(codec);
307      return false;
308  }
309  return true;
310}
311
312void AudioCodecBridge::PlayOutputBuffer(int index, size_t size) {
313  DCHECK_LE(0, index);
314  int numBytes = base::checked_numeric_cast<int>(size);
315  JNIEnv* env = AttachCurrentThread();
316  ScopedJavaLocalRef<jobject> buf =
317      Java_MediaCodecBridge_getOutputBuffer(env, media_codec(), index);
318  uint8* buffer = static_cast<uint8*>(env->GetDirectBufferAddress(buf.obj()));
319
320  ScopedJavaLocalRef<jbyteArray> byte_array =
321      base::android::ToJavaByteArray(env, buffer, numBytes);
322  Java_MediaCodecBridge_playOutputBuffer(
323      env, media_codec(), byte_array.obj());
324}
325
326VideoCodecBridge::VideoCodecBridge(const char* mime)
327    : MediaCodecBridge(mime) {
328}
329
330bool VideoCodecBridge::Start(
331    const VideoCodec codec, const gfx::Size& size, jobject surface) {
332  JNIEnv* env = AttachCurrentThread();
333  DCHECK(VideoCodecToMimeType(codec));
334
335  ScopedJavaLocalRef<jstring> j_mime =
336      ConvertUTF8ToJavaString(env, VideoCodecToMimeType(codec));
337  ScopedJavaLocalRef<jobject> j_format(
338      Java_MediaCodecBridge_createVideoFormat(
339          env, j_mime.obj(), size.width(), size.height()));
340  DCHECK(!j_format.is_null());
341  if (!Java_MediaCodecBridge_configureVideo(
342      env, media_codec(), j_format.obj(), surface, NULL, 0)) {
343    return false;
344  }
345  StartInternal();
346  return true;
347}
348
349AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec codec) {
350  const char* mime = AudioCodecToMimeType(codec);
351  return mime ? new AudioCodecBridge(mime) : NULL;
352}
353
354VideoCodecBridge* VideoCodecBridge::Create(const VideoCodec codec) {
355  const char* mime = VideoCodecToMimeType(codec);
356  return mime ? new VideoCodecBridge(mime) : NULL;
357}
358
359bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) {
360  return RegisterNativesImpl(env);
361}
362
363}  // namespace media
364
365