1 // Copyright (c) 2019 - 2020 Intel Corporation
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in all
11 // copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 // SOFTWARE.
20
21 #include <sys/ioctl.h>
22 #include <drm.h>
23 #include <drm_fourcc.h>
24 #include <map>
25 #include <string>
26
27 #include "common_vaapi.h"
28
29 struct sharedResponse {
30 mfxFrameAllocResponse mfxResponse;
31 int refCount;
32 };
33
34 std::map<mfxMemId*, mfxHDL> allocResponses;
35 std::map<mfxHDL, sharedResponse> allocDecodeResponses;
36
37
va_to_mfx_status(VAStatus va_res)38 mfxStatus va_to_mfx_status(VAStatus va_res)
39 {
40 mfxStatus mfxRes = MFX_ERR_NONE;
41
42 switch (va_res) {
43 case VA_STATUS_SUCCESS:
44 mfxRes = MFX_ERR_NONE;
45 break;
46 case VA_STATUS_ERROR_ALLOCATION_FAILED:
47 mfxRes = MFX_ERR_MEMORY_ALLOC;
48 break;
49 case VA_STATUS_ERROR_ATTR_NOT_SUPPORTED:
50 case VA_STATUS_ERROR_UNSUPPORTED_PROFILE:
51 case VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT:
52 case VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT:
53 case VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE:
54 case VA_STATUS_ERROR_FLAG_NOT_SUPPORTED:
55 case VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED:
56 mfxRes = MFX_ERR_UNSUPPORTED;
57 break;
58 case VA_STATUS_ERROR_INVALID_DISPLAY:
59 case VA_STATUS_ERROR_INVALID_CONFIG:
60 case VA_STATUS_ERROR_INVALID_CONTEXT:
61 case VA_STATUS_ERROR_INVALID_SURFACE:
62 case VA_STATUS_ERROR_INVALID_BUFFER:
63 case VA_STATUS_ERROR_INVALID_IMAGE:
64 case VA_STATUS_ERROR_INVALID_SUBPICTURE:
65 mfxRes = MFX_ERR_NOT_INITIALIZED;
66 break;
67 case VA_STATUS_ERROR_INVALID_PARAMETER:
68 mfxRes = MFX_ERR_INVALID_VIDEO_PARAM;
69 default:
70 mfxRes = MFX_ERR_UNKNOWN;
71 break;
72 }
73 return mfxRes;
74 }
75
76
77 // global variables shared by the below functions
78
79 // VAAPI display handle
80 VADisplay m_va_dpy = NULL;
81 // gfx card file descriptor
82 int m_fd = -1;
83
84 constexpr uint32_t DRI_MAX_NODES_NUM = 16;
85 constexpr uint32_t DRI_RENDER_START_INDEX = 128;
86 constexpr uint32_t DRM_DRIVER_NAME_LEN = 4;
87 const char* DRM_INTEL_DRIVER_NAME = "i915";
88 const char* DRI_PATH = "/dev/dri/";
89 const char* DRI_NODE_RENDER = "renderD";
90
get_drm_driver_name(int fd,char * name,int name_size)91 int get_drm_driver_name(int fd, char *name, int name_size)
92 {
93 drm_version_t version = {};
94 version.name_len = name_size;
95 version.name = name;
96 return ioctl(fd, DRM_IOWR(0, drm_version), &version);
97 }
98
open_intel_adapter()99 int open_intel_adapter()
100 {
101 std::string adapterPath = DRI_PATH;
102 adapterPath += DRI_NODE_RENDER;
103
104 char driverName[DRM_DRIVER_NAME_LEN + 1] = {};
105 mfxU32 nodeIndex = DRI_RENDER_START_INDEX;
106
107 for (mfxU32 i = 0; i < DRI_MAX_NODES_NUM; ++i) {
108 std::string curAdapterPath = adapterPath + std::to_string(nodeIndex + i);
109
110 int fd = open(curAdapterPath.c_str(), O_RDWR);
111 if (fd < 0) continue;
112
113 if (!get_drm_driver_name(fd, driverName, DRM_DRIVER_NAME_LEN) &&
114 !strcmp(driverName, DRM_INTEL_DRIVER_NAME)) {
115 return fd;
116 }
117 close(fd);
118 }
119
120 return -1;
121 }
122
CreateVAEnvDRM(mfxHDL * displayHandle)123 mfxStatus CreateVAEnvDRM(mfxHDL* displayHandle)
124 {
125 VAStatus va_res = VA_STATUS_SUCCESS;
126 mfxStatus sts = MFX_ERR_NONE;
127 int major_version = 0, minor_version = 0;
128
129 m_fd = open_intel_adapter();
130
131 if (m_fd < 0)
132 sts = MFX_ERR_NOT_INITIALIZED;
133
134 if (MFX_ERR_NONE == sts) {
135 m_va_dpy = vaGetDisplayDRM(m_fd);
136
137 *displayHandle = m_va_dpy;
138
139 if (!m_va_dpy) {
140 close(m_fd);
141 sts = MFX_ERR_NULL_PTR;
142 }
143 }
144 if (MFX_ERR_NONE == sts) {
145 va_res = vaInitialize(m_va_dpy, &major_version, &minor_version);
146 sts = va_to_mfx_status(va_res);
147 if (MFX_ERR_NONE != sts) {
148 close(m_fd);
149 m_fd = -1;
150 }
151 }
152 if (MFX_ERR_NONE != sts)
153 throw std::bad_alloc();
154
155 return MFX_ERR_NONE;
156 }
157
CleanupVAEnvDRM()158 void CleanupVAEnvDRM()
159 {
160 if (m_va_dpy) {
161 vaTerminate(m_va_dpy);
162 }
163 if (m_fd >= 0) {
164 close(m_fd);
165 }
166 }
167
168 //utiility function to convert MFX fourcc to VA format
ConvertMfxFourccToVAFormat(mfxU32 fourcc)169 unsigned int ConvertMfxFourccToVAFormat(mfxU32 fourcc)
170 {
171 switch (fourcc) {
172 case MFX_FOURCC_NV12:
173 return VA_FOURCC_NV12;
174 case MFX_FOURCC_YUY2:
175 return VA_FOURCC_YUY2;
176 case MFX_FOURCC_YV12:
177 return VA_FOURCC_YV12;
178 case MFX_FOURCC_RGB4:
179 return VA_FOURCC_ARGB;
180 case MFX_FOURCC_P8:
181 return VA_FOURCC_P208;
182
183 default:
184 assert(!"unsupported fourcc");
185 return 0;
186 }
187 }
188
ClearYUVSurfaceVAAPI(mfxMemId memId)189 void ClearYUVSurfaceVAAPI(mfxMemId memId)
190 {
191 // todo: clear VAAPI surface
192 }
193
ClearRGBSurfaceVAAPI(mfxMemId memId)194 void ClearRGBSurfaceVAAPI(mfxMemId memId)
195 {
196 // todo: clear VAAPI surface
197 }
198
199 // VAAPI Allocator internal Mem ID
200 struct vaapiMemId {
201 VASurfaceID* m_surface;
202 VAImage m_image;
203 // variables for VAAPI Allocator inernal color convertion
204 unsigned int m_fourcc;
205 mfxU8* m_sys_buffer;
206 mfxU8* m_va_buffer;
207 };
208
209 //
210 // Media SDK memory allocator entrypoints....
211 //
212
_simple_alloc(mfxFrameAllocRequest * request,mfxFrameAllocResponse * response)213 mfxStatus _simple_alloc(mfxFrameAllocRequest* request,
214 mfxFrameAllocResponse* response)
215 {
216 mfxStatus mfx_res = MFX_ERR_NONE;
217 VAStatus va_res = VA_STATUS_SUCCESS;
218 unsigned int va_fourcc = 0;
219 VASurfaceID* surfaces = NULL;
220 VASurfaceAttrib attrib;
221 vaapiMemId* vaapi_mids = NULL, *vaapi_mid = NULL;
222 mfxMemId* mids = NULL;
223 mfxU32 fourcc = request->Info.FourCC;
224 mfxU16 surfaces_num = request->NumFrameSuggested, numAllocated = 0, i =
225 0;
226 bool bCreateSrfSucceeded = false;
227
228 memset(response, 0, sizeof(mfxFrameAllocResponse));
229
230 va_fourcc = ConvertMfxFourccToVAFormat(fourcc);
231 if (!va_fourcc || ((VA_FOURCC_NV12 != va_fourcc) &&
232 (VA_FOURCC_YV12 != va_fourcc) &&
233 (VA_FOURCC_YUY2 != va_fourcc) &&
234 (VA_FOURCC_ARGB != va_fourcc) &&
235 (VA_FOURCC_P208 != va_fourcc))) {
236 return MFX_ERR_MEMORY_ALLOC;
237 }
238 if (!surfaces_num) {
239 return MFX_ERR_MEMORY_ALLOC;
240 }
241
242 if (MFX_ERR_NONE == mfx_res) {
243 surfaces =
244 (VASurfaceID*) calloc(surfaces_num, sizeof(VASurfaceID));
245 vaapi_mids =
246 (vaapiMemId*) calloc(surfaces_num, sizeof(vaapiMemId));
247 mids = (mfxMemId*) calloc(surfaces_num, sizeof(mfxMemId));
248 if ((NULL == surfaces) || (NULL == vaapi_mids)
249 || (NULL == mids))
250 mfx_res = MFX_ERR_MEMORY_ALLOC;
251 }
252 if (MFX_ERR_NONE == mfx_res) {
253 if (VA_FOURCC_P208 != va_fourcc) {
254 attrib.type = VASurfaceAttribPixelFormat;
255 attrib.value.type = VAGenericValueTypeInteger;
256 attrib.value.value.i = va_fourcc;
257 attrib.flags = VA_SURFACE_ATTRIB_SETTABLE;
258
259 va_res = vaCreateSurfaces(m_va_dpy,
260 VA_RT_FORMAT_YUV420,
261 request->Info.Width,
262 request->Info.Height,
263 surfaces, surfaces_num,
264 &attrib, 1);
265 mfx_res = va_to_mfx_status(va_res);
266 bCreateSrfSucceeded = (MFX_ERR_NONE == mfx_res);
267 } else {
268 VAContextID context_id = request->reserved[0];
269 int codedbuf_size = (request->Info.Width * request->Info.Height) * 400 / (16 * 16); //from libva spec
270
271 for (numAllocated = 0; numAllocated < surfaces_num;
272 numAllocated++) {
273 VABufferID coded_buf;
274
275 va_res = vaCreateBuffer(m_va_dpy,
276 context_id,
277 VAEncCodedBufferType,
278 codedbuf_size,
279 1, NULL, &coded_buf);
280 mfx_res = va_to_mfx_status(va_res);
281 if (MFX_ERR_NONE != mfx_res)
282 break;
283 surfaces[numAllocated] = coded_buf;
284 }
285 }
286 }
287 if (MFX_ERR_NONE == mfx_res) {
288 for (i = 0; i < surfaces_num; ++i) {
289 vaapi_mid = &(vaapi_mids[i]);
290 vaapi_mid->m_fourcc = fourcc;
291 vaapi_mid->m_surface = &(surfaces[i]);
292 mids[i] = vaapi_mid;
293 }
294 }
295 if (MFX_ERR_NONE == mfx_res) {
296 response->mids = mids;
297 response->NumFrameActual = surfaces_num;
298 } else { // i.e. MFX_ERR_NONE != mfx_res
299 response->mids = NULL;
300 response->NumFrameActual = 0;
301 if (VA_FOURCC_P208 != va_fourcc) {
302 if (bCreateSrfSucceeded)
303 vaDestroySurfaces(m_va_dpy, surfaces,
304 surfaces_num);
305 } else {
306 for (i = 0; i < numAllocated; i++)
307 vaDestroyBuffer(m_va_dpy, surfaces[i]);
308 }
309 if (mids) {
310 free(mids);
311 mids = NULL;
312 }
313 if (vaapi_mids) {
314 free(vaapi_mids);
315 vaapi_mids = NULL;
316 }
317 if (surfaces) {
318 free(surfaces);
319 surfaces = NULL;
320 }
321 }
322 return mfx_res;
323 }
324
simple_alloc(mfxHDL pthis,mfxFrameAllocRequest * request,mfxFrameAllocResponse * response)325 mfxStatus simple_alloc(mfxHDL pthis, mfxFrameAllocRequest* request,
326 mfxFrameAllocResponse* response)
327 {
328 mfxStatus sts = MFX_ERR_NONE;
329
330 if (0 == request || 0 == response || 0 == request->NumFrameSuggested)
331 return MFX_ERR_MEMORY_ALLOC;
332
333 if ((request->Type & (MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET |
334 MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET)) == 0)
335 return MFX_ERR_UNSUPPORTED;
336
337 if (request->NumFrameSuggested <=
338 allocDecodeResponses[pthis].mfxResponse.NumFrameActual
339 && MFX_MEMTYPE_EXTERNAL_FRAME & request->Type
340 && MFX_MEMTYPE_FROM_DECODE & request->Type
341 &&allocDecodeResponses[pthis].mfxResponse.NumFrameActual != 0) {
342 // Memory for this request was already allocated during manual allocation stage. Return saved response
343 // When decode acceleration device (VAAPI) is created it requires a list of VAAPI surfaces to be passed.
344 // Therefore Media SDK will ask for the surface info/mids again at Init() stage, thus requiring us to return the saved response
345 // (No such restriction applies to Encode or VPP)
346
347 *response = allocDecodeResponses[pthis].mfxResponse;
348 allocDecodeResponses[pthis].refCount++;
349
350 } else {
351 sts = _simple_alloc(request, response);
352
353 if (MFX_ERR_NONE == sts) {
354 if (MFX_MEMTYPE_EXTERNAL_FRAME & request->Type &&
355 MFX_MEMTYPE_FROM_DECODE & request->Type) {
356 // Decode alloc response handling
357 allocDecodeResponses[pthis].mfxResponse = *response;
358 allocDecodeResponses[pthis].refCount++;
359 //allocDecodeRefCount[pthis]++;
360 } else {
361 // Encode and VPP alloc response handling
362 allocResponses[response->mids] = pthis;
363 }
364 }
365 }
366
367 return sts;
368 }
369
simple_lock(mfxHDL pthis,mfxMemId mid,mfxFrameData * ptr)370 mfxStatus simple_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData* ptr)
371 {
372 mfxStatus mfx_res = MFX_ERR_NONE;
373 VAStatus va_res = VA_STATUS_SUCCESS;
374 vaapiMemId* vaapi_mid = (vaapiMemId*) mid;
375 mfxU8* pBuffer = 0;
376
377 if (!vaapi_mid || !(vaapi_mid->m_surface))
378 return MFX_ERR_INVALID_HANDLE;
379
380 if (MFX_FOURCC_P8 == vaapi_mid->m_fourcc) { // bitstream processing
381 VACodedBufferSegment* coded_buffer_segment;
382 va_res =
383 vaMapBuffer(m_va_dpy, *(vaapi_mid->m_surface),
384 (void**)(&coded_buffer_segment));
385 mfx_res = va_to_mfx_status(va_res);
386 ptr->Y = (mfxU8*) coded_buffer_segment->buf;
387 } else { // Image processing
388 va_res = vaSyncSurface(m_va_dpy, *(vaapi_mid->m_surface));
389 mfx_res = va_to_mfx_status(va_res);
390
391 if (MFX_ERR_NONE == mfx_res) {
392 va_res =
393 vaDeriveImage(m_va_dpy, *(vaapi_mid->m_surface),
394 &(vaapi_mid->m_image));
395 mfx_res = va_to_mfx_status(va_res);
396 }
397 if (MFX_ERR_NONE == mfx_res) {
398 va_res =
399 vaMapBuffer(m_va_dpy, vaapi_mid->m_image.buf,
400 (void**)&pBuffer);
401 mfx_res = va_to_mfx_status(va_res);
402 }
403 if (MFX_ERR_NONE == mfx_res) {
404 switch (vaapi_mid->m_image.format.fourcc) {
405 case VA_FOURCC_NV12:
406 if (vaapi_mid->m_fourcc == MFX_FOURCC_NV12) {
407 ptr->Pitch =
408 (mfxU16) vaapi_mid->
409 m_image.pitches[0];
410 ptr->Y =
411 pBuffer +
412 vaapi_mid->m_image.offsets[0];
413 ptr->U =
414 pBuffer +
415 vaapi_mid->m_image.offsets[1];
416 ptr->V = ptr->U + 1;
417 } else
418 mfx_res = MFX_ERR_LOCK_MEMORY;
419 break;
420 case VA_FOURCC_YV12:
421 if (vaapi_mid->m_fourcc == MFX_FOURCC_YV12) {
422 ptr->Pitch =
423 (mfxU16) vaapi_mid->
424 m_image.pitches[0];
425 ptr->Y =
426 pBuffer +
427 vaapi_mid->m_image.offsets[0];
428 ptr->V =
429 pBuffer +
430 vaapi_mid->m_image.offsets[1];
431 ptr->U =
432 pBuffer +
433 vaapi_mid->m_image.offsets[2];
434 } else
435 mfx_res = MFX_ERR_LOCK_MEMORY;
436 break;
437 case VA_FOURCC_YUY2:
438 if (vaapi_mid->m_fourcc == MFX_FOURCC_YUY2) {
439 ptr->Pitch =
440 (mfxU16) vaapi_mid->
441 m_image.pitches[0];
442 ptr->Y =
443 pBuffer +
444 vaapi_mid->m_image.offsets[0];
445 ptr->U = ptr->Y + 1;
446 ptr->V = ptr->Y + 3;
447 } else
448 mfx_res = MFX_ERR_LOCK_MEMORY;
449 break;
450 case VA_FOURCC_ARGB:
451 if (vaapi_mid->m_fourcc == MFX_FOURCC_RGB4) {
452 ptr->Pitch =
453 (mfxU16) vaapi_mid->
454 m_image.pitches[0];
455 ptr->B =
456 pBuffer +
457 vaapi_mid->m_image.offsets[0];
458 ptr->G = ptr->B + 1;
459 ptr->R = ptr->B + 2;
460 ptr->A = ptr->B + 3;
461 } else
462 mfx_res = MFX_ERR_LOCK_MEMORY;
463 break;
464 default:
465 mfx_res = MFX_ERR_LOCK_MEMORY;
466 break;
467 }
468 }
469 }
470 return mfx_res;
471 }
472
simple_unlock(mfxHDL pthis,mfxMemId mid,mfxFrameData * ptr)473 mfxStatus simple_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData* ptr)
474 {
475 vaapiMemId* vaapi_mid = (vaapiMemId*) mid;
476
477 if (!vaapi_mid || !(vaapi_mid->m_surface))
478 return MFX_ERR_INVALID_HANDLE;
479
480 if (MFX_FOURCC_P8 == vaapi_mid->m_fourcc) { // bitstream processing
481 vaUnmapBuffer(m_va_dpy, *(vaapi_mid->m_surface));
482 } else { // Image processing
483 vaUnmapBuffer(m_va_dpy, vaapi_mid->m_image.buf);
484 vaDestroyImage(m_va_dpy, vaapi_mid->m_image.image_id);
485
486 if (NULL != ptr) {
487 ptr->Pitch = 0;
488 ptr->Y = NULL;
489 ptr->U = NULL;
490 ptr->V = NULL;
491 ptr->A = NULL;
492 }
493 }
494 return MFX_ERR_NONE;
495 }
496
simple_gethdl(mfxHDL pthis,mfxMemId mid,mfxHDL * handle)497 mfxStatus simple_gethdl(mfxHDL pthis, mfxMemId mid, mfxHDL* handle)
498 {
499 vaapiMemId* vaapi_mid = (vaapiMemId*) mid;
500
501 if (!handle || !vaapi_mid || !(vaapi_mid->m_surface))
502 return MFX_ERR_INVALID_HANDLE;
503
504 *handle = vaapi_mid->m_surface; //VASurfaceID* <-> mfxHDL
505 return MFX_ERR_NONE;
506 }
507
_simple_free(mfxHDL pthis,mfxFrameAllocResponse * response)508 mfxStatus _simple_free(mfxHDL pthis, mfxFrameAllocResponse* response)
509 {
510 vaapiMemId* vaapi_mids = NULL;
511 VASurfaceID* surfaces = NULL;
512 mfxU32 i = 0;
513 bool isBitstreamMemory = false;
514 bool actualFreeMemory = false;
515
516 if (0 == memcmp(response, &(allocDecodeResponses[pthis].mfxResponse),
517 sizeof(*response))) {
518 // Decode free response handling
519 allocDecodeResponses[pthis].refCount--;
520 if (0 == allocDecodeResponses[pthis].refCount)
521 actualFreeMemory = true;
522 } else {
523 // Encode and VPP free response handling
524 actualFreeMemory = true;
525 }
526
527 if (actualFreeMemory) {
528 if (response->mids) {
529
530 vaapi_mids = (vaapiMemId*) (response->mids[0]);
531
532 isBitstreamMemory =
533 (MFX_FOURCC_P8 ==
534 vaapi_mids->m_fourcc) ? true : false;
535 surfaces = vaapi_mids->m_surface;
536 for (i = 0; i < response->NumFrameActual; ++i) {
537 if (MFX_FOURCC_P8 == vaapi_mids[i].m_fourcc)
538 vaDestroyBuffer(m_va_dpy, surfaces[i]);
539 else if (vaapi_mids[i].m_sys_buffer)
540 free(vaapi_mids[i].m_sys_buffer);
541 }
542
543 free(vaapi_mids);
544 free(response->mids);
545 response->mids = NULL;
546
547 if (!isBitstreamMemory)
548 vaDestroySurfaces(m_va_dpy, surfaces, response->NumFrameActual);
549 free(surfaces);
550 }
551 response->NumFrameActual = 0;
552 }
553 return MFX_ERR_NONE;
554 }
555
simple_free(mfxHDL pthis,mfxFrameAllocResponse * response)556 mfxStatus simple_free(mfxHDL pthis, mfxFrameAllocResponse* response)
557 {
558 if (!response) return MFX_ERR_NULL_PTR;
559
560 if (allocResponses.find(response->mids) == allocResponses.end()) {
561 // Decode free response handling
562 if (--allocDecodeResponses[pthis].refCount == 0) {
563 _simple_free(pthis,response);
564 allocDecodeResponses.erase(pthis);
565 }
566 } else {
567 // Encode and VPP free response handling
568 allocResponses.erase(response->mids);
569 _simple_free(pthis,response);
570 }
571
572 return MFX_ERR_NONE;
573 }
574