/* * 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 #include #include #include #include #include "Decoder.h" #include "Encoder.h" #include 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 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; }