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