1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "Recorder.h"
17 #include <bits/alltypes.h>
18 #include "av_codec_sample_log.h"
19 #include "dfx/error/av_codec_sample_error.h"
20
21 #undef LOG_TAG
22 #define LOG_TAG "recorder"
23
24 namespace {
25 using namespace std::chrono_literals;
26 constexpr int64_t MICROSECOND = 1000000;
27 constexpr int32_t INPUT_FRAME_BYTES = 2 * 1024;
28 }
29
~Recorder()30 Recorder::~Recorder() { StartRelease(); }
31
Init(SampleInfo & sampleInfo)32 int32_t Recorder::Init(SampleInfo &sampleInfo)
33 {
34 std::lock_guard<std::mutex> lock(mutex_);
35 CHECK_AND_RETURN_RET_LOG(!isStarted_, AVCODEC_SAMPLE_ERR_ERROR, "Already started.");
36 CHECK_AND_RETURN_RET_LOG(videoEncoder_ == nullptr && muxer_ == nullptr,
37 AVCODEC_SAMPLE_ERR_ERROR, "Already started.");
38
39 sampleInfo_ = sampleInfo;
40 // Audio Capturer Init
41 audioEncoder_ = std::make_unique<AudioEncoder>();
42 audioCapturer_ = std::make_unique<AudioCapturer>();
43
44 videoEncoder_ = std::make_unique<VideoEncoder>();
45 muxer_ = std::make_unique<Muxer>();
46
47 int32_t ret = videoEncoder_->Create(sampleInfo_.videoCodecMime);
48 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create video encoder failed");
49 ret = muxer_->Create(sampleInfo_.outputFd);
50 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create muxer with fd(%{public}d) failed",
51 sampleInfo_.outputFd);
52
53 ret = muxer_->Config(sampleInfo_);
54 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Recorder muxer config failed");
55
56 encContext_ = new CodecUserData;
57 encContext_->isEncFirstFrame = true;
58 ret = CreateAudioEncoder();
59 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create audio encoder failed");
60
61 // Init AudioCapturer
62 audioCapturer_->AudioCapturerInit(sampleInfo_, audioEncContext_);
63
64 ret = CreateVideoEncoder();
65 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create video encoder failed");
66
67 sampleInfo.window = sampleInfo_.window;
68
69 releaseThread_ = nullptr;
70 AVCODEC_SAMPLE_LOGI("Succeed");
71 return AVCODEC_SAMPLE_ERR_OK;
72 }
73
Start()74 int32_t Recorder::Start()
75 {
76 std::lock_guard<std::mutex> lock(mutex_);
77 CHECK_AND_RETURN_RET_LOG(!isStarted_, AVCODEC_SAMPLE_ERR_ERROR, "Already started.");
78 CHECK_AND_RETURN_RET_LOG(encContext_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR,
79 "Already started.");
80 CHECK_AND_RETURN_RET_LOG(videoEncoder_ != nullptr && muxer_ != nullptr,
81 AVCODEC_SAMPLE_ERR_ERROR, "Already started.");
82
83 int32_t ret = muxer_->Start();
84 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Muxer start failed");
85 ret = videoEncoder_->Start();
86 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Encoder start failed");
87
88 isStarted_ = true;
89 encOutputThread_ = std::make_unique<std::thread>(&Recorder::EncOutputThread, this);
90 if (encOutputThread_ == nullptr) {
91 AVCODEC_SAMPLE_LOGE("Create thread failed");
92 StartRelease();
93 return AVCODEC_SAMPLE_ERR_ERROR;
94 }
95
96 if (audioEncContext_) {
97 // Start AudioCapturer
98 audioCapturer_->AudioCapturerStart();
99
100 ret = audioEncoder_->Start();
101 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Audio Encoder start failed");
102 isStarted_ = true;
103 audioEncInputThread_ = std::make_unique<std::thread>(&Recorder::AudioEncInputThread, this);
104 audioEncOutputThread_ = std::make_unique<std::thread>(&Recorder::AudioEncOutputThread, this);
105 if (audioEncInputThread_ == nullptr || audioEncOutputThread_ == nullptr) {
106 AVCODEC_SAMPLE_LOGE("Create thread failed");
107 StartRelease();
108 return AVCODEC_SAMPLE_ERR_ERROR;
109 }
110
111 // 清空播放缓存队列
112 if (audioEncContext_ != nullptr) {
113 audioEncContext_->ClearCache();
114 }
115 }
116
117 AVCODEC_SAMPLE_LOGI("Succeed");
118 return AVCODEC_SAMPLE_ERR_OK;
119 }
120
EncOutputThread()121 void Recorder::EncOutputThread()
122 {
123 while (true) {
124 CHECK_AND_BREAK_LOG(isStarted_, "Work done, thread out");
125 std::unique_lock<std::mutex> lock(encContext_->outputMutex);
126 bool condRet = encContext_->outputCond.wait_for(
127 lock, 5s, [this]() { return !isStarted_ || !encContext_->outputBufferInfoQueue.empty(); });
128 CHECK_AND_BREAK_LOG(isStarted_, "Work done, thread out");
129 CHECK_AND_CONTINUE_LOG(!encContext_->outputBufferInfoQueue.empty(),
130 "Buffer queue is empty, continue, cond ret: %{public}d", condRet);
131
132 CodecBufferInfo bufferInfo = encContext_->outputBufferInfoQueue.front();
133 encContext_->outputBufferInfoQueue.pop();
134 CHECK_AND_BREAK_LOG(!(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS), "Catch EOS, thread out");
135 lock.unlock();
136 if ((bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_SYNC_FRAME) ||
137 (bufferInfo.attr.flags == AVCODEC_BUFFER_FLAGS_NONE)) {
138 encContext_->outputFrameCount++;
139 bufferInfo.attr.pts = encContext_->outputFrameCount * MICROSECOND / sampleInfo_.frameRate;
140 } else {
141 bufferInfo.attr.pts = 0;
142 }
143
144 AVCODEC_SAMPLE_LOGW("Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}" PRId64,
145 encContext_->outputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags,
146 bufferInfo.attr.pts);
147
148 muxer_->WriteSample(muxer_->GetVideoTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer),
149 bufferInfo.attr);
150 int32_t ret = videoEncoder_->FreeOutputBuffer(bufferInfo.bufferIndex);
151 CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Encoder output thread out");
152 }
153 AVCODEC_SAMPLE_LOGI("Exit, frame count: %{public}u", encContext_->outputFrameCount);
154 StartRelease();
155 }
156
StartRelease()157 void Recorder::StartRelease()
158 {
159 if (releaseThread_ == nullptr) {
160 AVCODEC_SAMPLE_LOGI("Start release CodecTest");
161 releaseThread_ = std::make_unique<std::thread>(&Recorder::Release, this);
162 }
163 }
164
Release()165 void Recorder::Release()
166 {
167 std::lock_guard<std::mutex> lock(mutex_);
168 isStarted_ = false;
169 if (encOutputThread_ && encOutputThread_->joinable()) {
170 encOutputThread_->join();
171 encOutputThread_.reset();
172 }
173 if (audioEncInputThread_ && audioEncInputThread_->joinable()) {
174 audioEncContext_->inputCond.notify_all();
175 audioEncInputThread_->join();
176 audioEncInputThread_.reset();
177 }
178 if (audioEncOutputThread_ && audioEncOutputThread_->joinable()) {
179 audioEncContext_->outputCond.notify_all();
180 audioEncOutputThread_->join();
181 audioEncOutputThread_.reset();
182 }
183 if (muxer_ != nullptr) {
184 muxer_->Release();
185 muxer_.reset();
186 }
187 if (videoEncoder_ != nullptr) {
188 videoEncoder_->Stop();
189 if (sampleInfo_.window != nullptr) {
190 OH_NativeWindow_DestroyNativeWindow(sampleInfo_.window);
191 sampleInfo_.window = nullptr;
192 }
193 videoEncoder_->Release();
194 videoEncoder_.reset();
195 }
196 if (audioEncoder_ != nullptr) {
197 audioEncoder_->Stop();
198 audioEncoder_->Release();
199 audioEncoder_.reset();
200 }
201 if (audioCapturer_ != nullptr) {
202 audioCapturer_->AudioCapturerRelease();
203 audioCapturer_.reset();
204 }
205 if (audioEncContext_ != nullptr) {
206 delete audioEncContext_;
207 audioEncContext_ = nullptr;
208 }
209 if (encContext_ != nullptr) {
210 delete encContext_;
211 encContext_ = nullptr;
212 }
213 doneCond_.notify_all();
214 AVCODEC_SAMPLE_LOGI("Succeed");
215 }
216
WaitForDone()217 int32_t Recorder::WaitForDone()
218 {
219 AVCODEC_SAMPLE_LOGI("Wait called");
220 std::unique_lock<std::mutex> lock(mutex_);
221 doneCond_.wait(lock);
222 if (releaseThread_ && releaseThread_->joinable()) {
223 releaseThread_->join();
224 releaseThread_.reset();
225 }
226 AVCODEC_SAMPLE_LOGI("Done");
227 return AVCODEC_SAMPLE_ERR_OK;
228 }
229
Stop()230 int32_t Recorder::Stop()
231 {
232 if (isStarted_) {
233 int32_t ret = videoEncoder_->NotifyEndOfStream();
234 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Encoder notifyEndOfStream failed");
235 } else {
236 StartRelease();
237 }
238 return WaitForDone();
239 }
240
CreateVideoEncoder()241 int32_t Recorder::CreateVideoEncoder()
242 {
243 int32_t ret = videoEncoder_->Create(sampleInfo_.videoCodecMime);
244 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create video encoder failed");
245
246 encContext_ = new CodecUserData;
247 ret = videoEncoder_->Config(sampleInfo_, encContext_);
248 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Encoder config failed");
249
250 return AVCODEC_SAMPLE_ERR_OK;
251 }
252
CreateAudioEncoder()253 int32_t Recorder::CreateAudioEncoder()
254 {
255 int32_t ret = audioEncoder_->Create(sampleInfo_.audioCodecMime);
256 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create audio encoder(%{public}s) failed",
257 sampleInfo_.audioCodecMime.c_str());
258 AVCODEC_SAMPLE_LOGI("Create audio encoder(%{public}s)", sampleInfo_.audioCodecMime.c_str());
259
260 audioEncContext_ = new CodecUserData;
261 ret = audioEncoder_->Config(sampleInfo_, audioEncContext_);
262 CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Encoder config failed");
263
264 return AVCODEC_SAMPLE_ERR_OK;
265 }
266
AudioEncInputThread()267 void Recorder::AudioEncInputThread()
268 {
269 while (true) {
270 CHECK_AND_BREAK_LOG(isStarted_, "Encoder input thread out");
271 std::unique_lock<std::mutex> lock(audioEncContext_->inputMutex);
272 bool condRet = audioEncContext_->inputCond.wait_for(lock, 5s, [this]() {
273 return !isStarted_ || (!audioEncContext_->inputBufferInfoQueue.empty() &&
274 (audioEncContext_->remainlen >= sampleInfo_.audioMaxInputSize));
275 });
276
277 CHECK_AND_CONTINUE_LOG(!audioEncContext_->inputBufferInfoQueue.empty(),
278 "Audio Buffer queue is empty, continue, cond ret: %{public}d", condRet);
279
280 if (isStarted_ && audioEncContext_->remainlen < sampleInfo_.audioMaxInputSize) {
281 continue;
282 }
283
284 CodecBufferInfo bufferInfo = audioEncContext_->inputBufferInfoQueue.front();
285 audioEncContext_->inputBufferInfoQueue.pop();
286 audioEncContext_->inputFrameCount++;
287
288 uint8_t *inputBufferAddr = OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer));
289 audioEncContext_->ReadCache(inputBufferAddr, sampleInfo_.audioMaxInputSize);
290 lock.unlock();
291
292 bufferInfo.attr.size = sampleInfo_.audioMaxInputSize;
293 if (isFirstFrame_) {
294 bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
295 isFirstFrame_ = false;
296 } else {
297 bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_NONE;
298 }
299 int32_t ret = audioEncoder_->PushInputData(bufferInfo);
300 CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Push data failed, thread out");
301 }
302 }
303
AudioEncOutputThread()304 void Recorder::AudioEncOutputThread()
305 {
306 while (true) {
307 CHECK_AND_BREAK_LOG(isStarted_, "Work done, thread out");
308 std::unique_lock<std::mutex> lock(audioEncContext_->outputMutex);
309 bool condRet = audioEncContext_->outputCond.wait_for(
310 lock, 5s, [this]() { return !isStarted_ || !audioEncContext_->outputBufferInfoQueue.empty(); });
311 CHECK_AND_BREAK_LOG(isStarted_, "Work done, thread out");
312 CHECK_AND_CONTINUE_LOG(!audioEncContext_->outputBufferInfoQueue.empty(),
313 "Buffer queue is empty, continue, cond ret: %{public}d", condRet);
314
315 CodecBufferInfo bufferInfo = audioEncContext_->outputBufferInfoQueue.front();
316 audioEncContext_->outputBufferInfoQueue.pop();
317 audioEncContext_->outputFrameCount++;
318 AVCODEC_SAMPLE_LOGW(
319 "Audio Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}" PRId64,
320 audioEncContext_->outputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags, bufferInfo.attr.pts);
321 muxer_->WriteSample(muxer_->GetAudioTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer),
322 bufferInfo.attr);
323 int32_t ret = audioEncoder_->FreeOutputData(bufferInfo.bufferIndex);
324 CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Encoder output thread out");
325 }
326 AVCODEC_SAMPLE_LOGI("Exit, frame count: %{public}u", audioEncContext_->inputFrameCount);
327 StartRelease();
328 }