1 /*
2  * Copyright (C) 2023 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 "video_encoder_sample.h"
17 #include <unistd.h>
18 #include <memory>
19 #include <sys/mman.h>
20 #include <cmath>
21 #include "external_window.h"
22 #include "native_buffer_inner.h"
23 #include "av_codec_sample_log.h"
24 #include "av_codec_sample_error.h"
25 #include "avcodec_trace.h"
26 #include "sample_utils.h"
27 
28 namespace {
29 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_TEST, "VideoEncoderSample"};
30 }
31 
32 namespace OHOS {
33 namespace MediaAVCodec {
34 namespace Sample {
Init()35 int32_t VideoEncoderSample::Init()
36 {
37     return AVCODEC_SAMPLE_ERR_OK;
38 }
39 
Prepare()40 int32_t VideoEncoderSample::Prepare()
41 {
42     CHECK_AND_RETURN_RET_LOG(context_ && context_->sampleInfo, AVCODEC_SAMPLE_ERR_ERROR, "Context is nullptr");
43     auto &info = *context_->sampleInfo;
44     if (static_cast<uint8_t>(info.codecRunMode) & 0b01) {  // 0b01: Buffer mode mask
45         auto format = context_->videoCodec->GetFormat();
46         OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_STRIDE, &info.videoStrideWidth);
47         OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_SLICE_HEIGHT, &info.videoSliceHeight);
48 
49         inputThread_ = std::make_unique<std::thread>(&VideoEncoderSample::BufferInputThread, this);
50     } else {
51         int32_t strideAlignment = 0;
52         (void)OH_NativeWindow_NativeWindowHandleOpt(info.window.get(), GET_STRIDE, &strideAlignment);
53         info.videoStrideWidth = strideAlignment != 0 ?
54             (strideAlignment * std::ceil(static_cast<float>(info.videoWidth) / strideAlignment)) :
55             info.videoWidth;
56         info.videoSliceHeight = info.videoHeight;
57 
58         inputThread_ = std::make_unique<std::thread>(&VideoEncoderSample::SurfaceInputThread, this);
59     }
60     AVCODEC_LOGI("Resolution: %{public}d*%{public}d => %{public}d*%{public}d",
61         info.videoWidth, info.videoHeight,
62         info.videoStrideWidth, info.videoSliceHeight);
63 
64     outputThread_ = std::make_unique<std::thread>(&VideoEncoderSample::OutputThread, this);
65     if (inputThread_ == nullptr || outputThread_ == nullptr) {
66         AVCODEC_LOGE("Create thread failed");
67         return AVCODEC_SAMPLE_ERR_ERROR;
68     }
69     return AVCODEC_SAMPLE_ERR_OK;
70 }
71 
BufferInputThread()72 void VideoEncoderSample::BufferInputThread()
73 {
74     OHOS::MediaAVCodec::AVCodecTrace::TraceBegin("SampleWorkTime", FAKE_POINTER(this));
75     auto &info = *context_->sampleInfo;
76     while (true) {
77         auto bufferInfoOpt = context_->inputBufferQueue.DequeueBuffer();
78         CHECK_AND_CONTINUE_LOG(bufferInfoOpt != std::nullopt, "Buffer queue is empty, try dequeue again");
79         auto &bufferInfo = bufferInfoOpt.value();
80 
81         int32_t ret = dataProducer_->ReadSample(bufferInfo);
82         CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Read frame failed, thread out");
83         AVCODEC_LOGV("In buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}" PRId64,
84             context_->inputBufferQueue.GetFrameCount(),
85             bufferInfo.attr.size, bufferInfo.attr.flags, bufferInfo.attr.pts);
86 
87         ThreadSleep(info.threadSleepMode == THREAD_SLEEP_MODE_INPUT_SLEEP, info.frameInterval);
88 
89         ret = context_->videoCodec->PushInput(bufferInfo);
90         CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Push data failed, thread out");
91         CHECK_AND_BREAK_LOG(!(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS), "Push EOS frame, thread out");
92     }
93     AVCODEC_LOGI("Exit, frame count: %{public}u", context_->inputBufferQueue.GetFrameCount());
94     PushEosFrame();
95 }
96 
SurfaceInputThread()97 void VideoEncoderSample::SurfaceInputThread()
98 {
99     OHNativeWindowBuffer *buffer = nullptr;
100     OHOS::MediaAVCodec::AVCodecTrace::TraceBegin("SampleWorkTime", FAKE_POINTER(this));
101     auto &info = *context_->sampleInfo;
102     while (true) {
103         uint32_t frameCount = context_->inputBufferQueue.IncFrameCount();
104         uint64_t pts = static_cast<uint64_t>(frameCount) *
105             ((info.frameInterval == 0) ? 1 : info.frameInterval) * 1000; // 1000: 1ms to us
106         (void)OH_NativeWindow_NativeWindowHandleOpt(info.window.get(), SET_UI_TIMESTAMP, pts);
107         int fenceFd = -1;
108         int32_t ret = OH_NativeWindow_NativeWindowRequestBuffer(info.window.get(), &buffer, &fenceFd);
109         CHECK_AND_CONTINUE_LOG(ret == 0, "RequestBuffer failed, ret: %{public}d", ret);
110 
111         BufferHandle* bufferHandle = OH_NativeWindow_GetBufferHandleFromNative(buffer);
112         CHECK_AND_BREAK_LOG(bufferHandle != nullptr, "Get buffer handle failed, thread out");
113         uint8_t *bufferAddr = static_cast<uint8_t *>(mmap(bufferHandle->virAddr, bufferHandle->size,
114             PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0));
115         CHECK_AND_BREAK_LOG(bufferAddr != MAP_FAILED, "Map native window buffer failed, thread out");
116 
117         CodecBufferInfo bufferInfo(bufferAddr);
118         ret = dataProducer_->ReadSample(bufferInfo);
119         CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Read frame failed, thread out");
120         CHECK_AND_BREAK_LOG(!(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS), "Push EOS frame, thread out");
121         ret = munmap(bufferAddr, bufferHandle->size);
122         CHECK_AND_BREAK_LOG(ret != -1, "Unmap buffer failed, thread out");
123 
124         ThreadSleep(info.threadSleepMode == THREAD_SLEEP_MODE_INPUT_SLEEP, info.frameInterval);
125 
126         AVCodecTrace::TraceBegin("OH::Frame", pts);
127         ret = OH_NativeWindow_NativeWindowFlushBuffer(info.window.get(), buffer, fenceFd, {nullptr, 0});
128         CHECK_AND_BREAK_LOG(ret == 0, "Read frame failed, thread out");
129 
130         buffer = nullptr;
131     }
132     OH_NativeWindow_DestroyNativeWindowBuffer(buffer);
133     context_->videoCodec->PushInput(eosBufferInfo);
134     AVCODEC_LOGI("Exit, frame count: %{public}u", context_->inputBufferQueue.GetFrameCount());
135 }
136 
OutputThread()137 void VideoEncoderSample::OutputThread()
138 {
139     auto &info = *context_->sampleInfo;
140     while (true) {
141         auto bufferInfoOpt = context_->outputBufferQueue.DequeueBuffer();
142         CHECK_AND_CONTINUE_LOG(bufferInfoOpt != std::nullopt, "Buffer queue is empty, try dequeue again");
143         auto &bufferInfo = bufferInfoOpt.value();
144         AVCODEC_LOGV("Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}" PRId64,
145             context_->outputBufferQueue.GetFrameCount(),
146             bufferInfo.attr.size, bufferInfo.attr.flags, bufferInfo.attr.pts);
147         CHECK_AND_BREAK_LOG(!(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS), "Catch EOS frame, thread out");
148 
149         DumpOutput(bufferInfo);
150         ThreadSleep(info.threadSleepMode == THREAD_SLEEP_MODE_OUTPUT_SLEEP, info.frameInterval);
151 
152         int32_t ret = context_->videoCodec->FreeOutput(bufferInfo.bufferIndex);
153         CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Decoder output thread out");
154     }
155     OHOS::MediaAVCodec::AVCodecTrace::TraceEnd("SampleWorkTime", FAKE_POINTER(this));
156     OHOS::MediaAVCodec::AVCodecTrace::CounterTrace("SampleFrameCount", context_->outputBufferQueue.GetFrameCount());
157     NotifySampleDone();
158     AVCODEC_LOGI("Exit, frame count: %{public}u", context_->outputBufferQueue.GetFrameCount());
159 }
160 } // Sample
161 } // MediaAVCodec
162 } // OHOS