Chromium Code Reviews| Index: media/base/android/ndk_media_codec_bridge.cc |
| diff --git a/media/base/android/ndk_media_codec_bridge.cc b/media/base/android/ndk_media_codec_bridge.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8287ddde77b24acf07c469e79c6a2269b7c4bfd7 |
| --- /dev/null |
| +++ b/media/base/android/ndk_media_codec_bridge.cc |
| @@ -0,0 +1,228 @@ |
| +// Copyright 2015 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/ndk_media_codec_bridge.h" |
| + |
| +#include <media/NdkMediaError.h> |
| +#include <media/NdkMediaFormat.h> |
| + |
| +#include "base/strings/string_util.h" |
| +#include "media/base/decrypt_config.h" |
| + |
| +namespace { |
| +const char kMediaFormatKeyCropLeft[] = "crop-left"; |
| +const char kMediaFormatKeyCropRight[] = "crop-right"; |
| +const char kMediaFormatKeyCropBottom[] = "crop-bottom"; |
| +const char kMediaFormatKeyCropTop[] = "crop-top"; |
| +} |
| + |
| +namespace media { |
| + |
| +// Translate media_status_t to MediaCodecStatus. |
| +static MediaCodecStatus TranslateMediaCodecStatus(media_status_t status) { |
| + switch (status) { |
| + case AMEDIA_OK: |
| + return MEDIA_CODEC_OK; |
| + case AMEDIA_DRM_NEED_KEY: |
| + return MEDIA_CODEC_NO_KEY; |
| + default: |
| + return MEDIA_CODEC_ERROR; |
| + } |
| +} |
| + |
| +NdkMediaCodecBridge::~NdkMediaCodecBridge() {} |
| + |
| +NdkMediaCodecBridge::NdkMediaCodecBridge(const std::string& mime, |
| + bool is_secure, |
| + MediaCodecDirection direction) { |
| + if (base::StartsWith(mime, "video", base::CompareCase::SENSITIVE) && |
| + is_secure && direction == MEDIA_CODEC_DECODER) { |
| + // TODO(qinmin): get the secure decoder name from java. |
| + NOTIMPLEMENTED(); |
| + return; |
| + } |
| + |
| + if (direction == MEDIA_CODEC_DECODER) |
| + media_codec_.reset(AMediaCodec_createDecoderByType(mime.c_str())); |
| + else |
| + media_codec_.reset(AMediaCodec_createEncoderByType(mime.c_str())); |
| +} |
| + |
| +MediaCodecStatus NdkMediaCodecBridge::Reset() { |
| + media_status_t status = AMediaCodec_flush(media_codec_.get()); |
| + return TranslateMediaCodecStatus(status); |
| +} |
| + |
| +bool NdkMediaCodecBridge::Start() { |
| + return AMEDIA_OK == AMediaCodec_start(media_codec_.get()); |
| +} |
| + |
| +void NdkMediaCodecBridge::Stop() { |
| + AMediaCodec_stop(media_codec_.get()); |
| +} |
| + |
| +void NdkMediaCodecBridge::GetOutputFormat(int* width, int* height) { |
| + AMediaFormat* format = AMediaCodec_getOutputFormat(media_codec_.get()); |
| + int left, right, bottom, top; |
| + bool has_left = AMediaFormat_getInt32(format, kMediaFormatKeyCropLeft, &left); |
| + bool has_right = |
| + AMediaFormat_getInt32(format, kMediaFormatKeyCropRight, &right); |
| + bool has_bottom = |
| + AMediaFormat_getInt32(format, kMediaFormatKeyCropBottom, &bottom); |
| + bool has_top = AMediaFormat_getInt32(format, kMediaFormatKeyCropTop, &top); |
| + if (has_left && has_right && has_bottom && has_top) { |
| + // Use crop size as it is more accurate. right and bottom are inclusive. |
| + *width = right - left + 1; |
| + *height = top - bottom + 1; |
| + } else { |
| + AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, width); |
| + AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, height); |
| + } |
| +} |
| + |
| +int NdkMediaCodecBridge::GetOutputSamplingRate() { |
| + AMediaFormat* format = AMediaCodec_getOutputFormat(media_codec_.get()); |
| + int sample_rate = 0; |
| + AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sample_rate); |
| + DCHECK(sample_rate != 0); |
| + return sample_rate; |
| +} |
| + |
| +MediaCodecStatus NdkMediaCodecBridge::QueueInputBuffer( |
| + int index, |
| + const uint8* data, |
| + size_t data_size, |
| + const base::TimeDelta& presentation_time) { |
| + if (data_size > base::checked_cast<size_t>(kint32max)) |
| + return MEDIA_CODEC_ERROR; |
| + if (data && !FillInputBuffer(index, data, data_size)) |
| + return MEDIA_CODEC_ERROR; |
| + |
| + media_status_t status = |
| + AMediaCodec_queueInputBuffer(media_codec_.get(), index, 0, data_size, |
| + presentation_time.InMicroseconds(), 0); |
| + return TranslateMediaCodecStatus(status); |
| +} |
| + |
| +MediaCodecStatus NdkMediaCodecBridge::QueueSecureInputBuffer( |
| + int index, |
| + const uint8* data, |
| + size_t data_size, |
| + std::vector<char> key_id, |
| + std::vector<char> iv, |
| + const SubsampleEntry* subsamples, |
| + int subsamples_size, |
| + const base::TimeDelta& presentation_time) { |
| + if (data_size > base::checked_cast<size_t>(kint32max)) |
| + return MEDIA_CODEC_ERROR; |
| + if (key_id.size() > 16 || iv.size()) |
| + return MEDIA_CODEC_ERROR; |
| + if (data && !FillInputBuffer(index, data, data_size)) |
| + return MEDIA_CODEC_ERROR; |
| + |
| + int new_subsamples_size = subsamples_size == 0 ? 1 : subsamples_size; |
| + std::vector<size_t> clear_data, encrypted_data; |
| + if (subsamples_size == 0) { |
| + DCHECK(!subsamples); |
| + clear_data.push_back(0); |
| + encrypted_data.push_back(data_size); |
| + } else { |
| + DCHECK_GT(subsamples_size, 0); |
| + DCHECK(subsamples); |
| + for (int i = 0; i < subsamples_size; ++i) { |
| + DCHECK(subsamples[i].clear_bytes <= std::numeric_limits<uint16>::max()); |
| + if (subsamples[i].cypher_bytes > |
| + static_cast<uint32>(std::numeric_limits<int32>::max())) { |
| + return MEDIA_CODEC_ERROR; |
| + } |
| + clear_data.push_back(subsamples[i].clear_bytes); |
| + encrypted_data.push_back(subsamples[i].cypher_bytes); |
| + } |
| + } |
| + |
| + AMediaCodecCryptoInfo* crypto_info = |
| + AMediaCodecCryptoInfo_new(new_subsamples_size, |
| + reinterpret_cast<uint8*>(&key_id[0]), |
|
xhwang
2015/12/04 18:07:58
we can use vector::data() now
qinmin
2015/12/04 20:26:04
Done.
|
| + reinterpret_cast<uint8*>(&iv[0]), |
| + AMEDIACODECRYPTOINFO_MODE_AES_CTR, |
| + clear_data.data(), encrypted_data.data()); |
| + |
| + media_status_t status = AMediaCodec_queueSecureInputBuffer( |
| + media_codec_.get(), index, 0, crypto_info, |
| + presentation_time.InMicroseconds(), 0); |
| + return TranslateMediaCodecStatus(status); |
| +} |
| + |
| +void NdkMediaCodecBridge::QueueEOS(int input_buffer_index) { |
| + AMediaCodec_queueInputBuffer(media_codec_.get(), input_buffer_index, 0, 0, 0, |
| + AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); |
| +} |
| + |
| +MediaCodecStatus NdkMediaCodecBridge::DequeueInputBuffer( |
| + const base::TimeDelta& timeout, |
| + int* index) { |
| + *index = AMediaCodec_dequeueInputBuffer(media_codec_.get(), |
| + timeout.InMicroseconds()); |
| + if (*index >= 0) |
| + return MEDIA_CODEC_OK; |
| + else if (*index == AMEDIACODEC_INFO_TRY_AGAIN_LATER) |
| + return MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER; |
| + else |
| + return MEDIA_CODEC_ERROR; |
| +} |
| + |
| +MediaCodecStatus NdkMediaCodecBridge::DequeueOutputBuffer( |
| + const base::TimeDelta& timeout, |
| + int* index, |
| + size_t* offset, |
| + size_t* size, |
| + base::TimeDelta* presentation_time, |
| + bool* end_of_stream, |
| + bool* key_frame) { |
| + AMediaCodecBufferInfo buffer_info; |
| + *index = AMediaCodec_dequeueOutputBuffer(media_codec_.get(), &buffer_info, |
| + timeout.InMicroseconds()); |
| + *offset = buffer_info.offset; |
| + *size = buffer_info.size; |
| + *presentation_time = |
| + base::TimeDelta::FromMicroseconds(buffer_info.presentationTimeUs); |
| + if (end_of_stream) |
| + *end_of_stream = buffer_info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM; |
| + if (key_frame) |
| + *key_frame = false; // This is deprecated. |
| + if (*index >= 0) |
| + return MEDIA_CODEC_OK; |
| + else if (*index == AMEDIACODEC_INFO_TRY_AGAIN_LATER) |
| + return MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER; |
| + else |
| + return MEDIA_CODEC_ERROR; |
| +} |
| + |
| +void NdkMediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { |
| + AMediaCodec_releaseOutputBuffer(media_codec_.get(), index, render); |
| +} |
| + |
| +void NdkMediaCodecBridge::GetInputBuffer(int input_buffer_index, |
| + uint8** data, |
| + size_t* capacity) { |
| + *data = AMediaCodec_getInputBuffer(media_codec_.get(), input_buffer_index, |
| + capacity); |
| +} |
| + |
| +bool NdkMediaCodecBridge::CopyFromOutputBuffer(int index, |
| + size_t offset, |
| + void* dst, |
| + int dst_size) { |
| + size_t capacity; |
| + uint8_t* src_data = |
| + AMediaCodec_getOutputBuffer(media_codec_.get(), index, &capacity); |
| + |
| + if (capacity < offset || capacity - offset < static_cast<size_t>(dst_size)) |
| + return false; |
| + |
| + memcpy(dst, src_data + offset, dst_size); |
| + return true; |
| +} |
| + |
| +} // namespace media |