1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // #define LOG_NDEBUG 0
6 #define LOG_TAG "C2VDAAdaptorProxy"
7
8 #include <C2ArcVideoAcceleratorFactory.h>
9 #include <C2VDAAdaptorProxy.h>
10
11 #include <videodev2.h>
12
13 #include <arc/MojoProcessSupport.h>
14 #include <arc/MojoThread.h>
15 #include <base/bind.h>
16 #include <binder/IServiceManager.h>
17 #include <mojo/edk/embedder/embedder.h>
18 #include <mojo/public/cpp/system/handle.h>
19 #include <utils/Log.h>
20
21 namespace mojo {
22 template <>
23 struct TypeConverter<::arc::VideoFramePlane, android::VideoFramePlane> {
Convertmojo::TypeConverter24 static ::arc::VideoFramePlane Convert(const android::VideoFramePlane& plane) {
25 return ::arc::VideoFramePlane{static_cast<int32_t>(plane.mOffset),
26 static_cast<int32_t>(plane.mStride)};
27 }
28 };
29 } // namespace mojo
30
31 namespace android {
32 namespace arc {
33 constexpr SupportedPixelFormat kSupportedPixelFormats[] = {
34 // {mCrcb, mSemiplanar, mPixelFormat}
35 {false, true, HalPixelFormat::NV12},
36 {true, false, HalPixelFormat::YV12},
37 // Add more buffer formats when needed
38 };
39
C2VDAAdaptorProxy()40 C2VDAAdaptorProxy::C2VDAAdaptorProxy()
41 : C2VDAAdaptorProxy(::arc::MojoProcessSupport::getLeakyInstance()) {}
42
C2VDAAdaptorProxy(::arc::MojoProcessSupport * mojoProcessSupport)43 C2VDAAdaptorProxy::C2VDAAdaptorProxy(::arc::MojoProcessSupport* mojoProcessSupport)
44 : mClient(nullptr),
45 mMojoTaskRunner(mojoProcessSupport->mojo_thread().getTaskRunner()),
46 mBinding(this),
47 mRelay(new ::arc::CancellationRelay()) {}
48
~C2VDAAdaptorProxy()49 C2VDAAdaptorProxy::~C2VDAAdaptorProxy() {}
50
onConnectionError(const std::string & pipeName)51 void C2VDAAdaptorProxy::onConnectionError(const std::string& pipeName) {
52 ALOGE("onConnectionError (%s)", pipeName.c_str());
53 mRelay->cancel();
54 NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
55 }
56
establishChannel()57 bool C2VDAAdaptorProxy::establishChannel() {
58 ALOGV("establishChannel");
59 auto future = ::arc::Future<bool>::make_shared(mRelay);
60 mMojoTaskRunner->PostTask(FROM_HERE,
61 base::Bind(&C2VDAAdaptorProxy::establishChannelOnMojoThread,
62 base::Unretained(this), future));
63 return future->wait() && future->get();
64 }
65
establishChannelOnMojoThread(std::shared_ptr<::arc::Future<bool>> future)66 void C2VDAAdaptorProxy::establishChannelOnMojoThread(std::shared_ptr<::arc::Future<bool>> future) {
67 C2ArcVideoAcceleratorFactory& factory = ::android::C2ArcVideoAcceleratorFactory::getInstance();
68
69 if (!factory.createVideoDecodeAccelerator(mojo::MakeRequest(&mVDAPtr))) {
70 future->set(false);
71 return;
72 }
73 mVDAPtr.set_connection_error_handler(base::Bind(&C2VDAAdaptorProxy::onConnectionError,
74 base::Unretained(this),
75 std::string("mVDAPtr (vda pipe)")));
76 mVDAPtr.QueryVersion(base::Bind(&C2VDAAdaptorProxy::onVersionReady, base::Unretained(this),
77 std::move(future)));
78 }
79
onVersionReady(std::shared_ptr<::arc::Future<bool>> future,uint32_t version)80 void C2VDAAdaptorProxy::onVersionReady(std::shared_ptr<::arc::Future<bool>> future, uint32_t version) {
81 ALOGI("VideoDecodeAccelerator ready (version=%d)", version);
82
83 future->set(true);
84 }
85
ProvidePictureBuffers(::arc::mojom::PictureBufferFormatPtr format)86 void C2VDAAdaptorProxy::ProvidePictureBuffers(::arc::mojom::PictureBufferFormatPtr format) {
87 ALOGV("ProvidePictureBuffers");
88 mClient->providePictureBuffers(
89 format->min_num_buffers,
90 media::Size(format->coded_size.width(), format->coded_size.height()));
91 }
PictureReady(::arc::mojom::PicturePtr picture)92 void C2VDAAdaptorProxy::PictureReady(::arc::mojom::PicturePtr picture) {
93 ALOGV("PictureReady");
94 const auto& rect = picture->crop_rect;
95 mClient->pictureReady(picture->picture_buffer_id, picture->bitstream_id,
96 media::Rect(rect.x(), rect.y(), rect.right(), rect.bottom()));
97 }
98
convertErrorCode(::arc::mojom::VideoDecodeAccelerator::Result error)99 static VideoDecodeAcceleratorAdaptor::Result convertErrorCode(
100 ::arc::mojom::VideoDecodeAccelerator::Result error) {
101 switch (error) {
102 case ::arc::mojom::VideoDecodeAccelerator::Result::ILLEGAL_STATE:
103 return VideoDecodeAcceleratorAdaptor::ILLEGAL_STATE;
104 case ::arc::mojom::VideoDecodeAccelerator::Result::INVALID_ARGUMENT:
105 return VideoDecodeAcceleratorAdaptor::INVALID_ARGUMENT;
106 case ::arc::mojom::VideoDecodeAccelerator::Result::UNREADABLE_INPUT:
107 return VideoDecodeAcceleratorAdaptor::UNREADABLE_INPUT;
108 case ::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE:
109 return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
110 case ::arc::mojom::VideoDecodeAccelerator::Result::INSUFFICIENT_RESOURCES:
111 return VideoDecodeAcceleratorAdaptor::INSUFFICIENT_RESOURCES;
112
113 default:
114 ALOGE("Unknown error code: %d", static_cast<int>(error));
115 return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
116 }
117 }
118
NotifyError(::arc::mojom::VideoDecodeAccelerator::Result error)119 void C2VDAAdaptorProxy::NotifyError(::arc::mojom::VideoDecodeAccelerator::Result error) {
120 ALOGE("NotifyError %d", static_cast<int>(error));
121 mClient->notifyError(convertErrorCode(error));
122 }
123
NotifyEndOfBitstreamBuffer(int32_t bitstream_id)124 void C2VDAAdaptorProxy::NotifyEndOfBitstreamBuffer(int32_t bitstream_id) {
125 ALOGV("NotifyEndOfBitstreamBuffer");
126 mClient->notifyEndOfBitstreamBuffer(bitstream_id);
127 }
128
NotifyResetDone(::arc::mojom::VideoDecodeAccelerator::Result result)129 void C2VDAAdaptorProxy::NotifyResetDone(::arc::mojom::VideoDecodeAccelerator::Result result) {
130 ALOGV("NotifyResetDone");
131 if (result != ::arc::mojom::VideoDecodeAccelerator::Result::SUCCESS) {
132 ALOGE("Reset is done incorrectly.");
133 NotifyError(result);
134 return;
135 }
136 mClient->notifyResetDone();
137 }
138
NotifyFlushDone(::arc::mojom::VideoDecodeAccelerator::Result result)139 void C2VDAAdaptorProxy::NotifyFlushDone(::arc::mojom::VideoDecodeAccelerator::Result result) {
140 ALOGV("NotifyFlushDone");
141 if (result == ::arc::mojom::VideoDecodeAccelerator::Result::CANCELLED) {
142 // Flush is cancelled by a succeeding Reset(). A client expects this behavior.
143 ALOGE("Flush is canceled.");
144 return;
145 }
146 if (result != ::arc::mojom::VideoDecodeAccelerator::Result::SUCCESS) {
147 ALOGE("Flush is done incorrectly.");
148 NotifyError(result);
149 return;
150 }
151 mClient->notifyFlushDone();
152 }
153
154 //static
GetSupportedProfiles(uint32_t inputFormatFourcc)155 media::VideoDecodeAccelerator::SupportedProfiles C2VDAAdaptorProxy::GetSupportedProfiles(
156 uint32_t inputFormatFourcc) {
157 media::VideoDecodeAccelerator::SupportedProfiles profiles(1);
158 profiles[0].min_resolution = media::Size(16, 16);
159 profiles[0].max_resolution = media::Size(4096, 4096);
160 switch (inputFormatFourcc) {
161 case V4L2_PIX_FMT_H264:
162 case V4L2_PIX_FMT_H264_SLICE:
163 profiles[0].profile = media::H264PROFILE_MAIN;
164 break;
165 case V4L2_PIX_FMT_VP8:
166 case V4L2_PIX_FMT_VP8_FRAME:
167 profiles[0].profile = media::VP8PROFILE_ANY;
168 break;
169 case V4L2_PIX_FMT_VP9:
170 case V4L2_PIX_FMT_VP9_FRAME:
171 profiles[0].profile = media::VP9PROFILE_PROFILE0;
172 break;
173 default:
174 ALOGE("Unknown formatfourcc: %d", inputFormatFourcc);
175 return {};
176 }
177 return profiles;
178 }
179
180 //static
ResolveBufferFormat(bool crcb,bool semiplanar)181 HalPixelFormat C2VDAAdaptorProxy::ResolveBufferFormat(bool crcb, bool semiplanar) {
182 auto value = std::find_if(std::begin(kSupportedPixelFormats), std::end(kSupportedPixelFormats),
183 [crcb, semiplanar](const struct SupportedPixelFormat& f) {
184 return f.mCrcb == crcb && f.mSemiplanar == semiplanar;
185 });
186 LOG_ALWAYS_FATAL_IF(value == std::end(kSupportedPixelFormats),
187 "Unsupported pixel format: (crcb=%d, semiplanar=%d)", crcb, semiplanar);
188 return value->mPixelFormat;
189 }
190
initialize(media::VideoCodecProfile profile,bool secureMode,VideoDecodeAcceleratorAdaptor::Client * client)191 VideoDecodeAcceleratorAdaptor::Result C2VDAAdaptorProxy::initialize(
192 media::VideoCodecProfile profile, bool secureMode,
193 VideoDecodeAcceleratorAdaptor::Client* client) {
194 ALOGV("initialize(profile=%d, secureMode=%d)", static_cast<int>(profile),
195 static_cast<int>(secureMode));
196 DCHECK(client);
197 DCHECK(!mClient);
198 mClient = client;
199
200 if (!establishChannel()) {
201 ALOGE("establishChannel failed");
202 return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
203 }
204
205 auto future = ::arc::Future<::arc::mojom::VideoDecodeAccelerator::Result>::make_shared(mRelay);
206 mMojoTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAAdaptorProxy::initializeOnMojoThread,
207 base::Unretained(this), profile, secureMode,
208 ::arc::FutureCallback(future)));
209
210 if (!future->wait()) {
211 ALOGE("Connection lost");
212 return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
213 }
214 return static_cast<VideoDecodeAcceleratorAdaptor::Result>(future->get());
215 }
216
initializeOnMojoThread(const media::VideoCodecProfile profile,const bool secureMode,const::arc::mojom::VideoDecodeAccelerator::InitializeCallback & cb)217 void C2VDAAdaptorProxy::initializeOnMojoThread(
218 const media::VideoCodecProfile profile, const bool secureMode,
219 const ::arc::mojom::VideoDecodeAccelerator::InitializeCallback& cb) {
220 // base::Unretained is safe because we own |mBinding|.
221 auto client = mBinding.CreateInterfacePtrAndBind();
222 mBinding.set_connection_error_handler(base::Bind(&C2VDAAdaptorProxy::onConnectionError,
223 base::Unretained(this),
224 std::string("mBinding (client pipe)")));
225
226 ::arc::mojom::VideoDecodeAcceleratorConfigPtr arcConfig =
227 ::arc::mojom::VideoDecodeAcceleratorConfig::New();
228 arcConfig->secure_mode = secureMode;
229 arcConfig->profile = static_cast<::arc::mojom::VideoCodecProfile>(profile);
230 mVDAPtr->Initialize(std::move(arcConfig), std::move(client), cb);
231 }
232
decode(int32_t bitstreamId,int handleFd,off_t offset,uint32_t size)233 void C2VDAAdaptorProxy::decode(int32_t bitstreamId, int handleFd, off_t offset, uint32_t size) {
234 ALOGV("decode");
235 mMojoTaskRunner->PostTask(
236 FROM_HERE, base::Bind(&C2VDAAdaptorProxy::decodeOnMojoThread, base::Unretained(this),
237 bitstreamId, handleFd, offset, size));
238 }
239
decodeOnMojoThread(int32_t bitstreamId,int handleFd,off_t offset,uint32_t size)240 void C2VDAAdaptorProxy::decodeOnMojoThread(int32_t bitstreamId, int handleFd, off_t offset,
241 uint32_t size) {
242 MojoHandle wrappedHandle;
243 MojoResult wrapResult = mojo::edk::CreatePlatformHandleWrapper(
244 mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(handleFd)), &wrappedHandle);
245 if (wrapResult != MOJO_RESULT_OK) {
246 ALOGE("failed to wrap handle: %d", static_cast<int>(wrapResult));
247 NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
248 return;
249 }
250 auto bufferPtr = ::arc::mojom::BitstreamBuffer::New();
251 bufferPtr->bitstream_id = bitstreamId;
252 bufferPtr->handle_fd = mojo::ScopedHandle(mojo::Handle(wrappedHandle));
253 bufferPtr->offset = offset;
254 bufferPtr->bytes_used = size;
255 mVDAPtr->Decode(std::move(bufferPtr));
256 }
257
assignPictureBuffers(uint32_t numOutputBuffers)258 void C2VDAAdaptorProxy::assignPictureBuffers(uint32_t numOutputBuffers) {
259 ALOGV("assignPictureBuffers: %d", numOutputBuffers);
260 mMojoTaskRunner->PostTask(FROM_HERE,
261 base::Bind(&C2VDAAdaptorProxy::assignPictureBuffersOnMojoThread,
262 base::Unretained(this), numOutputBuffers));
263 }
264
assignPictureBuffersOnMojoThread(uint32_t numOutputBuffers)265 void C2VDAAdaptorProxy::assignPictureBuffersOnMojoThread(uint32_t numOutputBuffers) {
266 mVDAPtr->AssignPictureBuffers(numOutputBuffers);
267 }
268
importBufferForPicture(int32_t pictureBufferId,HalPixelFormat format,int handleFd,const std::vector<VideoFramePlane> & planes)269 void C2VDAAdaptorProxy::importBufferForPicture(int32_t pictureBufferId, HalPixelFormat format,
270 int handleFd,
271 const std::vector<VideoFramePlane>& planes) {
272 ALOGV("importBufferForPicture");
273 mMojoTaskRunner->PostTask(
274 FROM_HERE,
275 base::Bind(&C2VDAAdaptorProxy::importBufferForPictureOnMojoThread,
276 base::Unretained(this), pictureBufferId, format, handleFd, planes));
277 }
278
importBufferForPictureOnMojoThread(int32_t pictureBufferId,HalPixelFormat format,int handleFd,const std::vector<VideoFramePlane> & planes)279 void C2VDAAdaptorProxy::importBufferForPictureOnMojoThread(
280 int32_t pictureBufferId, HalPixelFormat format, int handleFd,
281 const std::vector<VideoFramePlane>& planes) {
282 MojoHandle wrappedHandle;
283 MojoResult wrapResult = mojo::edk::CreatePlatformHandleWrapper(
284 mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(handleFd)), &wrappedHandle);
285 if (wrapResult != MOJO_RESULT_OK) {
286 ALOGE("failed to wrap handle: %d", static_cast<int>(wrapResult));
287 NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
288 return;
289 }
290
291 mVDAPtr->ImportBufferForPicture(pictureBufferId,
292 static_cast<::arc::mojom::HalPixelFormat>(format),
293 mojo::ScopedHandle(mojo::Handle(wrappedHandle)),
294 mojo::ConvertTo<std::vector<::arc::VideoFramePlane>>(planes));
295 }
296
reusePictureBuffer(int32_t pictureBufferId)297 void C2VDAAdaptorProxy::reusePictureBuffer(int32_t pictureBufferId) {
298 ALOGV("reusePictureBuffer: %d", pictureBufferId);
299 mMojoTaskRunner->PostTask(FROM_HERE,
300 base::Bind(&C2VDAAdaptorProxy::reusePictureBufferOnMojoThread,
301 base::Unretained(this), pictureBufferId));
302 }
303
reusePictureBufferOnMojoThread(int32_t pictureBufferId)304 void C2VDAAdaptorProxy::reusePictureBufferOnMojoThread(int32_t pictureBufferId) {
305 mVDAPtr->ReusePictureBuffer(pictureBufferId);
306 }
307
flush()308 void C2VDAAdaptorProxy::flush() {
309 ALOGV("flush");
310 mMojoTaskRunner->PostTask(
311 FROM_HERE, base::Bind(&C2VDAAdaptorProxy::flushOnMojoThread, base::Unretained(this)));
312 }
313
flushOnMojoThread()314 void C2VDAAdaptorProxy::flushOnMojoThread() {
315 mVDAPtr->Flush(base::Bind(&C2VDAAdaptorProxy::NotifyFlushDone, base::Unretained(this)));
316 }
317
reset()318 void C2VDAAdaptorProxy::reset() {
319 ALOGV("reset");
320 mMojoTaskRunner->PostTask(
321 FROM_HERE, base::Bind(&C2VDAAdaptorProxy::resetOnMojoThread, base::Unretained(this)));
322 }
323
resetOnMojoThread()324 void C2VDAAdaptorProxy::resetOnMojoThread() {
325 mVDAPtr->Reset(base::Bind(&C2VDAAdaptorProxy::NotifyResetDone, base::Unretained(this)));
326 }
327
destroy()328 void C2VDAAdaptorProxy::destroy() {
329 ALOGV("destroy");
330 ::arc::Future<void> future;
331 ::arc::PostTaskAndSetFutureWithResult(
332 mMojoTaskRunner.get(), FROM_HERE,
333 base::Bind(&C2VDAAdaptorProxy::closeChannelOnMojoThread, base::Unretained(this)),
334 &future);
335 future.get();
336 }
337
closeChannelOnMojoThread()338 void C2VDAAdaptorProxy::closeChannelOnMojoThread() {
339 if (mBinding.is_bound()) mBinding.Close();
340 mVDAPtr.reset();
341 }
342
343 } // namespace arc
344 } // namespace android
345