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 "ECOSession"
19 //#define DEBUG_ECO_SESSION
20 #include "eco/ECOSession.h"
21
22 #include <android/binder_ibinder.h>
23 #include <cutils/atomic.h>
24 #include <inttypes.h>
25 #include <pthread.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <utils/Log.h>
29 #include <utils/Timers.h>
30
31 #include <algorithm>
32 #include <climits>
33 #include <cstring>
34 #include <ctime>
35 #include <string>
36
37 #include "eco/ECODataKey.h"
38 #include "eco/ECODebug.h"
39
40 namespace android {
41 namespace media {
42 namespace eco {
43
44 using ::aidl::android::media::eco::ECODataKeyValueIterator;
45 using ndk::ScopedAStatus;
46
47 #define RETURN_IF_ERROR(expr) \
48 { \
49 status_t _errorCode = (expr); \
50 if (_errorCode != true) { \
51 return _errorCode; \
52 } \
53 }
54
55 // static
createECOSession(int32_t width,int32_t height,bool isCameraRecording)56 std::shared_ptr<ECOSession> ECOSession::createECOSession(int32_t width, int32_t height,
57 bool isCameraRecording) {
58 // Only support up to 1080P.
59 // TODO: Support the same resolution as in EAF.
60 if (width <= 0 || height <= 0 || width > 5120 || height > 5120 ||
61 width > 1920 * 1088 / height) {
62 ECOLOGE("Failed to create ECOSession with w: %d, h: %d, isCameraRecording: %d", width,
63 height, isCameraRecording);
64 return nullptr;
65 }
66 return ndk::SharedRefBase::make<ECOSession>(width, height, isCameraRecording);
67 }
68
ECOSession(int32_t width,int32_t height,bool isCameraRecording)69 ECOSession::ECOSession(int32_t width, int32_t height, bool isCameraRecording)
70 : BnECOSession(),
71 mStopThread(false),
72 mLastReportedQp(0),
73 mListener(nullptr),
74 mProvider(nullptr),
75 mWidth(width),
76 mHeight(height),
77 mIsCameraRecording(isCameraRecording) {
78 ECOLOGI("ECOSession created with w: %d, h: %d, isCameraRecording: %d", mWidth, mHeight,
79 mIsCameraRecording);
80 mThread = std::thread(startThread, this);
81
82 // Read the debug properies.
83 mLogStats = property_get_bool(kDebugLogStats, false);
84 mLogStatsEntries = mLogStats ? property_get_int32(kDebugLogStatsSize, 0) : 0;
85
86 mLogInfo = property_get_bool(kDebugLogStats, false);
87 mLogInfoEntries = mLogInfo ? property_get_int32(kDebugLogInfosSize, 0) : 0;
88
89 ECOLOGI("ECOSession debug settings: logStats: %s, entries: %d, logInfo: %s entries: %d",
90 mLogStats ? "true" : "false", mLogStatsEntries, mLogInfo ? "true" : "false",
91 mLogInfoEntries);
92 }
93
~ECOSession()94 ECOSession::~ECOSession() {
95 mStopThread = true;
96
97 mWorkerWaitCV.notify_all();
98 if (mThread.joinable()) {
99 ECOLOGD("ECOSession: join the thread");
100 mThread.join();
101 }
102 ECOLOGI("ECOSession destroyed with w: %d, h: %d, isCameraRecording: %d", mWidth, mHeight,
103 mIsCameraRecording);
104 }
105
106 // static
startThread(ECOSession * session)107 void ECOSession::startThread(ECOSession* session) {
108 session->run();
109 }
110
run()111 void ECOSession::run() {
112 ECOLOGD("ECOSession: starting main thread");
113
114 while (!mStopThread) {
115 std::unique_lock<std::mutex> runLock(mStatsQueueLock);
116
117 mWorkerWaitCV.wait(runLock, [this] {
118 return mStopThread == true || !mStatsQueue.empty() || mNewListenerAdded;
119 });
120
121 if (mStopThread) return;
122
123 std::scoped_lock<std::mutex> lock(mSessionLock);
124 if (mNewListenerAdded) {
125 // Check if there is any session info available.
126 ECOData sessionInfo = generateLatestSessionInfoEcoData();
127 if (!sessionInfo.isEmpty()) {
128 ScopedAStatus status = mListener->onNewInfo(sessionInfo);
129 if (!status.isOk()) {
130 ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
131 sessionInfo.debugString().c_str());
132 // Remove the listener. The lock has been acquired outside this function.
133 mListener = nullptr;
134 }
135 }
136 mNewListenerAdded = false;
137 }
138
139 if (!mStatsQueue.empty()) {
140 ECOData stats = mStatsQueue.front();
141 mStatsQueue.pop_front();
142 processStats(stats); // TODO: Handle the error from processStats
143 }
144 }
145
146 ECOLOGD("ECOSession: exiting main thread");
147 }
148
processStats(const ECOData & stats)149 bool ECOSession::processStats(const ECOData& stats) {
150 ECOLOGV("%s: receive stats: %s", __FUNCTION__, stats.debugString().c_str());
151
152 if (stats.getDataType() != ECOData::DATA_TYPE_STATS) {
153 ECOLOGE("Invalid stats. ECOData with type: %s", stats.getDataTypeString().c_str());
154 return false;
155 }
156
157 // Get the type of the stats.
158 std::string statsType;
159 if (stats.findString(KEY_STATS_TYPE, &statsType) != ECODataStatus::OK) {
160 ECOLOGE("Invalid stats ECOData without statsType");
161 return false;
162 }
163
164 if (statsType.compare(VALUE_STATS_TYPE_SESSION) == 0) {
165 processSessionStats(stats);
166 } else if (statsType.compare(VALUE_STATS_TYPE_FRAME) == 0) {
167 processFrameStats(stats);
168 } else {
169 ECOLOGE("processStats:: Failed to process stats as ECOData contains unknown stats type");
170 return false;
171 }
172
173 return true;
174 }
175
processSessionStats(const ECOData & stats)176 void ECOSession::processSessionStats(const ECOData& stats) {
177 ECOLOGV("processSessionStats");
178
179 ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
180 info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_SESSION);
181
182 ECODataKeyValueIterator iter(stats);
183 while (iter.hasNext()) {
184 ECOData::ECODataKeyValuePair entry = iter.next();
185 const std::string& key = entry.first;
186 const ECOData::ECODataValueType value = entry.second;
187 ECOLOGV("Processing key: %s", key.c_str());
188 if (!key.compare(KEY_STATS_TYPE)) {
189 // Skip the key KEY_STATS_TYPE as that has been parsed already.
190 continue;
191 } else if (!key.compare(ENCODER_TYPE)) {
192 mCodecType = std::get<int32_t>(value);
193 ECOLOGV("codec type is %d", mCodecType);
194 } else if (!key.compare(ENCODER_PROFILE)) {
195 mCodecProfile = std::get<int32_t>(value);
196 ECOLOGV("codec profile is %d", mCodecProfile);
197 } else if (!key.compare(ENCODER_LEVEL)) {
198 mCodecLevel = std::get<int32_t>(value);
199 ECOLOGV("codec level is %d", mCodecLevel);
200 } else if (!key.compare(ENCODER_TARGET_BITRATE_BPS)) {
201 mTargetBitrateBps = std::get<int32_t>(value);
202 ECOLOGV("codec target bitrate is %d", mTargetBitrateBps);
203 } else if (!key.compare(ENCODER_KFI_FRAMES)) {
204 mKeyFrameIntervalFrames = std::get<int32_t>(value);
205 ECOLOGV("codec kfi is %d", mKeyFrameIntervalFrames);
206 } else if (!key.compare(ENCODER_FRAMERATE_FPS)) {
207 mFramerateFps = std::get<float>(value);
208 ECOLOGV("codec framerate is %f", mFramerateFps);
209 } else if (!key.compare(ENCODER_INPUT_WIDTH)) {
210 int32_t width = std::get<int32_t>(value);
211 if (width != mWidth) {
212 ECOLOGW("Codec width: %d, expected: %d", width, mWidth);
213 }
214 ECOLOGV("codec input width is %d", width);
215 } else if (!key.compare(ENCODER_INPUT_HEIGHT)) {
216 int32_t height = std::get<int32_t>(value);
217 if (height != mHeight) {
218 ECOLOGW("Codec height: %d, expected: %d", height, mHeight);
219 }
220 ECOLOGV("codec input height is %d", height);
221 } else if (!key.compare(ENCODER_OUTPUT_WIDTH)) {
222 mOutputWidth = std::get<int32_t>(value);
223 if (mOutputWidth != mWidth) {
224 ECOLOGW("Codec output width: %d, expected: %d", mOutputWidth, mWidth);
225 }
226 ECOLOGV("codec output width is %d", mOutputWidth);
227 } else if (!key.compare(ENCODER_OUTPUT_HEIGHT)) {
228 mOutputHeight = std::get<int32_t>(value);
229 if (mOutputHeight != mHeight) {
230 ECOLOGW("Codec output height: %d, expected: %d", mOutputHeight, mHeight);
231 }
232 ECOLOGV("codec output height is %d", mOutputHeight);
233 } else {
234 ECOLOGW("Unknown session stats key %s from provider.", key.c_str());
235 continue;
236 }
237 info.set(key, value);
238 }
239
240 if (mListener != nullptr) {
241 ScopedAStatus status = mListener->onNewInfo(info);
242 if (!status.isOk()) {
243 ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
244 info.debugString().c_str());
245 // Remove the listener. The lock has been acquired outside this function.
246 mListener = nullptr;
247 }
248 }
249 }
250
generateLatestSessionInfoEcoData()251 ECOData ECOSession::generateLatestSessionInfoEcoData() {
252 bool hasInfo = false;
253
254 ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
255
256 if (mOutputWidth != -1) {
257 info.setInt32(ENCODER_OUTPUT_WIDTH, mOutputWidth);
258 hasInfo = true;
259 }
260
261 if (mOutputHeight != -1) {
262 info.setInt32(ENCODER_OUTPUT_HEIGHT, mOutputHeight);
263 hasInfo = true;
264 }
265
266 if (mCodecType != -1) {
267 info.setInt32(ENCODER_TYPE, mCodecType);
268 hasInfo = true;
269 }
270
271 if (mCodecProfile != -1) {
272 info.setInt32(ENCODER_PROFILE, mCodecProfile);
273 hasInfo = true;
274 }
275
276 if (mCodecLevel != -1) {
277 info.setInt32(ENCODER_LEVEL, mCodecLevel);
278 hasInfo = true;
279 }
280
281 if (mTargetBitrateBps != -1) {
282 info.setInt32(ENCODER_TARGET_BITRATE_BPS, mTargetBitrateBps);
283 hasInfo = true;
284 }
285
286 if (mKeyFrameIntervalFrames != -1) {
287 info.setInt32(ENCODER_KFI_FRAMES, mKeyFrameIntervalFrames);
288 hasInfo = true;
289 }
290
291 if (mFramerateFps > 0) {
292 info.setFloat(ENCODER_FRAMERATE_FPS, mFramerateFps);
293 hasInfo = true;
294 }
295
296 if (hasInfo) {
297 info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_SESSION);
298 }
299 return info;
300 }
301
processFrameStats(const ECOData & stats)302 void ECOSession::processFrameStats(const ECOData& stats) {
303 ECOLOGD("processFrameStats");
304
305 bool needToNotifyListener = false;
306 ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
307 info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_FRAME);
308
309 ECODataKeyValueIterator iter(stats);
310 while (iter.hasNext()) {
311 ECOData::ECODataKeyValuePair entry = iter.next();
312 const std::string& key = entry.first;
313 const ECOData::ECODataValueType value = entry.second;
314 ECOLOGD("Processing %s key", key.c_str());
315
316 if (!key.compare(KEY_STATS_TYPE)) {
317 // Skip the key KEY_STATS_TYPE as that has been parsed already.
318 continue;
319 } else if (!key.compare(FRAME_NUM) || !key.compare(FRAME_PTS_US) ||
320 !key.compare(FRAME_TYPE) || !key.compare(FRAME_SIZE_BYTES) ||
321 !key.compare(ENCODER_ACTUAL_BITRATE_BPS) ||
322 !key.compare(ENCODER_FRAMERATE_FPS)) {
323 // Only process the keys that are supported by ECOService 1.0.
324 info.set(key, value);
325 } else if (!key.compare(FRAME_AVG_QP)) {
326 // Check the qp to see if need to notify the listener.
327 const int32_t currAverageQp = std::get<int32_t>(value);
328
329 // Check if the delta between current QP and last reported QP is larger than the
330 // threshold specified by the listener.
331 const bool largeQPChangeDetected =
332 abs(currAverageQp - mLastReportedQp) > mListenerQpCondition.mQpChangeThreshold;
333
334 // Check if the qp is going from below threshold to beyond threshold.
335 const bool exceedQpBlockinessThreshold =
336 (mLastReportedQp <= mListenerQpCondition.mQpBlocknessThreshold &&
337 currAverageQp > mListenerQpCondition.mQpBlocknessThreshold);
338
339 // Check if the qp is going from beyond threshold to below threshold.
340 const bool fallBelowQpBlockinessThreshold =
341 (mLastReportedQp > mListenerQpCondition.mQpBlocknessThreshold &&
342 currAverageQp <= mListenerQpCondition.mQpBlocknessThreshold);
343
344 // Notify the listener if any of the above three conditions met.
345 if (largeQPChangeDetected || exceedQpBlockinessThreshold ||
346 fallBelowQpBlockinessThreshold) {
347 mLastReportedQp = currAverageQp;
348 needToNotifyListener = true;
349 }
350
351 info.set(key, value);
352 } else {
353 ECOLOGW("Unknown frame stats key %s from provider.", key.c_str());
354 }
355 }
356
357 if (needToNotifyListener && mListener != nullptr) {
358 ScopedAStatus status = mListener->onNewInfo(info);
359 if (!status.isOk()) {
360 ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
361 info.debugString().c_str());
362 // Remove the listener. The lock has been acquired outside this function.
363 mListener = nullptr;
364 }
365 }
366 }
367
getIsCameraRecording(bool * _aidl_return)368 ScopedAStatus ECOSession::getIsCameraRecording(bool* _aidl_return) {
369 std::scoped_lock<std::mutex> lock(mSessionLock);
370 *_aidl_return = mIsCameraRecording;
371 return ndk::ScopedAStatus::ok();
372 }
373
addStatsProvider(const std::shared_ptr<::android::media::eco::IECOServiceStatsProvider> & provider,const::android::media::eco::ECOData & config,bool * status)374 ScopedAStatus ECOSession::addStatsProvider(
375 const std::shared_ptr<::android::media::eco::IECOServiceStatsProvider>& provider,
376 const ::android::media::eco::ECOData& config, bool* status) {
377 std::string name;
378 ScopedAStatus result = provider->getName(&name);
379 if (!result.isOk()) {
380 // This binder transaction failure may due to permission issue.
381 *status = false;
382 ALOGE("Failed to get provider name");
383 return STATUS_ERROR(ERROR_PERMISSION_DENIED, "Failed to get provider name");
384 }
385
386 ECOLOGV("Try to add stats provider name: %s uid: %d pid %d", name.c_str(),
387 AIBinder_getCallingUid(), AIBinder_getCallingPid());
388
389 if (provider == nullptr) {
390 ECOLOGE("%s: provider must not be null", __FUNCTION__);
391 *status = false;
392 return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null provider given to addStatsProvider");
393 }
394
395 std::scoped_lock<std::mutex> lock(mSessionLock);
396
397 if (mProvider != nullptr) {
398 std::string name;
399 mProvider->getName(&name);
400 std::string errorMsg =
401 "ECOService 1.0 only supports one stats provider, current provider: " + name;
402 ECOLOGE("%s", errorMsg.c_str());
403 *status = false;
404 return STATUS_ERROR(ERROR_ALREADY_EXISTS, errorMsg.c_str());
405 }
406
407 // TODO: Handle the provider config.
408 if (config.getDataType() != ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG) {
409 ECOLOGE("Provider config is invalid");
410 *status = false;
411 return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Provider config is invalid");
412 }
413
414 mProvider = provider;
415 mProviderName = name;
416 *status = true;
417 return ndk::ScopedAStatus::ok();
418 }
419
removeStatsProvider(const std::shared_ptr<::android::media::eco::IECOServiceStatsProvider> & provider,bool * status)420 ScopedAStatus ECOSession::removeStatsProvider(
421 const std::shared_ptr<::android::media::eco::IECOServiceStatsProvider>& provider,
422 bool* status) {
423 std::scoped_lock<std::mutex> lock(mSessionLock);
424 // Check if the provider is the same as current provider for the session.
425 if (provider->asBinder() != mProvider->asBinder()) {
426 *status = false;
427 ECOLOGE("Failed to remove provider");
428 return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Provider does not match");
429 }
430
431 mProvider = nullptr;
432 *status = true;
433 return ndk::ScopedAStatus::ok();
434 }
435
addInfoListener(const std::shared_ptr<::android::media::eco::IECOServiceInfoListener> & listener,const::android::media::eco::ECOData & config,bool * status)436 ScopedAStatus ECOSession::addInfoListener(
437 const std::shared_ptr<::android::media::eco::IECOServiceInfoListener>& listener,
438 const ::android::media::eco::ECOData& config, bool* status) {
439 ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
440 std::scoped_lock<std::mutex> lock(mSessionLock);
441
442 std::string name;
443 ScopedAStatus result = listener->getName(&name);
444 if (!result.isOk()) {
445 // This binder transaction failure may due to permission issue.
446 *status = false;
447 ALOGE("Failed to get listener name");
448 return STATUS_ERROR(ERROR_PERMISSION_DENIED, "Failed to get listener name");
449 }
450
451 if (mListener != nullptr) {
452 ECOLOGE("ECOService 1.0 only supports one listener");
453 *status = false;
454 return STATUS_ERROR(ERROR_ALREADY_EXISTS, "ECOService 1.0 only supports one listener");
455 }
456
457 if (listener == nullptr) {
458 ECOLOGE("%s: listener must not be null", __FUNCTION__);
459 *status = false;
460 return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null listener given to addInfoListener");
461 }
462
463 if (config.getDataType() != ECOData::DATA_TYPE_INFO_LISTENER_CONFIG) {
464 *status = false;
465 ECOLOGE("%s: listener config is invalid", __FUNCTION__);
466 return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is invalid");
467 }
468
469 if (config.isEmpty()) {
470 *status = false;
471 ECOLOGE("Listener must provide listening criterion");
472 return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is empty");
473 }
474
475 // For ECOService 1.0, listener must specify the two threshold in order to receive info.
476 if (config.findInt32(KEY_LISTENER_QP_BLOCKINESS_THRESHOLD,
477 &mListenerQpCondition.mQpBlocknessThreshold) != ECODataStatus::OK ||
478 config.findInt32(KEY_LISTENER_QP_CHANGE_THRESHOLD,
479 &mListenerQpCondition.mQpChangeThreshold) != ECODataStatus::OK ||
480 mListenerQpCondition.mQpBlocknessThreshold < ENCODER_MIN_QP ||
481 mListenerQpCondition.mQpBlocknessThreshold > ENCODER_MAX_QP) {
482 *status = false;
483 ECOLOGE("%s: listener config is invalid", __FUNCTION__);
484 return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is not valid");
485 }
486
487 ECOLOGD("Info listener name: %s uid: %d pid %d", name.c_str(),
488 AIBinder_getCallingUid(), AIBinder_getCallingPid());
489
490 mListener = listener;
491 mListenerName = name;
492 mNewListenerAdded = true;
493 mWorkerWaitCV.notify_all();
494
495 *status = true;
496 return ndk::ScopedAStatus::ok();
497 }
498
removeInfoListener(const std::shared_ptr<::android::media::eco::IECOServiceInfoListener> & listener,bool * _aidl_return)499 ScopedAStatus ECOSession::removeInfoListener(
500 const std::shared_ptr<::android::media::eco::IECOServiceInfoListener>& listener,
501 bool* _aidl_return) {
502 std::scoped_lock<std::mutex> lock(mSessionLock);
503 // Check if the listener is the same as current listener for the session.
504 if (listener->asBinder() != mListener->asBinder()) {
505 *_aidl_return = false;
506 ECOLOGE("Failed to remove listener");
507 return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Listener does not match");
508 }
509
510 mListener = nullptr;
511 mNewListenerAdded = false;
512 *_aidl_return = true;
513 return ndk::ScopedAStatus::ok();
514 }
515
pushNewStats(const::android::media::eco::ECOData & stats,bool * _aidl_return)516 ScopedAStatus ECOSession::pushNewStats(const ::android::media::eco::ECOData& stats,
517 bool* _aidl_return) {
518 ECOLOGV("ECOSession get new stats type: %s", stats.getDataTypeString().c_str());
519 std::unique_lock<std::mutex> lock(mStatsQueueLock);
520 mStatsQueue.push_back(stats);
521 mWorkerWaitCV.notify_all();
522 *_aidl_return = true;
523 return ndk::ScopedAStatus::ok();
524 }
525
getWidth(int32_t * _aidl_return)526 ScopedAStatus ECOSession::getWidth(int32_t* _aidl_return) {
527 std::scoped_lock<std::mutex> lock(mSessionLock);
528 *_aidl_return = mWidth;
529 return ndk::ScopedAStatus::ok();
530 }
531
getHeight(int32_t * _aidl_return)532 ScopedAStatus ECOSession::getHeight(int32_t* _aidl_return) {
533 std::scoped_lock<std::mutex> lock(mSessionLock);
534 *_aidl_return = mHeight;
535 return ndk::ScopedAStatus::ok();
536 }
537
getNumOfListeners(int32_t * _aidl_return)538 ScopedAStatus ECOSession::getNumOfListeners(int32_t* _aidl_return) {
539 std::scoped_lock<std::mutex> lock(mSessionLock);
540 *_aidl_return = (mListener == nullptr ? 0 : 1);
541 return ndk::ScopedAStatus::ok();
542 }
543
getNumOfProviders(int32_t * _aidl_return)544 ScopedAStatus ECOSession::getNumOfProviders(int32_t* _aidl_return) {
545 std::scoped_lock<std::mutex> lock(mSessionLock);
546 *_aidl_return = (mProvider == nullptr ? 0 : 1);
547 return ndk::ScopedAStatus::ok();
548 }
549
dump(int fd,const std::vector<std::string> &)550 status_t ECOSession::dump(int fd, const std::vector<std::string>& /*args*/) {
551 std::scoped_lock<std::mutex> lock(mSessionLock);
552 dprintf(fd, "\n== Session Info: ==\n\n");
553 dprintf(fd,
554 "Width: %d Height: %d isCameraRecording: %d, target-bitrate: %d bps codetype: %d "
555 "profile: %d level: %d\n",
556 mWidth, mHeight, mIsCameraRecording, mTargetBitrateBps, mCodecType, mCodecProfile,
557 mCodecLevel);
558 if (mProvider != nullptr) {
559 dprintf(fd, "Provider: %s \n", mProviderName.c_str());
560 }
561 if (mListener != nullptr) {
562 dprintf(fd, "Listener: %s \n", mListenerName.c_str());
563 }
564 dprintf(fd, "\n===================\n\n");
565
566 return NO_ERROR;
567 }
568
logStats(const ECOData & data)569 void ECOSession::logStats(const ECOData& data) {
570 // Check if mLogStats is true;
571 if (!mLogStats || mLogStatsEntries == 0) return;
572
573 // Check if we need to remove the old entry.
574 if (mStatsDebugBuffer.size() >= mLogStatsEntries) {
575 mStatsDebugBuffer.pop_front();
576 }
577
578 mStatsDebugBuffer.push_back(data);
579 }
580
logInfos(const ECOData & data)581 void ECOSession::logInfos(const ECOData& data) {
582 // Check if mLogInfo is true;
583 if (!mLogInfo || mLogInfoEntries == 0) return;
584
585 // Check if we need to remove the old entry.
586 if (mInfosDebugBuffer.size() >= mLogInfoEntries) {
587 mInfosDebugBuffer.pop_front();
588 }
589
590 mInfosDebugBuffer.push_back(data);
591 }
592
593 } // namespace eco
594 } // namespace media
595 } // namespace android
596