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