• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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