blob: 1277c8b79906e664c3f03054fbb35418dd953f84 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "NativeEncoder"
#include <jni.h>
#include <sys/stat.h>
#include <fstream>
#include <iostream>
#include <android/log.h>
#include "Decoder.h"
#include "Encoder.h"
#include <stdio.h>
constexpr int32_t ENCODE_DEFAULT_FRAME_RATE = 25;
constexpr int32_t ENCODE_DEFAULT_AUDIO_BIT_RATE = 128000 /* 128 Kbps */;
constexpr int32_t ENCODE_DEFAULT_BIT_RATE = 8000000 /* 8 Mbps */;
constexpr int32_t ENCODE_MIN_BIT_RATE = 600000 /* 600 Kbps */;
extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Encode(
JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jOutFilePath,
jstring jStatsFile, jstring jCodecName) {
const char *filePath = env->GetStringUTFChars(jFilePath, nullptr);
const char *fileName = env->GetStringUTFChars(jFileName, nullptr);
string sFilePath = string(filePath) + string(fileName);
UNUSED(thiz);
FILE *inputFp = fopen(sFilePath.c_str(), "rb");
env->ReleaseStringUTFChars(jFileName, fileName);
env->ReleaseStringUTFChars(jFilePath, filePath);
if (!inputFp) {
ALOGE("Unable to open input file for reading");
return -1;
}
Decoder *decoder = new Decoder();
Extractor *extractor = decoder->getExtractor();
if (!extractor) {
ALOGE("Extractor creation failed");
return -1;
}
// Read file properties
struct stat buf;
stat(sFilePath.c_str(), &buf);
size_t fileSize = buf.st_size;
if (fileSize > kMaxBufferSize) {
ALOGE("File size greater than maximum buffer size");
return -1;
}
int32_t fd = fileno(inputFp);
int32_t trackCount = extractor->initExtractor(fd, fileSize);
if (trackCount <= 0) {
ALOGE("initExtractor failed");
return -1;
}
for (int curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
if (status != 0) {
ALOGE("Track Format invalid");
return -1;
}
uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
if (!inputBuffer) {
ALOGE("Insufficient memory");
return -1;
}
vector<AMediaCodecBufferInfo> frameInfo;
AMediaCodecBufferInfo info;
uint32_t inputBufferOffset = 0;
// Get frame data
while (1) {
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to decoder
if (inputBufferOffset + info.size > kMaxBufferSize) {
ALOGE("Memory allocated not sufficient");
free(inputBuffer);
return -1;
}
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
}
string decName = "";
const char *outputFilePath = env->GetStringUTFChars(jOutFilePath, nullptr);
FILE *outFp = fopen(outputFilePath, "wb");
if (outFp == nullptr) {
ALOGE("%s - File failed to open for writing!", outputFilePath);
free(inputBuffer);
return -1;
}
decoder->setupDecoder();
status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
if (status != AMEDIA_OK) {
ALOGE("Decode returned error");
free(inputBuffer);
return -1;
}
AMediaFormat *decoderFormat = decoder->getFormat();
AMediaFormat *format = extractor->getFormat();
if (inputBuffer) {
free(inputBuffer);
inputBuffer = nullptr;
}
const char *mime = nullptr;
AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
if (!mime) {
ALOGE("Error in AMediaFormat_getString");
return -1;
}
ifstream eleStream;
eleStream.open(outputFilePath, ifstream::binary | ifstream::ate);
if (!eleStream.is_open()) {
ALOGE("%s - File failed to open for reading!", outputFilePath);
env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
return -1;
}
const char *codecName = env->GetStringUTFChars(jCodecName, NULL);
const char *inputReference = env->GetStringUTFChars(jFileName, nullptr);
string sCodecName = string(codecName);
string sInputReference = string(inputReference);
bool asyncMode[2] = {true, false};
for (int i = 0; i < 2; i++) {
size_t eleSize = eleStream.tellg();
eleStream.seekg(0, ifstream::beg);
// Get encoder params
encParameter encParams;
if (!strncmp(mime, "video/", 6)) {
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &encParams.frameRate);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &encParams.bitrate);
if (encParams.bitrate <= 0 || encParams.frameRate <= 0) {
encParams.frameRate = ENCODE_DEFAULT_FRAME_RATE;
if (!strcmp(mime, "video/3gpp") || !strcmp(mime, "video/mp4v-es")) {
encParams.bitrate = ENCODE_MIN_BIT_RATE /* 600 Kbps */;
} else {
encParams.bitrate = ENCODE_DEFAULT_BIT_RATE /* 8 Mbps */;
}
}
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level);
AMediaFormat_getInt32(decoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT,
&encParams.colorFormat);
} else {
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
&encParams.numChannels);
encParams.bitrate = ENCODE_DEFAULT_AUDIO_BIT_RATE;
}
Encoder *encoder = new Encoder();
encoder->setupEncoder();
status = encoder->encode(sCodecName, eleStream, eleSize, asyncMode[i], encParams,
(char *)mime);
if (status != AMEDIA_OK) {
ALOGE("Encoder returned error");
return -1;
}
ALOGV("Encoding complete with codec %s for asyncMode = %d", sCodecName.c_str(),
asyncMode[i]);
encoder->deInitCodec();
const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
encoder->dumpStatistics(sInputReference, extractor->getClipDuration(), sCodecName,
(asyncMode[i] ? "async" : "sync"), statsFile);
env->ReleaseStringUTFChars(jStatsFile, statsFile);
encoder->resetEncoder();
delete encoder;
encoder = nullptr;
}
eleStream.close();
if (outFp) {
fclose(outFp);
outFp = nullptr;
}
env->ReleaseStringUTFChars(jFileName, inputReference);
env->ReleaseStringUTFChars(jCodecName, codecName);
env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
if (format) {
AMediaFormat_delete(format);
format = nullptr;
}
if (decoderFormat) {
AMediaFormat_delete(decoderFormat);
decoderFormat = nullptr;
}
decoder->deInitCodec();
decoder->resetDecoder();
}
if (inputFp) {
fclose(inputFp);
inputFp = nullptr;
}
extractor->deInitExtractor();
delete decoder;
return 0;
}