blob: ae23aff1d6ad9f4eba10518cfa9c3aeb73d72439 [file] [log] [blame]
// 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,
const std::vector<char>& key_id,
const 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*>(const_cast<char*>(key_id.data())),
reinterpret_cast<uint8*>(const_cast<char*>(iv.data())),
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