1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "NativeEncoder"
19
20 #include <jni.h>
21 #include <sys/stat.h>
22 #include <fstream>
23 #include <iostream>
24
25 #include <android/log.h>
26
27 #include "Decoder.h"
28 #include "Encoder.h"
29
30 #include <stdio.h>
31
32 constexpr int32_t ENCODE_DEFAULT_FRAME_RATE = 25;
33 constexpr int32_t ENCODE_DEFAULT_AUDIO_BIT_RATE = 128000 /* 128 Kbps */;
34 constexpr int32_t ENCODE_DEFAULT_BIT_RATE = 8000000 /* 8 Mbps */;
35 constexpr int32_t ENCODE_MIN_BIT_RATE = 600000 /* 600 Kbps */;
36
Java_com_android_media_benchmark_library_Native_Encode(JNIEnv * env,jobject thiz,jstring jFilePath,jstring jFileName,jstring jOutFilePath,jstring jStatsFile,jstring jCodecName)37 extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Encode(
38 JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jOutFilePath,
39 jstring jStatsFile, jstring jCodecName) {
40 const char *filePath = env->GetStringUTFChars(jFilePath, nullptr);
41 const char *fileName = env->GetStringUTFChars(jFileName, nullptr);
42 string sFilePath = string(filePath) + string(fileName);
43 UNUSED(thiz);
44 FILE *inputFp = fopen(sFilePath.c_str(), "rb");
45 env->ReleaseStringUTFChars(jFileName, fileName);
46 env->ReleaseStringUTFChars(jFilePath, filePath);
47 if (!inputFp) {
48 ALOGE("Unable to open input file for reading");
49 return -1;
50 }
51
52 Decoder *decoder = new Decoder();
53 Extractor *extractor = decoder->getExtractor();
54 if (!extractor) {
55 ALOGE("Extractor creation failed");
56 return -1;
57 }
58
59 // Read file properties
60 struct stat buf;
61 stat(sFilePath.c_str(), &buf);
62 size_t fileSize = buf.st_size;
63 if (fileSize > kMaxBufferSize) {
64 ALOGE("File size greater than maximum buffer size");
65 return -1;
66 }
67 int32_t fd = fileno(inputFp);
68 int32_t trackCount = extractor->initExtractor(fd, fileSize);
69 if (trackCount <= 0) {
70 ALOGE("initExtractor failed");
71 return -1;
72 }
73
74 for (int curTrack = 0; curTrack < trackCount; curTrack++) {
75 int32_t status = extractor->setupTrackFormat(curTrack);
76 if (status != 0) {
77 ALOGE("Track Format invalid");
78 return -1;
79 }
80 uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
81 if (!inputBuffer) {
82 ALOGE("Insufficient memory");
83 return -1;
84 }
85 vector<AMediaCodecBufferInfo> frameInfo;
86 AMediaCodecBufferInfo info;
87 uint32_t inputBufferOffset = 0;
88
89 // Get frame data
90 while (1) {
91 status = extractor->getFrameSample(info);
92 if (status || !info.size) break;
93 // copy the meta data and buffer to be passed to decoder
94 if (inputBufferOffset + info.size > kMaxBufferSize) {
95 ALOGE("Memory allocated not sufficient");
96 free(inputBuffer);
97 return -1;
98 }
99 memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
100 frameInfo.push_back(info);
101 inputBufferOffset += info.size;
102 }
103 string decName = "";
104 const char *outputFilePath = env->GetStringUTFChars(jOutFilePath, nullptr);
105 FILE *outFp = fopen(outputFilePath, "wb");
106 if (outFp == nullptr) {
107 ALOGE("%s - File failed to open for writing!", outputFilePath);
108 free(inputBuffer);
109 return -1;
110 }
111 decoder->setupDecoder();
112 status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
113 if (status != AMEDIA_OK) {
114 ALOGE("Decode returned error");
115 free(inputBuffer);
116 return -1;
117 }
118
119 AMediaFormat *decoderFormat = decoder->getFormat();
120 AMediaFormat *format = extractor->getFormat();
121 if (inputBuffer) {
122 free(inputBuffer);
123 inputBuffer = nullptr;
124 }
125 const char *mime = nullptr;
126 AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
127 if (!mime) {
128 ALOGE("Error in AMediaFormat_getString");
129 return -1;
130 }
131 ifstream eleStream;
132 eleStream.open(outputFilePath, ifstream::binary | ifstream::ate);
133 if (!eleStream.is_open()) {
134 ALOGE("%s - File failed to open for reading!", outputFilePath);
135 env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
136 return -1;
137 }
138 const char *codecName = env->GetStringUTFChars(jCodecName, NULL);
139 const char *inputReference = env->GetStringUTFChars(jFileName, nullptr);
140 string sCodecName = string(codecName);
141 string sInputReference = string(inputReference);
142
143 bool asyncMode[2] = {true, false};
144 for (int i = 0; i < 2; i++) {
145 size_t eleSize = eleStream.tellg();
146 eleStream.seekg(0, ifstream::beg);
147
148 // Get encoder params
149 encParameter encParams;
150 if (!strncmp(mime, "video/", 6)) {
151 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width);
152 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height);
153 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &encParams.frameRate);
154 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &encParams.bitrate);
155 if (encParams.bitrate <= 0 || encParams.frameRate <= 0) {
156 encParams.frameRate = ENCODE_DEFAULT_FRAME_RATE;
157 if (!strcmp(mime, "video/3gpp") || !strcmp(mime, "video/mp4v-es")) {
158 encParams.bitrate = ENCODE_MIN_BIT_RATE /* 600 Kbps */;
159 } else {
160 encParams.bitrate = ENCODE_DEFAULT_BIT_RATE /* 8 Mbps */;
161 }
162 }
163 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile);
164 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level);
165 AMediaFormat_getInt32(decoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT,
166 &encParams.colorFormat);
167 } else {
168 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate);
169 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
170 &encParams.numChannels);
171 encParams.bitrate = ENCODE_DEFAULT_AUDIO_BIT_RATE;
172 }
173 Encoder *encoder = new Encoder();
174 encoder->setupEncoder();
175 status = encoder->encode(sCodecName, eleStream, eleSize, asyncMode[i], encParams,
176 (char *)mime);
177 if (status != AMEDIA_OK) {
178 ALOGE("Encoder returned error");
179 return -1;
180 }
181 ALOGV("Encoding complete with codec %s for asyncMode = %d", sCodecName.c_str(),
182 asyncMode[i]);
183 encoder->deInitCodec();
184 const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
185 encoder->dumpStatistics(sInputReference, extractor->getClipDuration(), sCodecName,
186 (asyncMode[i] ? "async" : "sync"), statsFile);
187 env->ReleaseStringUTFChars(jStatsFile, statsFile);
188 encoder->resetEncoder();
189 delete encoder;
190 encoder = nullptr;
191 }
192 eleStream.close();
193 if (outFp) {
194 fclose(outFp);
195 outFp = nullptr;
196 }
197 env->ReleaseStringUTFChars(jFileName, inputReference);
198 env->ReleaseStringUTFChars(jCodecName, codecName);
199 env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
200 if (format) {
201 AMediaFormat_delete(format);
202 format = nullptr;
203 }
204 if (decoderFormat) {
205 AMediaFormat_delete(decoderFormat);
206 decoderFormat = nullptr;
207 }
208 decoder->deInitCodec();
209 decoder->resetDecoder();
210 }
211 if (inputFp) {
212 fclose(inputFp);
213 inputFp = nullptr;
214 }
215 extractor->deInitExtractor();
216 delete decoder;
217 return 0;
218 }
219