1 /*
2  * Copyright (C) 2023 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 #pragma once
18 
19 #include "SyncEvent.h"
20 
21 #pragma push_macro("LOG_TAG")
22 #undef LOG_TAG
23 #define LOG_TAG "SynchronizedRecordState"
24 
25 namespace android::audioflinger {
26 
27 class SynchronizedRecordState {
28 public:
SynchronizedRecordState(uint32_t sampleRate)29     explicit SynchronizedRecordState(uint32_t sampleRate)
30         : mSampleRate(sampleRate)
31         {}
32 
clear()33     void clear() {
34         std::lock_guard lg(mLock);
35         clear_l();
36     }
37 
38     // Called by the RecordThread when recording is starting.
startRecording(const sp<SyncEvent> & event)39     void startRecording(const sp<SyncEvent>& event) {
40         std::lock_guard lg(mLock);
41         mSyncStartEvent = event;
42         // Sync event can be cancelled by the trigger session if the track is not in a
43         // compatible state in which case we start record immediately
44         if (mSyncStartEvent->isCancelled()) {
45             clear_l();
46         } else {
47             mFramesToDrop = -(ssize_t)
48                 ((AudioSystem::kSyncRecordStartTimeOutMs * mSampleRate) / 1000);
49         }
50     }
51 
52     // Invoked by SyncEvent callback.
53     void onPlaybackFinished(const sp<SyncEvent>& event, size_t framesToDrop = 1) {
54         std::lock_guard lg(mLock);
55         if (event == mSyncStartEvent) {
56             mFramesToDrop = framesToDrop;  // compute this
57             ALOGV("%s: framesToDrop:%zd", __func__, mFramesToDrop);
58         }
59     }
60 
61     // Returns the current FramesToDrop counter
62     //
63     //   if <0 waiting (drop the frames)
64     //   if >0 draining (drop the frames)
65     //    else if ==0 proceed to record.
updateRecordFrames(size_t frames)66     ssize_t updateRecordFrames(size_t frames) {
67         std::lock_guard lg(mLock);
68         if (mFramesToDrop > 0) {
69             // we've been triggered, we count down for start delay
70             ALOGV("%s: trigger countdown %zd by %zu frames", __func__, mFramesToDrop, frames);
71             mFramesToDrop -= (ssize_t)frames;
72             if (mFramesToDrop <= 0) clear_l();
73         } else if (mFramesToDrop < 0) {
74             // we're waiting to be triggered.
75             // ALOGD("%s: timeout countup %zd with %zu frames", __func__, mFramesToDrop, frames);
76             mFramesToDrop += (ssize_t)frames;
77             if (mFramesToDrop >= 0 || !mSyncStartEvent || mSyncStartEvent->isCancelled()) {
78                 ALOGW("Synced record %s, trigger session %d",
79                         (mFramesToDrop >= 0) ? "timed out" : "cancelled",
80                         (mSyncStartEvent) ? mSyncStartEvent->triggerSession()
81                                           : AUDIO_SESSION_NONE);
82                  clear_l();
83             }
84         }
85         return mFramesToDrop;
86     }
87 
88 private:
89     const uint32_t mSampleRate;
90 
91     std::mutex mLock;
92     // number of captured frames to drop after the start sync event has been received.
93     // when < 0, maximum frames to drop before starting capture even if sync event is
94     // not received
95     ssize_t mFramesToDrop GUARDED_BY(mLock) = 0;
96 
97     // sync event triggering actual audio capture. Frames read before this event will
98     // be dropped and therefore not read by the application.
99     sp<SyncEvent> mSyncStartEvent GUARDED_BY(mLock);
100 
clear_l()101     void clear_l() REQUIRES(mLock) {
102         if (mSyncStartEvent) {
103             mSyncStartEvent->cancel();
104             mSyncStartEvent.clear();
105         }
106         mFramesToDrop = 0;
107     }
108 };
109 
110 } // namespace android::audioflinger
111 
112 #pragma pop_macro("LOG_TAG")
113