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