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 <arc/MojoProcessSupport.h>
12 #include <arc/MojoThread.h>
13 #include <base/bind.h>
14 #include <base/files/scoped_file.h>
15 #include <mojo/public/cpp/platform/platform_handle.h>
16 #include <mojo/public/cpp/system/platform_handle.h>
17
18 #include <binder/IServiceManager.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 {
C2VDAAdaptorProxy()33 C2VDAAdaptorProxy::C2VDAAdaptorProxy()
34 : C2VDAAdaptorProxy(::arc::MojoProcessSupport::getLeakyInstance()) {}
35
C2VDAAdaptorProxy(::arc::MojoProcessSupport * mojoProcessSupport)36 C2VDAAdaptorProxy::C2VDAAdaptorProxy(::arc::MojoProcessSupport* mojoProcessSupport)
37 : mClient(nullptr),
38 mMojoTaskRunner(mojoProcessSupport->mojo_thread().getTaskRunner()),
39 mBinding(this),
40 mRelay(new ::arc::CancellationRelay()) {}
41
~C2VDAAdaptorProxy()42 C2VDAAdaptorProxy::~C2VDAAdaptorProxy() {}
43
onConnectionError(const std::string & pipeName)44 void C2VDAAdaptorProxy::onConnectionError(const std::string& pipeName) {
45 ALOGE("onConnectionError (%s)", pipeName.c_str());
46 mRelay->cancel();
47 NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
48 }
49
establishChannel()50 bool C2VDAAdaptorProxy::establishChannel() {
51 ALOGV("establishChannel");
52 auto future = ::arc::Future<bool>::make_shared(mRelay);
53 mMojoTaskRunner->PostTask(FROM_HERE,
54 ::base::Bind(&C2VDAAdaptorProxy::establishChannelOnMojoThread,
55 ::base::Unretained(this), future));
56 return future->wait() && future->get();
57 }
58
establishChannelOnMojoThread(std::shared_ptr<::arc::Future<bool>> future)59 void C2VDAAdaptorProxy::establishChannelOnMojoThread(std::shared_ptr<::arc::Future<bool>> future) {
60 auto& factory = ::android::GetC2ArcVideoAcceleratorFactory();
61
62 if (!factory.createVideoDecodeAccelerator(mojo::MakeRequest(&mVDAPtr))) {
63 future->set(false);
64 return;
65 }
66 mVDAPtr.set_connection_error_handler(::base::Bind(&C2VDAAdaptorProxy::onConnectionError,
67 ::base::Unretained(this),
68 std::string("mVDAPtr (vda pipe)")));
69 mVDAPtr.QueryVersion(::base::Bind(&C2VDAAdaptorProxy::onVersionReady, ::base::Unretained(this),
70 std::move(future)));
71 }
72
onVersionReady(std::shared_ptr<::arc::Future<bool>> future,uint32_t version)73 void C2VDAAdaptorProxy::onVersionReady(std::shared_ptr<::arc::Future<bool>> future, uint32_t version) {
74 ALOGI("VideoDecodeAccelerator ready (version=%d)", version);
75
76 future->set(true);
77 }
78
ProvidePictureBuffers(::arc::mojom::PictureBufferFormatPtr format)79 void C2VDAAdaptorProxy::ProvidePictureBuffers(::arc::mojom::PictureBufferFormatPtr format) {
80 ALOGV("ProvidePictureBuffers");
81 mClient->providePictureBuffers(
82 format->min_num_buffers,
83 media::Size(format->coded_size.width(), format->coded_size.height()));
84 }
PictureReady(::arc::mojom::PicturePtr picture)85 void C2VDAAdaptorProxy::PictureReady(::arc::mojom::PicturePtr picture) {
86 ALOGV("PictureReady");
87 const auto& rect = picture->crop_rect;
88 mClient->pictureReady(picture->picture_buffer_id, picture->bitstream_id,
89 media::Rect(rect.x(), rect.y(), rect.right(), rect.bottom()));
90 }
91
convertErrorCode(::arc::mojom::VideoDecodeAccelerator::Result error)92 static VideoDecodeAcceleratorAdaptor::Result convertErrorCode(
93 ::arc::mojom::VideoDecodeAccelerator::Result error) {
94 switch (error) {
95 case ::arc::mojom::VideoDecodeAccelerator::Result::ILLEGAL_STATE:
96 return VideoDecodeAcceleratorAdaptor::ILLEGAL_STATE;
97 case ::arc::mojom::VideoDecodeAccelerator::Result::INVALID_ARGUMENT:
98 return VideoDecodeAcceleratorAdaptor::INVALID_ARGUMENT;
99 case ::arc::mojom::VideoDecodeAccelerator::Result::UNREADABLE_INPUT:
100 return VideoDecodeAcceleratorAdaptor::UNREADABLE_INPUT;
101 case ::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE:
102 return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
103 case ::arc::mojom::VideoDecodeAccelerator::Result::INSUFFICIENT_RESOURCES:
104 return VideoDecodeAcceleratorAdaptor::INSUFFICIENT_RESOURCES;
105
106 default:
107 ALOGE("Unknown error code: %d", static_cast<int>(error));
108 return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
109 }
110 }
111
NotifyError(::arc::mojom::VideoDecodeAccelerator::Result error)112 void C2VDAAdaptorProxy::NotifyError(::arc::mojom::VideoDecodeAccelerator::Result error) {
113 ALOGE("NotifyError %d", static_cast<int>(error));
114 mClient->notifyError(convertErrorCode(error));
115 }
116
NotifyEndOfBitstreamBuffer(int32_t bitstream_id)117 void C2VDAAdaptorProxy::NotifyEndOfBitstreamBuffer(int32_t bitstream_id) {
118 ALOGV("NotifyEndOfBitstreamBuffer");
119 mClient->notifyEndOfBitstreamBuffer(bitstream_id);
120 }
121
NotifyResetDone(::arc::mojom::VideoDecodeAccelerator::Result result)122 void C2VDAAdaptorProxy::NotifyResetDone(::arc::mojom::VideoDecodeAccelerator::Result result) {
123 ALOGV("NotifyResetDone");
124 // Always notify reset done to component even if result is not success. On shutdown, MediaCodec
125 // will wait on shutdown complete notification despite any error. If no notification, it will be
126 // hanging until timeout and force release.
127 if (result != ::arc::mojom::VideoDecodeAccelerator::Result::SUCCESS) {
128 ALOGE("Reset is done incorrectly.");
129 NotifyError(result);
130 }
131 mClient->notifyResetDone();
132 }
133
NotifyFlushDone(::arc::mojom::VideoDecodeAccelerator::Result result)134 void C2VDAAdaptorProxy::NotifyFlushDone(::arc::mojom::VideoDecodeAccelerator::Result result) {
135 ALOGV("NotifyFlushDone");
136 if (result == ::arc::mojom::VideoDecodeAccelerator::Result::CANCELLED) {
137 // Flush is cancelled by a succeeding Reset(). A client expects this behavior.
138 ALOGE("Flush is canceled.");
139 return;
140 }
141 if (result != ::arc::mojom::VideoDecodeAccelerator::Result::SUCCESS) {
142 ALOGE("Flush is done incorrectly.");
143 NotifyError(result);
144 return;
145 }
146 mClient->notifyFlushDone();
147 }
148
149 //static
GetSupportedProfiles(InputCodec inputCodec)150 media::VideoDecodeAccelerator::SupportedProfiles C2VDAAdaptorProxy::GetSupportedProfiles(
151 InputCodec inputCodec) {
152 media::VideoDecodeAccelerator::SupportedProfiles profiles(1);
153 profiles[0].min_resolution = media::Size(16, 16);
154 profiles[0].max_resolution = media::Size(4096, 4096);
155 switch (inputCodec) {
156 case InputCodec::H264:
157 profiles[0].profile = media::H264PROFILE_MAIN;
158 break;
159 case InputCodec::VP8:
160 profiles[0].profile = media::VP8PROFILE_ANY;
161 break;
162 case InputCodec::VP9:
163 profiles[0].profile = media::VP9PROFILE_PROFILE0;
164 break;
165 default:
166 ALOGE("Unknown input codec: %d", inputCodec);
167 return {};
168 }
169 return profiles;
170 }
171
initialize(media::VideoCodecProfile profile,bool secureMode,VideoDecodeAcceleratorAdaptor::Client * client)172 VideoDecodeAcceleratorAdaptor::Result C2VDAAdaptorProxy::initialize(
173 media::VideoCodecProfile profile, bool secureMode,
174 VideoDecodeAcceleratorAdaptor::Client* client) {
175 ALOGV("initialize(profile=%d, secureMode=%d)", static_cast<int>(profile),
176 static_cast<int>(secureMode));
177 DCHECK(client);
178 DCHECK(!mClient);
179 mClient = client;
180
181 if (!establishChannel()) {
182 ALOGE("establishChannel failed");
183 return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
184 }
185
186 auto future = ::arc::Future<::arc::mojom::VideoDecodeAccelerator::Result>::make_shared(mRelay);
187 mMojoTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::initializeOnMojoThread,
188 ::base::Unretained(this), profile, secureMode,
189 ::arc::FutureCallback(future)));
190
191 if (!future->wait()) {
192 ALOGE("Connection lost");
193 return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
194 }
195 return static_cast<VideoDecodeAcceleratorAdaptor::Result>(future->get());
196 }
197
initializeOnMojoThread(const media::VideoCodecProfile profile,const bool secureMode,const::arc::mojom::VideoDecodeAccelerator::InitializeCallback & cb)198 void C2VDAAdaptorProxy::initializeOnMojoThread(
199 const media::VideoCodecProfile profile, const bool secureMode,
200 const ::arc::mojom::VideoDecodeAccelerator::InitializeCallback& cb) {
201 // base::Unretained is safe because we own |mBinding|.
202 mojo::InterfacePtr<::arc::mojom::VideoDecodeClient> client;
203 mBinding.Bind(mojo::MakeRequest(&client));
204 mBinding.set_connection_error_handler(::base::Bind(&C2VDAAdaptorProxy::onConnectionError,
205 ::base::Unretained(this),
206 std::string("mBinding (client pipe)")));
207
208 ::arc::mojom::VideoDecodeAcceleratorConfigPtr arcConfig =
209 ::arc::mojom::VideoDecodeAcceleratorConfig::New();
210 arcConfig->secure_mode = secureMode;
211 arcConfig->profile = static_cast<::arc::mojom::VideoCodecProfile>(profile);
212 mVDAPtr->Initialize(std::move(arcConfig), std::move(client), cb);
213 }
214
decode(int32_t bitstreamId,int handleFd,off_t offset,uint32_t size)215 void C2VDAAdaptorProxy::decode(int32_t bitstreamId, int handleFd, off_t offset, uint32_t size) {
216 ALOGV("decode");
217 mMojoTaskRunner->PostTask(
218 FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::decodeOnMojoThread, ::base::Unretained(this),
219 bitstreamId, handleFd, offset, size));
220 }
221
decodeOnMojoThread(int32_t bitstreamId,int handleFd,off_t offset,uint32_t size)222 void C2VDAAdaptorProxy::decodeOnMojoThread(int32_t bitstreamId, int handleFd, off_t offset,
223 uint32_t size) {
224 mojo::ScopedHandle wrappedHandle =
225 mojo::WrapPlatformHandle(mojo::PlatformHandle(::base::ScopedFD(handleFd)));
226 if (!wrappedHandle.is_valid()) {
227 ALOGE("failed to wrap handle");
228 NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
229 return;
230 }
231 auto bufferPtr = ::arc::mojom::BitstreamBuffer::New();
232 bufferPtr->bitstream_id = bitstreamId;
233 bufferPtr->handle_fd = std::move(wrappedHandle);
234 bufferPtr->offset = offset;
235 bufferPtr->bytes_used = size;
236 mVDAPtr->Decode(std::move(bufferPtr));
237 }
238
assignPictureBuffers(uint32_t numOutputBuffers)239 void C2VDAAdaptorProxy::assignPictureBuffers(uint32_t numOutputBuffers) {
240 ALOGV("assignPictureBuffers: %d", numOutputBuffers);
241 mMojoTaskRunner->PostTask(FROM_HERE,
242 ::base::Bind(&C2VDAAdaptorProxy::assignPictureBuffersOnMojoThread,
243 ::base::Unretained(this), numOutputBuffers));
244 }
245
assignPictureBuffersOnMojoThread(uint32_t numOutputBuffers)246 void C2VDAAdaptorProxy::assignPictureBuffersOnMojoThread(uint32_t numOutputBuffers) {
247 mVDAPtr->AssignPictureBuffers(numOutputBuffers);
248 }
249
importBufferForPicture(int32_t pictureBufferId,HalPixelFormat format,int handleFd,const std::vector<VideoFramePlane> & planes)250 void C2VDAAdaptorProxy::importBufferForPicture(int32_t pictureBufferId, HalPixelFormat format,
251 int handleFd,
252 const std::vector<VideoFramePlane>& planes) {
253 ALOGV("importBufferForPicture");
254 mMojoTaskRunner->PostTask(
255 FROM_HERE,
256 ::base::Bind(&C2VDAAdaptorProxy::importBufferForPictureOnMojoThread,
257 ::base::Unretained(this), pictureBufferId, format, handleFd, planes));
258 }
259
importBufferForPictureOnMojoThread(int32_t pictureBufferId,HalPixelFormat format,int handleFd,const std::vector<VideoFramePlane> & planes)260 void C2VDAAdaptorProxy::importBufferForPictureOnMojoThread(
261 int32_t pictureBufferId, HalPixelFormat format, int handleFd,
262 const std::vector<VideoFramePlane>& planes) {
263 mojo::ScopedHandle wrappedHandle =
264 mojo::WrapPlatformHandle(mojo::PlatformHandle(::base::ScopedFD(handleFd)));
265 if (!wrappedHandle.is_valid()) {
266 ALOGE("failed to wrap handle");
267 NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
268 return;
269 }
270
271 mVDAPtr->ImportBufferForPicture(pictureBufferId,
272 static_cast<::arc::mojom::HalPixelFormat>(format),
273 std::move(wrappedHandle),
274 mojo::ConvertTo<std::vector<::arc::VideoFramePlane>>(planes));
275 }
276
reusePictureBuffer(int32_t pictureBufferId)277 void C2VDAAdaptorProxy::reusePictureBuffer(int32_t pictureBufferId) {
278 ALOGV("reusePictureBuffer: %d", pictureBufferId);
279 mMojoTaskRunner->PostTask(FROM_HERE,
280 ::base::Bind(&C2VDAAdaptorProxy::reusePictureBufferOnMojoThread,
281 ::base::Unretained(this), pictureBufferId));
282 }
283
reusePictureBufferOnMojoThread(int32_t pictureBufferId)284 void C2VDAAdaptorProxy::reusePictureBufferOnMojoThread(int32_t pictureBufferId) {
285 mVDAPtr->ReusePictureBuffer(pictureBufferId);
286 }
287
flush()288 void C2VDAAdaptorProxy::flush() {
289 ALOGV("flush");
290 mMojoTaskRunner->PostTask(
291 FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::flushOnMojoThread, ::base::Unretained(this)));
292 }
293
flushOnMojoThread()294 void C2VDAAdaptorProxy::flushOnMojoThread() {
295 mVDAPtr->Flush(::base::Bind(&C2VDAAdaptorProxy::NotifyFlushDone, ::base::Unretained(this)));
296 }
297
reset()298 void C2VDAAdaptorProxy::reset() {
299 ALOGV("reset");
300 mMojoTaskRunner->PostTask(
301 FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::resetOnMojoThread, ::base::Unretained(this)));
302 }
303
resetOnMojoThread()304 void C2VDAAdaptorProxy::resetOnMojoThread() {
305 mVDAPtr->Reset(::base::Bind(&C2VDAAdaptorProxy::NotifyResetDone, ::base::Unretained(this)));
306 }
307
destroy()308 void C2VDAAdaptorProxy::destroy() {
309 ALOGV("destroy");
310 ::arc::Future<void> future;
311 ::arc::PostTaskAndSetFutureWithResult(
312 mMojoTaskRunner.get(), FROM_HERE,
313 ::base::Bind(&C2VDAAdaptorProxy::closeChannelOnMojoThread, ::base::Unretained(this)),
314 &future);
315 future.get();
316 }
317
closeChannelOnMojoThread()318 void C2VDAAdaptorProxy::closeChannelOnMojoThread() {
319 if (mBinding.is_bound()) mBinding.Close();
320 mVDAPtr.reset();
321 }
322
323 } // namespace arc
324 } // namespace android
325