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 //#define LOG_NDEBUG 0
17 #define LOG_TAG "NativeMediaCommon"
18 #include <log/log.h>
19
20 #include <cstdio>
21 #include <cstring>
22 #include <utility>
23
24 #include "NativeMediaCommon.h"
25
26 /* TODO(b/153592281)
27 * Note: constants used by the native media tests but not available in media ndk api
28 */
29 const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
30 const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
31 const char* AMEDIA_MIMETYPE_VIDEO_AV1 = "video/av01";
32 const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
33 const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
34 const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
35 const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
36
37 const char* AMEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
38 const char* AMEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
39 const char* AMEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
40 const char* AMEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac";
41 const char* AMEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
42 const char* AMEDIA_MIMETYPE_AUDIO_OPUS = "audio/opus";
43 const char* AMEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
44
45 /* TODO(b/153592281) */
46 const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
47 const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
48 const char* COMPATIBLE_AMEDIAFORMAT_KEY_MAX_B_FRAMES = "max-bframes";
49 const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
50
51 // NDK counterpart of RMS_ERROR_TOLERANCE of CodecDecoderTest class
52 const float kRmsErrorTolerance = 1.05f;
53
54 // NDK counterpart of Q_DEQ_TIMEOUT_US and RETRY_LIMIT of CodecTestBase class
55 const long kQDeQTimeOutUs = 5000; // block at most 5ms while looking for io buffers
56 const int kRetryLimit = 100; // max poll counter before test aborts and returns error
57
isCSDIdentical(AMediaFormat * refFormat,AMediaFormat * testFormat)58 bool isCSDIdentical(AMediaFormat* refFormat, AMediaFormat* testFormat) {
59 for (int i = 0;; i++) {
60 std::pair<void*, size_t> refCsd;
61 std::pair<void*, size_t> testCsd;
62 char name[16];
63 snprintf(name, sizeof(name), "csd-%d", i);
64 bool hasRefCSD = AMediaFormat_getBuffer(refFormat, name, &refCsd.first, &refCsd.second);
65 bool hasTestCSD = AMediaFormat_getBuffer(testFormat, name, &testCsd.first, &testCsd.second);
66 if (hasRefCSD != hasTestCSD) {
67 ALOGW("mismatch, ref fmt has CSD %d, test fmt has CSD %d", hasRefCSD, hasTestCSD);
68 return false;
69 }
70 if (hasRefCSD) {
71 if (refCsd.second != testCsd.second) {
72 ALOGW("ref/test %s buffer sizes are not identical %zu/%zu", name, refCsd.second,
73 testCsd.second);
74 return false;
75 }
76 if (memcmp(refCsd.first, testCsd.first, refCsd.second)) {
77 ALOGW("ref/test %s buffers are not identical", name);
78 return false;
79 }
80 } else break;
81 }
82 return true;
83 }
84
85 template <class T>
flattenField(uint8_t * buffer,int * pos,T value)86 void flattenField(uint8_t* buffer, int* pos, T value) {
87 uint8_t* ptr = (buffer + *pos);
88 for (int i = sizeof(T) - 1; i >= 0; i--) {
89 *ptr++ = (uint8_t)((value >> (i * 8)) & 0xff);
90 }
91 *pos += sizeof(T);
92 }
93
94 template void flattenField<int32_t>(uint8_t* buffer, int* pos, int32_t value);
95 template void flattenField<int64_t>(uint8_t* buffer, int* pos, int64_t value);
96
isFormatSimilar(AMediaFormat * refFormat,AMediaFormat * testFormat)97 bool isFormatSimilar(AMediaFormat* refFormat, AMediaFormat* testFormat) {
98 const char *refMediaType = nullptr, *testMediaType = nullptr;
99 int64_t refKeyDuration, testKeyDuration;
100 bool hasRefMediaType = AMediaFormat_getString(refFormat, AMEDIAFORMAT_KEY_MIME, &refMediaType);
101 if (!hasRefMediaType) return false;
102 bool hasTestMediaType =
103 AMediaFormat_getString(testFormat, AMEDIAFORMAT_KEY_MIME, &testMediaType);
104 if (!hasTestMediaType) return false;
105 if (strcmp(refMediaType, testMediaType) != 0) return false;
106 bool hasRefKeyDuration =
107 AMediaFormat_getInt64(refFormat, AMEDIAFORMAT_KEY_DURATION, &refKeyDuration);
108 if (!hasRefKeyDuration) return false;
109 bool hasTestKeyDuration =
110 AMediaFormat_getInt64(testFormat, AMEDIAFORMAT_KEY_DURATION, &testKeyDuration);
111 if (!hasTestKeyDuration) return false;
112 if (refKeyDuration != testKeyDuration) {
113 ALOGW("Duration mismatches ref / test = %lld / %lld", (long long)refKeyDuration,
114 (long long)testKeyDuration);
115 // TODO (b/163477410)(b/163478168)
116 // return false;
117 }
118 if (!isCSDIdentical(refFormat, testFormat)) return false;
119 if (!strncmp(refMediaType, "audio/", strlen("audio/"))) {
120 int32_t refSampleRate, testSampleRate, refNumChannels, testNumChannels;
121 bool hasRefSampleRate =
122 AMediaFormat_getInt32(refFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &refSampleRate);
123 if (!hasRefSampleRate) return false;
124 bool hasTestSampleRate =
125 AMediaFormat_getInt32(testFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &testSampleRate);
126 if (!hasTestSampleRate) return false;
127 if (refSampleRate != testSampleRate) return false;
128 bool hasRefNumChannels =
129 AMediaFormat_getInt32(refFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &refNumChannels);
130 if (!hasRefNumChannels) return false;
131 bool hasTestNumChannels =
132 AMediaFormat_getInt32(testFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &testNumChannels);
133 if (!hasTestNumChannels) return false;
134 if (refNumChannels != testNumChannels) return false;
135 } else if (!strncmp(refMediaType, "video/", strlen("video/"))) {
136 int32_t refWidth, testWidth, refHeight, testHeight;
137 bool hasRefWidth = AMediaFormat_getInt32(refFormat, AMEDIAFORMAT_KEY_WIDTH, &refWidth);
138 if (!hasRefWidth) return false;
139 bool hasTestWidth = AMediaFormat_getInt32(testFormat, AMEDIAFORMAT_KEY_WIDTH, &testWidth);
140 if (!hasTestWidth) return false;
141 if (refWidth != testWidth) return false;
142 bool hasRefHeight = AMediaFormat_getInt32(refFormat, AMEDIAFORMAT_KEY_HEIGHT, &refHeight);
143 if (!hasRefHeight) return false;
144 bool hasTestHeight =
145 AMediaFormat_getInt32(testFormat, AMEDIAFORMAT_KEY_HEIGHT, &testHeight);
146 if (!hasTestHeight) return false;
147 if (refHeight != testHeight) return false;
148 }
149 return true;
150 }
151
isMediaTypeOutputUnAffectedBySeek(const char * mediaType)152 bool isMediaTypeOutputUnAffectedBySeek(const char* mediaType) {
153 if (strcmp(mediaType, AMEDIA_MIMETYPE_AUDIO_FLAC) == 0) return true;
154 if (strcmp(mediaType, AMEDIA_MIMETYPE_AUDIO_RAW) == 0) return true;
155 if (strncmp(mediaType, "video/", strlen("video/")) == 0) return true;
156 return false;
157 }
158
deSerializeMediaFormat(const char * msg,const char * separator)159 AMediaFormat* deSerializeMediaFormat(const char* msg, const char* separator) {
160 // constants to be kept in sync with definitions at MediaFormat.java
161 static const int TYPE_INTEGER = 1;
162 static const int TYPE_FLOAT = 3;
163 static const int TYPE_STRING = 4;
164 std::string limiter{separator};
165 std::string fmtMsg{msg};
166 AMediaFormat* fmt = AMediaFormat_new();
167 if (fmt == nullptr) {
168 ALOGE("no format received");
169 return nullptr;
170 }
171 auto start = 0u;
172 auto end = fmtMsg.find(limiter);
173 std::string keyStr, valueTypeStr, valueStr;
174 for (; end != std::string::npos;) {
175 // key
176 keyStr = fmtMsg.substr(start, end - start);
177 start = end + limiter.length();
178 end = fmtMsg.find(limiter, start);
179 if (end == std::string::npos) {
180 ALOGE("incomplete media format received %s", msg);
181 AMediaFormat_delete(fmt);
182 return nullptr;
183 }
184 // value type
185 valueTypeStr = fmtMsg.substr(start, end - start);
186 start = end + limiter.length();
187 end = fmtMsg.find(limiter, start);
188 if (end == std::string::npos) {
189 ALOGE("incomplete media format received %s", msg);
190 AMediaFormat_delete(fmt);
191 return nullptr;
192 }
193
194 // value
195 valueStr = fmtMsg.substr(start, end - start);
196 start = end + limiter.length();
197 end = fmtMsg.find(limiter, start);
198
199 auto valueType = std::stoi(valueTypeStr);
200 if (valueType == TYPE_INTEGER) {
201 AMediaFormat_setInt32(fmt, keyStr.c_str(), std::stoi(valueStr));
202 } else if (valueType == TYPE_FLOAT) {
203 AMediaFormat_setFloat(fmt, keyStr.c_str(), std::stof(valueStr));
204 } else if (valueType == TYPE_STRING) {
205 AMediaFormat_setString(fmt, keyStr.c_str(), valueStr.c_str());
206 } else {
207 ALOGE("unrecognized type for key %s", keyStr.c_str());
208 AMediaFormat_delete(fmt);
209 return nullptr;
210 }
211 }
212 return fmt;
213 }
214