diff --git a/media/tests/benchmark/MediaBenchmarkTest/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/Android.bp index 91b03f11dd..489ab04d36 100644 --- a/media/tests/benchmark/MediaBenchmarkTest/Android.bp +++ b/media/tests/benchmark/MediaBenchmarkTest/Android.bp @@ -29,6 +29,10 @@ android_test { "android.test.base", ], + jni_libs: [ + "libmediabenchmark_jni", + ], + static_libs: [ "libMediaBenchmark", "junit", diff --git a/media/tests/benchmark/MediaBenchmarkTest/build.gradle b/media/tests/benchmark/MediaBenchmarkTest/build.gradle index b0ee692698..b2aee1a9c4 100644 --- a/media/tests/benchmark/MediaBenchmarkTest/build.gradle +++ b/media/tests/benchmark/MediaBenchmarkTest/build.gradle @@ -30,7 +30,7 @@ android { compileSdkVersion 29 defaultConfig { applicationId "com.android.media.benchmark" - minSdkVersion 21 + minSdkVersion 28 targetSdkVersion 29 versionCode 1 versionName "1.0" @@ -48,6 +48,18 @@ android { manifest.srcFile 'AndroidManifest.xml' } } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + version "3.10.2" + } + } } repositories { diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java index be2633dbc8..b2233add3b 100644 --- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java +++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java @@ -27,6 +27,7 @@ import com.android.media.benchmark.R; import com.android.media.benchmark.library.CodecUtils; import com.android.media.benchmark.library.Decoder; import com.android.media.benchmark.library.Extractor; +import com.android.media.benchmark.library.Native; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,6 +45,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import static org.junit.Assert.assertTrue; + @RunWith(Parameterized.class) public class DecoderTest { private static final Context mContext = @@ -194,4 +197,32 @@ public class DecoderTest { + mInputFilePath); } } + + @Test + public void testNativeDecoder() throws IOException { + File inputFile = new File(mInputFilePath + mInputFile); + assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath, + inputFile.exists()); + int status = -1; + FileInputStream fileInput = new FileInputStream(inputFile); + FileDescriptor fileDescriptor = fileInput.getFD(); + Extractor extractor = new Extractor(); + int trackCount = extractor.setUpExtractor(fileDescriptor); + assertTrue("Extraction failed. No tracks for file: ", trackCount > 0); + for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) { + extractor.selectExtractorTrack(currentTrack); + MediaFormat format = extractor.getFormat(currentTrack); + String mime = format.getString(MediaFormat.KEY_MIME); + ArrayList mediaCodecs = CodecUtils.selectCodecs(mime, false); + for (String codecName : mediaCodecs) { + Log.i("Test: %s\n", mInputFile); + Native nativeDecoder = new Native(); + status = nativeDecoder.Decode(mInputFilePath, mInputFile, codecName, mAsyncMode); + assertTrue( + codecName + " decoder returned error " + status + " for file:" + mInputFile, + status == 0); + } + } + fileInput.close(); + } } diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java index 9db9c845a6..978dd95e44 100644 --- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java +++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java @@ -28,6 +28,7 @@ import com.android.media.benchmark.library.CodecUtils; import com.android.media.benchmark.library.Decoder; import com.android.media.benchmark.library.Encoder; import com.android.media.benchmark.library.Extractor; +import com.android.media.benchmark.library.Native; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,6 +44,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import static org.junit.Assert.assertTrue; + @RunWith(Parameterized.class) public class EncoderTest { private static final Context mContext = @@ -85,19 +88,16 @@ public class EncoderTest { public void sampleEncoderTest() throws Exception { int status; int frameSize; - //Parameters for video int width = 0; int height = 0; int profile = 0; int level = 0; int frameRate = 0; - //Parameters for audio int bitRate = 0; int sampleRate = 0; int numChannels = 0; - File inputFile = new File(mInputFilePath + mInputFile); if (inputFile.exists()) { FileInputStream fileInput = new FileInputStream(inputFile); @@ -129,7 +129,6 @@ public class EncoderTest { bufInfo.presentationTimeUs + " size = " + bufInfo.size); } } while (sampleSize > 0); - int tid = android.os.Process.myTid(); File decodedFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out"); FileOutputStream decodeOutputStream = new FileOutputStream(decodedFile); @@ -269,4 +268,43 @@ public class EncoderTest { mInputFilePath); } } + + @Test + public void testNativeEncoder() throws Exception { + File inputFile = new File(mInputFilePath + mInputFile); + assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath, + inputFile.exists()); + int status = -1; + int tid = android.os.Process.myTid(); + final String mDecodedFile = mContext.getFilesDir() + "/decoder_" + tid + ".out"; + FileInputStream fileInput = new FileInputStream(inputFile); + FileDescriptor fileDescriptor = fileInput.getFD(); + Extractor extractor = new Extractor(); + int trackCount = extractor.setUpExtractor(fileDescriptor); + assertTrue("Extraction failed. No tracks for file: ", trackCount > 0); + for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) { + extractor.selectExtractorTrack(currentTrack); + MediaFormat format = extractor.getFormat(currentTrack); + String mime = format.getString(MediaFormat.KEY_MIME); + ArrayList mediaCodecs = CodecUtils.selectCodecs(mime, true); + // Encoding the decoder's output + for (String codecName : mediaCodecs) { + Native nativeEncoder = new Native(); + status = nativeEncoder.Encode(mInputFilePath, mInputFile, mDecodedFile, codecName); + assertTrue( + codecName + " encoder returned error " + status + " for file:" + mInputFile, + status == 0); + } + } + File decodedFile = new File(mDecodedFile); + // Cleanup temporary input file + if (decodedFile.exists()) { + if (decodedFile.delete()) { + Log.i(TAG, "Successfully deleted - " + mDecodedFile); + } else { + Log.e(TAG, "Unable to delete - " + mDecodedFile); + } + } + fileInput.close(); + } } diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java index a02011c737..609cf00f00 100644 --- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java +++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java @@ -18,6 +18,7 @@ package com.android.media.benchmark.tests; import com.android.media.benchmark.R; import com.android.media.benchmark.library.Extractor; +import com.android.media.benchmark.library.Native; import android.content.Context; import android.util.Log; @@ -89,4 +90,25 @@ public class ExtractorTest { } assertThat(status, is(equalTo(0))); } + + @Test + public void sampleExtractNativeTest() throws IOException { + Native nativeExtractor = new Native(); + int status = -1; + File inputFile = new File(mInputFilePath + mInputFileName); + if (inputFile.exists()) { + FileInputStream fileInput = new FileInputStream(inputFile); + status = nativeExtractor.Extract(mInputFilePath, mInputFileName); + fileInput.close(); + if (status != 0) { + Log.e(TAG, "Extraction for " + mInputFileName + " failed."); + } else { + Log.i(TAG, "Extracted " + mInputFileName + " successfully."); + } + } else { + Log.e(TAG, "Cannot find " + inputFile + " in directory " + + mInputFilePath); + } + assertThat(status, is(equalTo(0))); + } } diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java index 8c3080cb8b..70da968675 100644 --- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java +++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java @@ -18,6 +18,7 @@ package com.android.media.benchmark.tests; import com.android.media.benchmark.R; import com.android.media.benchmark.library.Extractor; import com.android.media.benchmark.library.Muxer; +import com.android.media.benchmark.library.Native; import androidx.test.platform.app.InstrumentationRegistry; @@ -149,4 +150,35 @@ public class MuxerTest { } assertThat(status, is(equalTo(0))); } + + @Test + public void sampleMuxerNativeTest() { + int status = -1; + Native nativeMuxer = new Native(); + File inputFile = new File(mInputFilePath + mInputFileName); + if (inputFile.exists()) { + int tid = android.os.Process.myTid(); + String mMuxOutputFile = (mContext.getFilesDir() + "/mux_" + tid + ".out"); + status = nativeMuxer.Mux(mInputFilePath, mInputFileName, mMuxOutputFile, + mFormat); + if (status != 0) { + Log.e(TAG, "Mux for " + mInputFileName + " failed."); + } else { + Log.i(TAG, "Muxed " + mInputFileName + " successfully."); + } + File muxedFile = new File(mMuxOutputFile); + // Cleanup temporary output file + if (muxedFile.exists()) { + if (muxedFile.delete()) { + Log.i(TAG, "Successfully deleted" + mMuxOutputFile + " file."); + } else { + Log.e(TAG, "Unable to delete" + mMuxOutputFile + " file."); + } + } + } else { + Log.e(TAG, "Cannot find " + inputFile + " in directory " + + mInputFilePath); + } + assertThat(status, is(equalTo(0))); + } } diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp new file mode 100644 index 0000000000..a10d6b1e17 --- /dev/null +++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp @@ -0,0 +1,36 @@ +cc_test_library { + name: "libmediabenchmark_jni", + + defaults: [ + "libmediabenchmark_common-defaults", + "libmediabenchmark_soft_sanitize_all-defaults", + ], + + srcs: [ + "NativeExtractor.cpp", + "NativeMuxer.cpp", + "NativeEncoder.cpp", + "NativeDecoder.cpp", + ], + + shared_libs: [ + "liblog", + ], + + sdk_version: "current", + + static_libs: [ + "libmediabenchmark_common", + "libmediabenchmark_extractor", + "libmediabenchmark_muxer", + "libmediabenchmark_decoder", + "libmediabenchmark_encoder", + ], + + stl: "c++_static", + + cflags: [ + "-Wall", + "-Werror", + ], +} diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000..5823883661 --- /dev/null +++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt @@ -0,0 +1,44 @@ +# +# 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. +# + +cmake_minimum_required(VERSION 3.4.1) + +set(native_source_path "../../../../src/native") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") + +add_library( + mediabenchmark_jni SHARED + NativeExtractor.cpp + NativeMuxer.cpp + NativeDecoder.cpp + NativeEncoder.cpp + ${native_source_path}/common/BenchmarkCommon.cpp + ${native_source_path}/common/Stats.cpp + ${native_source_path}/common/utils/Timers.cpp + ${native_source_path}/extractor/Extractor.cpp + ${native_source_path}/muxer/Muxer.cpp + ${native_source_path}/decoder/Decoder.cpp + ${native_source_path}/encoder/Encoder.cpp) + +include_directories(${native_source_path}/common) +include_directories(${native_source_path}/extractor) +include_directories(${native_source_path}/muxer) +include_directories(${native_source_path}/decoder) +include_directories(${native_source_path}/encoder) + +find_library(log-lib log) + +target_link_libraries(mediabenchmark_jni mediandk ${log-lib}) diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp new file mode 100644 index 0000000000..5aa35a290e --- /dev/null +++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp @@ -0,0 +1,126 @@ +/* + * 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 "NativeDecoder" + +#include +#include +#include +#include + +#include + +#include "Decoder.h" + +extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Decode( + JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jCodecName, + jboolean asyncMode) { + 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; + } + + const char *codecName = env->GetStringUTFChars(jCodecName, nullptr); + string sCodecName = string(codecName); + decoder->setupDecoder(); + status = decoder->decode(inputBuffer, frameInfo, sCodecName, asyncMode); + if (status != AMEDIA_OK) { + ALOGE("Decode returned error"); + free(inputBuffer); + env->ReleaseStringUTFChars(jCodecName, codecName); + return -1; + } + decoder->deInitCodec(); + env->ReleaseStringUTFChars(jCodecName, codecName); + const char *inputReference = env->GetStringUTFChars(jFileName, nullptr); + string sInputReference = string(inputReference); + decoder->dumpStatistics(sInputReference); + env->ReleaseStringUTFChars(jFileName, inputReference); + if(inputBuffer) { + free(inputBuffer); + inputBuffer = nullptr; + } + decoder->resetDecoder(); + } + if(inputFp) { + fclose(inputFp); + inputFp = nullptr; + } + extractor->deInitExtractor(); + delete decoder; + return 0; +} diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp new file mode 100644 index 0000000000..2deda68362 --- /dev/null +++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp @@ -0,0 +1,199 @@ +/* + * 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 + +extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Encode( + JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jOutFilePath, + 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 *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 = 25; + if (!strcmp(mime, "video/3gpp") || !strcmp(mime, "video/mp4v-es")) { + encParams.bitrate = 600000 /* 600 Kbps */; + } else { + encParams.bitrate = 8000000 /* 8 Mbps */; + } + } + AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile); + AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level); + } else { + AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate); + AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, + &encParams.numChannels); + encParams.bitrate = + encParams.sampleRate * encParams.numChannels * 16 /* bitsPerSample */; + } + Encoder *encoder = new Encoder(); + encoder->setupEncoder(); + status = encoder->encode(sCodecName, eleStream, eleSize, asyncMode[i], encParams, + (char *)mime); + encoder->deInitCodec(); + cout << "codec : " << codecName << endl; + ALOGV(" asyncMode = %d \n", asyncMode[i]); + encoder->dumpStatistics(sInputReference, extractor->getClipDuration()); + 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; + } + decoder->deInitCodec(); + decoder->resetDecoder(); + } + if(inputFp) { + fclose(inputFp); + inputFp = nullptr; + } + extractor->deInitExtractor(); + delete decoder; + return 0; +} diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp new file mode 100644 index 0000000000..21099d3deb --- /dev/null +++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp @@ -0,0 +1,68 @@ +/* + * 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 "NativeExtractor" + +#include +#include +#include + +#include "Extractor.h" + +extern "C" +JNIEXPORT int32_t JNICALL +Java_com_android_media_benchmark_library_Native_Extract(JNIEnv *env, jobject thiz, + jstring jInputFilePath, + jstring jInputFileName) { + UNUSED(thiz); + const char *inputFilePath = env->GetStringUTFChars(jInputFilePath, nullptr); + const char *inputFileName = env->GetStringUTFChars(jInputFileName, nullptr); + string sFilePath = string(inputFilePath) + string(inputFileName); + FILE *inputFp = fopen(sFilePath.c_str(), "rb"); + + // Read file properties + struct stat buf; + stat(sFilePath.c_str(), &buf); + size_t fileSize = buf.st_size; + int32_t fd = fileno(inputFp); + + Extractor *extractObj = new Extractor(); + int32_t trackCount = extractObj->initExtractor((long)fd, fileSize); + if (trackCount <= 0) { + ALOGE("initExtractor failed"); + return -1; + } + + int32_t trackID = 0; + int32_t status = extractObj->extract(trackID); + if (status != AMEDIA_OK) { + ALOGE("Extraction failed"); + return -1; + } + if (inputFp) { + fclose(inputFp); + inputFp = nullptr; + } + extractObj->deInitExtractor(); + extractObj->dumpStatistics(inputFileName); + + env->ReleaseStringUTFChars(jInputFilePath, inputFilePath); + env->ReleaseStringUTFChars(jInputFileName, inputFileName); + + delete extractObj; + return status; +} diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp new file mode 100644 index 0000000000..4af845a8d7 --- /dev/null +++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp @@ -0,0 +1,182 @@ +/* + * 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 "NativeMuxer" + +#include +#include +#include + +#include "Muxer.h" + +MUXER_OUTPUT_T getMuxerOutFormat(const char *fmt); + +extern "C" +JNIEXPORT int32_t JNICALL +Java_com_android_media_benchmark_library_Native_Mux(JNIEnv *env, jobject thiz, + jstring jInputFilePath, jstring jInputFileName, + jstring jOutputFilePath, jstring jFormat) { + UNUSED(thiz); + ALOGV("Mux the samples given by extractor"); + const char *inputFilePath = env->GetStringUTFChars(jInputFilePath, nullptr); + const char *inputFileName = env->GetStringUTFChars(jInputFileName, nullptr); + string sInputFile = string(inputFilePath) + string(inputFileName); + FILE *inputFp = fopen(sInputFile.c_str(), "rb"); + if (!inputFp) { + ALOGE("Unable to open input file for reading"); + return -1; + } + + const char *fmt = env->GetStringUTFChars(jFormat, nullptr); + MUXER_OUTPUT_T outputFormat = getMuxerOutFormat(fmt); + env->ReleaseStringUTFChars(jFormat, fmt); + if (outputFormat == MUXER_OUTPUT_FORMAT_INVALID) { + ALOGE("output format is MUXER_OUTPUT_FORMAT_INVALID"); + return MUXER_OUTPUT_FORMAT_INVALID; + } + + Muxer *muxerObj = new Muxer(); + Extractor *extractor = muxerObj->getExtractor(); + if (!extractor) { + ALOGE("Extractor creation failed"); + return -1; + } + + // Read file properties + struct stat buf; + stat(sInputFile.c_str(), &buf); + size_t fileSize = buf.st_size; + 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("Allocation Failed"); + return -1; + } + vector frameInfos; + 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 muxer + if (inputBufferOffset + info.size > fileSize) { + ALOGE("Memory allocated not sufficient"); + if (inputBuffer) { + free(inputBuffer); + inputBuffer = nullptr; + } + return -1; + } + memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), + static_cast(info.size)); + info.offset = inputBufferOffset; + frameInfos.push_back(info); + inputBufferOffset += info.size; + } + + const char *outputFilePath = env->GetStringUTFChars(jOutputFilePath, nullptr); + FILE *outputFp = fopen(((string)outputFilePath).c_str(), "w+b"); + env->ReleaseStringUTFChars(jOutputFilePath, outputFilePath); + + if (!outputFp) { + ALOGE("Unable to open output file for writing"); + if (inputBuffer) { + free(inputBuffer); + inputBuffer = nullptr; + } + return -1; + } + int32_t outFd = fileno(outputFp); + + status = muxerObj->initMuxer(outFd, (MUXER_OUTPUT_T)outputFormat); + if (status != 0) { + ALOGE("initMuxer failed"); + if (inputBuffer) { + free(inputBuffer); + inputBuffer = nullptr; + } + return -1; + } + + status = muxerObj->mux(inputBuffer, frameInfos); + if (status != 0) { + ALOGE("Mux failed"); + if (inputBuffer) { + free(inputBuffer); + inputBuffer = nullptr; + } + return -1; + } + muxerObj->deInitMuxer(); + muxerObj->dumpStatistics(inputFileName); + env->ReleaseStringUTFChars(jInputFilePath, inputFilePath); + env->ReleaseStringUTFChars(jInputFileName, inputFileName); + + if (inputBuffer) { + free(inputBuffer); + inputBuffer = nullptr; + } + if (outputFp) { + fclose(outputFp); + outputFp = nullptr; + } + muxerObj->resetMuxer(); + } + if (inputFp) { + fclose(inputFp); + inputFp = nullptr; + } + extractor->deInitExtractor(); + delete muxerObj; + + return 0; +} + +MUXER_OUTPUT_T getMuxerOutFormat(const char *fmt) { + static const struct { + const char *name; + int value; + } kFormatMaps[] = {{"mp4", MUXER_OUTPUT_FORMAT_MPEG_4}, + {"webm", MUXER_OUTPUT_FORMAT_WEBM}, + {"3gpp", MUXER_OUTPUT_FORMAT_3GPP}, + {"ogg", MUXER_OUTPUT_FORMAT_OGG}}; + + int32_t muxOutputFormat = MUXER_OUTPUT_FORMAT_INVALID; + for (auto kFormatMap : kFormatMaps) { + if (!strcmp(fmt, kFormatMap.name)) { + muxOutputFormat = kFormatMap.value; + break; + } + } + return (MUXER_OUTPUT_T)muxOutputFormat; +} diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java new file mode 100644 index 0000000000..0e01754fdd --- /dev/null +++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package com.android.media.benchmark.library; + +public class Native { + static { System.loadLibrary("mediabenchmark_jni"); } + + public native int Extract(String inputFilePath, String inputFileName); + + public native int Mux(String inputFilePath, String inputFileName, String outputFilePath, + String format); + + public native int Decode(String inputFilePath, String inputFileName, String codecName, + boolean asyncMode); + + public native int Encode(String inputFilePath, String inputFileName, String outputFilePath, + String codecName); +} diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp index babc329a8a..6f7ffe6028 100644 --- a/media/tests/benchmark/src/native/common/Android.bp +++ b/media/tests/benchmark/src/native/common/Android.bp @@ -29,7 +29,7 @@ cc_library_static { export_include_dirs: ["."], - ldflags: ["-Wl,-Bsymbolic"] + ldflags: ["-Wl,-Bsymbolic"], } cc_defaults { @@ -52,10 +52,14 @@ cc_defaults { "liblog", ], + stl: "libc++_static", + + sdk_version: "current", + cflags: [ "-Wall", "-Werror", - ] + ], } cc_library_static { @@ -66,18 +70,20 @@ cc_library_static { srcs: [ "BenchmarkC2Common.cpp", + "BenchmarkCommon.cpp", + "Stats.cpp", + "utils/Timers.cpp", ], export_include_dirs: ["."], - ldflags: ["-Wl,-Bsymbolic"] + ldflags: ["-Wl,-Bsymbolic"], } cc_defaults { name: "libmediabenchmark_codec2_common-defaults", defaults: [ - "libmediabenchmark_common-defaults", "libcodec2-hidl-client-defaults", "libmediabenchmark_soft_sanitize_all-defaults", ], @@ -88,7 +94,14 @@ cc_defaults { shared_libs: [ "libcodec2_client", - ] + "libmediandk", + "liblog", + ], + + cflags: [ + "-Wall", + "-Werror", + ], } // public dependency for native implementation @@ -102,5 +115,5 @@ cc_defaults { "signed-integer-overflow", ], cfi: true, - } + }, } diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp index ab745086c2..cb49b8e8e5 100644 --- a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp +++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp @@ -56,6 +56,7 @@ void OnFormatChangedCB(AMediaCodec *codec, void *userdata, AMediaFormat *format) void OnErrorCB(AMediaCodec *codec, void *userdata, media_status_t err, int32_t actionCode, const char *detail) { + (void)codec; ALOGE("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail); CallBackHandle *self = (CallBackHandle *)userdata; self->mSawError = true; @@ -91,7 +92,7 @@ AMediaCodec *createMediaCodec(AMediaFormat *format, const char *mime, string cod /* Configure codec with the given format*/ const char *s = AMediaFormat_toString(format); - ALOGV("Input format: %s\n", s); + ALOGI("Input format: %s\n", s); media_status_t status = AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder); if (status != AMEDIA_OK) { @@ -99,4 +100,4 @@ AMediaCodec *createMediaCodec(AMediaFormat *format, const char *mime, string cod return nullptr; } return codec; -} \ No newline at end of file +} diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.h b/media/tests/benchmark/src/native/common/BenchmarkCommon.h index e13158afc4..c453a788c6 100644 --- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h +++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h @@ -27,6 +27,7 @@ #include #include "Stats.h" +#define UNUSED(x) (void)(x) using namespace std; diff --git a/media/tests/benchmark/src/native/common/Stats.cpp b/media/tests/benchmark/src/native/common/Stats.cpp index 2d9bb31657..e8b62dc9d3 100644 --- a/media/tests/benchmark/src/native/common/Stats.cpp +++ b/media/tests/benchmark/src/native/common/Stats.cpp @@ -53,14 +53,14 @@ void Stats::dumpStatistics(std::string operation, std::string inputReference, in } // Print the Stats - std::cout << "Input Reference : " << inputReference << endl; - std::cout << "Setup Time in nano sec : " << mInitTimeNs << endl; - std::cout << "Average Time in nano sec : " << totalTimeTakenNs / mOutputTimer.size() << endl; - std::cout << "Time to first frame in nano sec : " << timeToFirstFrameNs << endl; - std::cout << "Time taken (in nano sec) to " << operation - << " 1 sec of content : " << timeTakenPerSec << endl; - std::cout << "Total bytes " << operation << "ed : " << size << endl; - std::cout << "Minimum Time in nano sec : " << minTimeTakenNs << endl; - std::cout << "Maximum Time in nano sec : " << maxTimeTakenNs << endl; - std::cout << "Destroy Time in nano sec : " << mDeInitTimeNs << endl; + ALOGI("Input Reference : %s \n", inputReference.c_str()); + ALOGI("Setup Time in nano sec : %" PRId64 "\n", mInitTimeNs); + ALOGI("Average Time in nano sec : %" PRId64 "\n", totalTimeTakenNs / mOutputTimer.size()); + ALOGI("Time to first frame in nano sec : %" PRId64 "\n", timeToFirstFrameNs); + ALOGI("Time taken (in nano sec) to %s 1 sec of content : %" PRId64 "\n", operation.c_str(), + timeTakenPerSec); + ALOGI("Total bytes %sed : %d\n", operation.c_str(), size); + ALOGI("Minimum Time in nano sec : %" PRId64 "\n", minTimeTakenNs); + ALOGI("Maximum Time in nano sec : %" PRId64 "\n", maxTimeTakenNs); + ALOGI("Destroy Time in nano sec : %" PRId64 "\n", mDeInitTimeNs); } diff --git a/media/tests/benchmark/src/native/common/Stats.h b/media/tests/benchmark/src/native/common/Stats.h index 2f556eefa0..2ecf42f181 100644 --- a/media/tests/benchmark/src/native/common/Stats.h +++ b/media/tests/benchmark/src/native/common/Stats.h @@ -18,6 +18,7 @@ #define __STATS_H__ #include +#include #ifndef ALOG #define ALOG(priority, tag, ...) ((void)__android_log_print(ANDROID_##priority, tag, __VA_ARGS__)) @@ -25,6 +26,12 @@ #define ALOGI(...) ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__) #define ALOGE(...) ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__) #define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define ALOGW(...) ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__) + +#ifndef LOG_NDEBUG +#define LOG_NDEBUG 1 +#endif + #if LOG_NDEBUG #define ALOGV(cond, ...) ((void)0) #else diff --git a/media/tests/benchmark/src/native/decoder/Android.bp b/media/tests/benchmark/src/native/decoder/Android.bp index b5072abb56..9791c11d60 100644 --- a/media/tests/benchmark/src/native/decoder/Android.bp +++ b/media/tests/benchmark/src/native/decoder/Android.bp @@ -27,24 +27,26 @@ cc_library_static { export_include_dirs: ["."], - ldflags: ["-Wl,-Bsymbolic"] + ldflags: ["-Wl,-Bsymbolic"], } cc_library_static { name: "libmediabenchmark_codec2_decoder", defaults: [ - "libmediabenchmark_common-defaults", "libmediabenchmark_codec2_common-defaults", ], - srcs: ["C2Decoder.cpp"], + srcs: [ + "C2Decoder.cpp", + "Decoder.cpp", + ], static_libs: [ "libmediabenchmark_codec2_common", - "libmediabenchmark_extractor", + "libmediabenchmark_codec2_extractor", ], export_include_dirs: ["."], - ldflags: ["-Wl,-Bsymbolic"] + ldflags: ["-Wl,-Bsymbolic"], } diff --git a/media/tests/benchmark/src/native/encoder/Android.bp b/media/tests/benchmark/src/native/encoder/Android.bp index 57865efe39..8de782387a 100644 --- a/media/tests/benchmark/src/native/encoder/Android.bp +++ b/media/tests/benchmark/src/native/encoder/Android.bp @@ -23,19 +23,19 @@ cc_library_static { srcs: ["Encoder.cpp"], - static_libs: ["libmediabenchmark_extractor", - "libmediabenchmark_decoder", + static_libs: [ + "libmediabenchmark_extractor", + "libmediabenchmark_decoder", ], export_include_dirs: ["."], - ldflags: ["-Wl,-Bsymbolic"] + ldflags: ["-Wl,-Bsymbolic"], } cc_library_static { name: "libmediabenchmark_codec2_encoder", defaults: [ - "libmediabenchmark_common-defaults", "libmediabenchmark_codec2_common-defaults", ], @@ -43,11 +43,11 @@ cc_library_static { static_libs: [ "libmediabenchmark_codec2_common", - "libmediabenchmark_extractor", - "libmediabenchmark_decoder", + "libmediabenchmark_codec2_extractor", + "libmediabenchmark_codec2_decoder", ], export_include_dirs: ["."], - ldflags: ["-Wl,-Bsymbolic"] + ldflags: ["-Wl,-Bsymbolic"], } diff --git a/media/tests/benchmark/src/native/encoder/Encoder.cpp b/media/tests/benchmark/src/native/encoder/Encoder.cpp index a5605de5b4..4cb52ceadf 100644 --- a/media/tests/benchmark/src/native/encoder/Encoder.cpp +++ b/media/tests/benchmark/src/native/encoder/Encoder.cpp @@ -206,7 +206,7 @@ int32_t Encoder::encode(string &codecName, ifstream &eleStream, size_t eleSize, AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, mParams.bitrate); } const char *s = AMediaFormat_toString(mFormat); - ALOGV("Input format: %s\n", s); + ALOGI("Input format: %s\n", s); int64_t sTime = mStats->getCurTime(); mCodec = createMediaCodec(mFormat, mMime, codecName, true /*isEncoder*/); diff --git a/media/tests/benchmark/src/native/extractor/Android.bp b/media/tests/benchmark/src/native/extractor/Android.bp index dfd0d49fb2..7ed94764c3 100644 --- a/media/tests/benchmark/src/native/extractor/Android.bp +++ b/media/tests/benchmark/src/native/extractor/Android.bp @@ -27,3 +27,20 @@ cc_library_static { ldflags: ["-Wl,-Bsymbolic"] } + +cc_library_static { + name: "libmediabenchmark_codec2_extractor", + defaults: [ + "libmediabenchmark_codec2_common-defaults", + ], + + srcs: ["Extractor.cpp"], + + static_libs: [ + "libmediabenchmark_codec2_common", + ], + + export_include_dirs: ["."], + + ldflags: ["-Wl,-Bsymbolic"] +} diff --git a/media/tests/benchmark/tests/Android.bp b/media/tests/benchmark/tests/Android.bp index de6a8bf177..f46fa4a99e 100644 --- a/media/tests/benchmark/tests/Android.bp +++ b/media/tests/benchmark/tests/Android.bp @@ -87,7 +87,7 @@ cc_test { srcs: ["C2DecoderTest.cpp"], static_libs: [ - "libmediabenchmark_extractor", + "libmediabenchmark_codec2_extractor", "libmediabenchmark_codec2_common", "libmediabenchmark_codec2_decoder", ], @@ -103,8 +103,8 @@ cc_test { srcs: ["C2EncoderTest.cpp"], static_libs: [ - "libmediabenchmark_extractor", - "libmediabenchmark_decoder", + "libmediabenchmark_codec2_extractor", + "libmediabenchmark_codec2_decoder", "libmediabenchmark_codec2_common", "libmediabenchmark_codec2_encoder", ],