void test() { VAEntrypoint entrypoints[5]; int num_entrypoints,slice_entrypoint; VAConfigAttrib attrib[2]; VAConfigID config_id; unsigned int fourcc, luma_stride, chroma_u_stride, chroma_v_stride, luma_offset, chroma_u_offset; unsigned int chroma_v_offset, buffer_name; unsigned int *p_buffer; int i, ret; char c; int frame_width=640, frame_height=480; VASurfaceID surface_id; VAImage image_id; unsigned char *usrbuf; usrbuf = (unsigned char*)malloc(frame_width * frame_height * 2); ASSERT ( usrbuf != NULL); VASurfaceAttribExternalBuffers vaSurfaceExternBuf; VASurfaceAttrib attrib_list[2]; va_status = vaQueryConfigEntrypoints(va_dpy, VAProfileH264Baseline, entrypoints, &num_entrypoints); ASSERT( VA_STATUS_SUCCESS == va_status ); for (slice_entrypoint = 0; slice_entrypoint < num_entrypoints; slice_entrypoint++) { if (entrypoints[slice_entrypoint] == VAEntrypointEncSlice) break; } if (slice_entrypoint == num_entrypoints) { /* not find Slice entry point */ status("VAEntrypointEncSlice doesn't support, exit.\n"); ASSERT(0); } /* find out the format for the render target, and rate control mode */ attrib[0].type = VAConfigAttribRTFormat; attrib[1].type = VAConfigAttribRateControl; va_status = vaGetConfigAttributes(va_dpy, VAProfileH264Baseline, VAEntrypointEncSlice, &attrib[0], 2); ASSERT( VA_STATUS_SUCCESS == va_status ); if ((attrib[0].value & VA_RT_FORMAT_YUV420) == 0) { /* not find desired YUV420 RT format */ status("VA_RT_FORMAT_YUV420 doesn't support, exit\n"); ASSERT(0); } if ((attrib[1].value & VA_RC_VBR) == 0) { /* Can't find matched RC mode */ status("VBR mode doesn't found, exit\n"); ASSERT(0); } attrib[0].value = VA_RT_FORMAT_YUV420; /* set to desired RT format */ attrib[1].value = VA_RC_VBR; /* set to desired RC mode */ va_status = vaCreateConfig(va_dpy, VAProfileH264Baseline, VAEntrypointEncSlice, &attrib[0], 2, &config_id); ASSERT( VA_STATUS_SUCCESS == va_status ); attrib_list[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor; attrib_list[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType; va_status = vaGetSurfaceAttributes(va_dpy, config_id, attrib_list, 2); ASSERT( VA_STATUS_SUCCESS == va_status ); if (attrib_list[0].flags != VA_SURFACE_ATTRIB_NOT_SUPPORTED) { status("supported memory type:\n"); if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_VA) status("\tVA_SURFACE_ATTRIB_MEM_TYPE_VA\n"); if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_V4L2) status("\tVA_SURFACE_ATTRIB_MEM_TYPE_V4L2\n"); if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR) status("\tVA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR\n"); if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC) status("\tVA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC\n"); if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_ION) status("\tVA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_ION\n"); if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM) status("\tVA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM\n"); } if ((attrib_list[1].flags != VA_SURFACE_ATTRIB_NOT_SUPPORTED) && (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR)) { status("vaCreateSurfaces from external usr pointer\n"); vaSurfaceExternBuf.buffers = (unsigned long*)malloc(sizeof(unsigned int)); vaSurfaceExternBuf.buffers[0] = usrbuf; vaSurfaceExternBuf.num_buffers = 1; vaSurfaceExternBuf.width = frame_width; vaSurfaceExternBuf.height = frame_height; vaSurfaceExternBuf.pitches[0] = vaSurfaceExternBuf.pitches[1] = vaSurfaceExternBuf.pitches[2] = frame_width; //vaSurfaceExternBuf.flags = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC; vaSurfaceExternBuf.pixel_format = VA_FOURCC_NV12; //vaSurfaceExternBuf.pitches[0] = attribute_tpi->luma_stride; attrib_list[1].flags = VA_SURFACE_ATTRIB_SETTABLE; attrib_list[1].value.type = VAGenericValueTypePointer; attrib_list[1].value.value.p = (void *)&vaSurfaceExternBuf; attrib_list[0].flags = VA_SURFACE_ATTRIB_SETTABLE; attrib_list[0].value.type = VAGenericValueTypeInteger; attrib_list[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR; va_status = vaCreateSurfaces(va_dpy, VA_RT_FORMAT_YUV420, frame_width, frame_height, &surface_id, 1, attrib_list, 2); ASSERT( VA_STATUS_SUCCESS == va_status ); va_status = vaDeriveImage(va_dpy, surface_id, &image_id); ASSERT( VA_STATUS_SUCCESS == va_status ); va_status = vaMapBuffer(va_dpy, image_id.buf, (void**)&p_buffer); ASSERT( VA_STATUS_SUCCESS == va_status ); memset(p_buffer, 0x80, image_id.width * image_id.height); va_status = vaUnmapBuffer(va_dpy, image_id.buf); ASSERT( VA_STATUS_SUCCESS == va_status ); va_status = vaDestroyImage(va_dpy, image_id.image_id); ASSERT( VA_STATUS_SUCCESS == va_status ); va_status = vaDestroySurfaces(va_dpy, &surface_id, 1); ASSERT( VA_STATUS_SUCCESS == va_status ); } va_status = vaDestroyConfig(va_dpy, config_id); ASSERT( VA_STATUS_SUCCESS == va_status ); free(usrbuf); }
ImageRawPtr VaapiImageRaw::create(const DisplayPtr& display, const ImagePtr& image, VideoDataMemoryType memoryType) { ImageRawPtr raw; RealeaseCallback release; uintptr_t handle; VAStatus status; VAImagePtr& vaImage = image->m_image; if (memoryType == VIDEO_DATA_MEMORY_TYPE_RAW_POINTER || memoryType == VIDEO_DATA_MEMORY_TYPE_RAW_COPY) { void* data; status = vaMapBuffer(display->getID(), vaImage->buf, &data); release = vaUnmapBuffer; handle = (uintptr_t)data; } else { VABufferInfo bufferInfo; if (memoryType == VIDEO_DATA_MEMORY_TYPE_DRM_NAME) bufferInfo.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM; else if (memoryType == VIDEO_DATA_MEMORY_TYPE_DMA_BUF) bufferInfo.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; else ASSERT(0); status = vaAcquireBufferHandle(display->getID(), vaImage->buf, &bufferInfo); release = vaReleaseBufferHandle; handle = (uintptr_t)bufferInfo.handle; } if (!checkVaapiStatus(status, "VaapiImageRaw::create()")) return raw; raw.reset(new VaapiImageRaw(display, image, memoryType, handle, release)); return raw; }
/* Maps VA buffer */ gpointer vaapi_map_buffer (VADisplay dpy, VABufferID buf_id) { VAStatus status; gpointer data = NULL; status = vaMapBuffer (dpy, buf_id, &data); if (!vaapi_check_status (status, "vaMapBuffer()")) return NULL; return data; }
static void *alloc_buffer(struct vaapi_context *vactx, int type, unsigned int size, uint32_t *buf_id) { void *data = NULL; *buf_id = 0; if (vaCreateBuffer(vactx->display, vactx->context_id, type, size, 1, NULL, buf_id) == VA_STATUS_SUCCESS) vaMapBuffer(vactx->display, *buf_id, &data); return data; }
void VAApiWriter::draw( VASurfaceID _id, int _field ) { if ( _id != VA_INVALID_SURFACE && _field > -1 ) { if ( id != _id || _field == field ) vaSyncSurface( VADisp, _id ); id = _id; field = _field; } if ( id == VA_INVALID_SURFACE ) return; bool associated = false; osd_mutex.lock(); if ( !osd_list.isEmpty() ) { QRect bounds; const qreal scaleW = ( qreal )W / outW, scaleH = ( qreal )H / outH; bool mustRepaint = Functions::mustRepaintOSD( osd_list, osd_checksums, &scaleW, &scaleH, &bounds ); if ( !mustRepaint ) mustRepaint = vaImgSize != bounds.size(); bool canAssociate = !mustRepaint; if ( mustRepaint ) { if ( vaImgSize != bounds.size() ) { clearRGBImage(); vaImgSize = QSize(); if ( vaCreateImage( VADisp, rgbImgFmt, bounds.width(), bounds.height(), &vaImg ) == VA_STATUS_SUCCESS ) { if ( vaCreateSubpicture( VADisp, vaImg.image_id, &vaSubpicID ) == VA_STATUS_SUCCESS ) vaImgSize = bounds.size(); else clearRGBImage(); } } if ( vaSubpicID ) { quint8 *buff; if ( vaMapBuffer( VADisp, vaImg.buf, ( void ** )&buff ) == VA_STATUS_SUCCESS ) { QImage osdImg( buff += vaImg.offsets[ 0 ], vaImg.pitches[ 0 ] >> 2, bounds.height(), QImage::Format_ARGB32 ); osdImg.fill( 0 ); QPainter p( &osdImg ); p.translate( -bounds.topLeft() ); Functions::paintOSD( osd_list, scaleW, scaleH, p, &osd_checksums ); vaUnmapBuffer( VADisp, vaImg.buf ); canAssociate = true; } } }
/* Maps VA buffer */ void *vaapiMapBuffer(VADisplay dpy, VABufferID bufId) { VAStatus status; void *data = NULL; status = vaMapBuffer(dpy, bufId, &data); if (!checkVaapiStatus(status, "vaMapBuffer()")) { ERROR("fail to map bufId = %x", bufId); return NULL; } return data; }
quint8 *VAApiWriter::getImage( VAImage &image, VASurfaceID surfaceID, VAImageFormat *img_fmt ) const { if ( vaCreateImage( VADisp, img_fmt, outW, outH, &image ) == VA_STATUS_SUCCESS ) { quint8 *data; if ( vaSyncSurface( VADisp, surfaceID ) == VA_STATUS_SUCCESS && vaGetImage( VADisp, surfaceID, 0, 0, outW, outH, image.image_id ) == VA_STATUS_SUCCESS && vaMapBuffer( VADisp, image.buf, ( void ** )&data ) == VA_STATUS_SUCCESS ) return data; vaDestroyImage( VADisp, image.image_id ); } return NULL; }
VaapiImageRaw *VaapiImage::map(VideoDataMemoryType memoryType) { uint32_t i; void *data; VAStatus status; if (m_isMapped) { return &m_rawImage; } m_rawImage.format = m_image->format.fourcc; m_rawImage.width = m_image->width; m_rawImage.height = m_image->height; m_rawImage.numPlanes = m_image->num_planes; m_rawImage.size = m_image->data_size; for (i = 0; i < m_image->num_planes; i++) { m_rawImage.strides[i] = m_image->pitches[i]; } if (memoryType == VIDEO_DATA_MEMORY_TYPE_RAW_POINTER || memoryType == VIDEO_DATA_MEMORY_TYPE_RAW_COPY) { status = vaMapBuffer(m_display->getID(), m_image->buf, &data); if (!checkVaapiStatus(status, "vaMapBuffer()")) return NULL; for (i = 0; i < m_image->num_planes; i++) { m_rawImage.handles[i] = (intptr_t) data + m_image->offsets[i]; } } else { VABufferInfo bufferInfo; if (memoryType == VIDEO_DATA_MEMORY_TYPE_DRM_NAME) bufferInfo.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM; else if (memoryType == VIDEO_DATA_MEMORY_TYPE_DMA_BUF) bufferInfo.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; else ASSERT(0); status = vaAcquireBufferHandle(m_display->getID(), m_image->buf, &bufferInfo); if (!checkVaapiStatus(status, "vaAcquireBufferHandle()")) return NULL; for (i = 0; i < m_image->num_planes; i++) { m_rawImage.handles[i] = (intptr_t) bufferInfo.handle; } } m_rawImage.memoryType = memoryType; m_isMapped = true; return &m_rawImage; }
static void *alloc_buffer(VAAPIContext *vaapi, int type, unsigned int size, VABufferID *buf_id) { VAStatus status; void *data = NULL; *buf_id = 0; status = vaCreateBuffer(vaapi->display, vaapi->context_id, type, size, 1, NULL, buf_id); if (!vaapi_check_status(status, "vaCreateBuffer()")) return NULL; status = vaMapBuffer(vaapi->display, *buf_id, &data); if (!vaapi_check_status(status, "vaMapBuffer()")) return NULL; return data; }
Encode_Status VideoEncoderH263::renderSliceParams(EncodeTask *task) { VAStatus vaStatus = VA_STATUS_SUCCESS; uint32_t sliceHeight; uint32_t sliceHeightInMB; LOG_V("Begin\n\n"); sliceHeight = mComParams.resolution.height; sliceHeight += 15; sliceHeight &= (~15); sliceHeightInMB = sliceHeight / 16; vaStatus = vaCreateBuffer( mVADisplay, mVAContext, VAEncSliceParameterBufferType, sizeof(VAEncSliceParameterBuffer), 1, NULL, &mSliceParamBuf); CHECK_VA_STATUS_RETURN("vaCreateBuffer"); VAEncSliceParameterBuffer *sliceParams; vaStatus = vaMapBuffer(mVADisplay, mSliceParamBuf, (void **)&sliceParams); CHECK_VA_STATUS_RETURN("vaMapBuffer"); // starting MB row number for this slice sliceParams->start_row_number = 0; // slice height measured in MB sliceParams->slice_height = sliceHeightInMB; sliceParams->slice_flags.bits.is_intra = (task->type == FTYPE_I)?1:0; sliceParams->slice_flags.bits.disable_deblocking_filter_idc = 0; LOG_V("======h263 slice params======\n"); LOG_I("start_row_number = %d\n", (int) sliceParams->start_row_number); LOG_I("slice_height_in_mb = %d\n", (int) sliceParams->slice_height); LOG_I("slice.is_intra = %d\n", (int) sliceParams->slice_flags.bits.is_intra); vaStatus = vaUnmapBuffer(mVADisplay, mSliceParamBuf); CHECK_VA_STATUS_RETURN("vaUnmapBuffer"); vaStatus = vaRenderPicture(mVADisplay, mVAContext, &mSliceParamBuf, 1); CHECK_VA_STATUS_RETURN("vaRenderPicture"); LOG_V("end\n"); return ENCODE_SUCCESS; }
gboolean _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image) { GstVaapiImagePrivate * const priv = image->priv; GstVaapiDisplay *display; void *image_data; VAStatus status; guint i; if (_gst_vaapi_image_is_mapped(image)) return TRUE; display = GST_VAAPI_OBJECT_DISPLAY(image); if (!display) return FALSE; GST_VAAPI_DISPLAY_LOCK(display); status = vaMapBuffer( GST_VAAPI_DISPLAY_VADISPLAY(display), image->priv->image.buf, &image_data ); GST_VAAPI_DISPLAY_UNLOCK(display); if (!vaapi_check_status(status, "vaMapBuffer()")) return FALSE; image->priv->image_data = image_data; if (raw_image) { const VAImage * const va_image = &priv->image; raw_image->format = priv->format; raw_image->width = va_image->width; raw_image->height = va_image->height; raw_image->num_planes = va_image->num_planes; for (i = 0; i < raw_image->num_planes; i++) { raw_image->pixels[i] = (guchar *)image_data + va_image->offsets[i]; raw_image->stride[i] = va_image->pitches[i]; } } return TRUE; }
static int bind_image(VAImage *va_image, Image *image) { VAAPIContext * const vaapi = vaapi_get_context(); VAImageFormat * const va_format = &va_image->format; VAStatus status; void *va_image_data; unsigned int i; if (va_image->num_planes > MAX_IMAGE_PLANES) return -1; status = vaMapBuffer(vaapi->display, va_image->buf, &va_image_data); if (!vaapi_check_status(status, "vaMapBuffer()")) return -1; memset(image, 0, sizeof(*image)); image->format = va_format->fourcc; if (is_vaapi_rgb_format(va_format)) { image->format = image_rgba_format( va_format->bits_per_pixel, va_format->byte_order == VA_MSB_FIRST, va_format->red_mask, va_format->green_mask, va_format->blue_mask, va_format->alpha_mask ); if (!image->format) return -1; } image->width = va_image->width; image->height = va_image->height; image->num_planes = va_image->num_planes; for (i = 0; i < va_image->num_planes; i++) { image->pixels[i] = (uint8_t *)va_image_data + va_image->offsets[i]; image->pitches[i] = va_image->pitches[i]; } return 0; }
void* SurfaceInteropVAAPI::mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); VAImage image; static const unsigned int fcc[] = { VA_FOURCC_NV12, VA_FOURCC_YV12, VA_FOURCC_IYUV, 0}; va_new_image(m_surface->vadisplay(), fcc, &image, m_surface->width(), m_surface->height()); if (image.image_id == VA_INVALID_ID) return NULL; void *p_base; VA_ENSURE(vaGetImage(m_surface->vadisplay(), m_surface->get(), 0, 0, m_surface->width(), m_surface->height(), image.image_id), NULL); VA_ENSURE(vaMapBuffer(m_surface->vadisplay(), image.buf, &p_base), NULL); //TODO: destroy image before return VideoFormat::PixelFormat pixfmt = pixelFormatFromVA(image.format.fourcc); bool swap_uv = image.format.fourcc != VA_FOURCC_NV12; if (pixfmt == VideoFormat::Format_Invalid) { qWarning("unsupported vaapi pixel format: %#x", image.format.fourcc); VA_ENSURE(vaDestroyImage(m_surface->vadisplay(), image.image_id), NULL); return NULL; } const VideoFormat fmt(pixfmt); uint8_t *src[3]; int pitch[3]; for (int i = 0; i < fmt.planeCount(); ++i) { src[i] = (uint8_t*)p_base + image.offsets[i]; pitch[i] = image.pitches[i]; } VideoFrame frame = VideoFrame::fromGPU(fmt, frame_width, frame_height, m_surface->height(), src, pitch, true, swap_uv); if (format != fmt) frame = frame.to(format); VAWARN(vaUnmapBuffer(m_surface->vadisplay(), image.buf)); VAWARN(vaDestroyImage(m_surface->vadisplay(), image.image_id)); image.image_id = VA_INVALID_ID; VideoFrame *f = reinterpret_cast<VideoFrame*>(handle); frame.setTimestamp(f->timestamp()); *f = frame; return f; }
static bool map(VABufferID id, T *&buf) { return id != VA_INVALID_ID && vaMapBuffer(VaApi::glx(), id, (void**)&buf) == VA_STATUS_SUCCESS; }
mfxStatus vaapiFrameAllocator::LockFrame(mfxMemId mid, mfxFrameData *ptr) { mfxStatus mfx_res = MFX_ERR_NONE; VAStatus va_res = VA_STATUS_SUCCESS; vaapiMemId* vaapi_mid = (vaapiMemId*)mid; mfxU8* pBuffer = 0; if (!vaapi_mid || !(vaapi_mid->m_surface)) return MFX_ERR_INVALID_HANDLE; if (MFX_FOURCC_P8 == vaapi_mid->m_fourcc) // bitstream processing { VACodedBufferSegment *coded_buffer_segment; va_res = vaMapBuffer(m_dpy, *(vaapi_mid->m_surface), (void **)(&coded_buffer_segment)); mfx_res = va_to_mfx_status(va_res); ptr->Y = (mfxU8*)coded_buffer_segment->buf; // !!! bug } else // Image processing { va_res = vaDeriveImage(m_dpy, *(vaapi_mid->m_surface), &(vaapi_mid->m_image)); mfx_res = va_to_mfx_status(va_res); if (MFX_ERR_NONE == mfx_res) { va_res = vaMapBuffer(m_dpy, vaapi_mid->m_image.buf, (void **) &pBuffer); mfx_res = va_to_mfx_status(va_res); } if (MFX_ERR_NONE == mfx_res) { switch (vaapi_mid->m_image.format.fourcc) { case VA_FOURCC_NV12: if (vaapi_mid->m_fourcc == MFX_FOURCC_NV12) { ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0]; ptr->U = pBuffer + vaapi_mid->m_image.offsets[1]; ptr->V = ptr->U + 1; } else mfx_res = MFX_ERR_LOCK_MEMORY; break; case VA_FOURCC_YV12: if (vaapi_mid->m_fourcc == MFX_FOURCC_YV12) { ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0]; ptr->V = pBuffer + vaapi_mid->m_image.offsets[1]; ptr->U = pBuffer + vaapi_mid->m_image.offsets[2]; } else mfx_res = MFX_ERR_LOCK_MEMORY; break; case VA_FOURCC_YUY2: if (vaapi_mid->m_fourcc == MFX_FOURCC_YUY2) { ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0]; ptr->U = ptr->Y + 1; ptr->V = ptr->Y + 3; } else mfx_res = MFX_ERR_LOCK_MEMORY; break; case VA_FOURCC_ARGB: if (vaapi_mid->m_fourcc == MFX_FOURCC_RGB4) { ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0]; ptr->B = pBuffer + vaapi_mid->m_image.offsets[0]; ptr->G = ptr->B + 1; ptr->R = ptr->B + 2; ptr->A = ptr->B + 3; } else mfx_res = MFX_ERR_LOCK_MEMORY; break; default: mfx_res = MFX_ERR_LOCK_MEMORY; break; } } } return mfx_res; }
bool VAAPIContext::CopySurfaceToFrame(VideoFrame *frame, const void *buf) { MythXLocker locker(m_display->m_x_disp); if (!m_deriveSupport && m_image.image_id == VA_INVALID_ID) InitImage(buf); if (!frame || !buf || (m_dispType != kVADisplayX11) || (!m_deriveSupport && m_image.image_id == VA_INVALID_ID)) return false; const vaapi_surface *surf = (vaapi_surface*)buf; INIT_ST; va_status = vaSyncSurface(m_ctx.display, surf->m_id); CHECK_ST; if (m_deriveSupport) { va_status = vaDeriveImage(m_ctx.display, surf->m_id, &m_image); } else { va_status = vaGetImage(m_ctx.display, surf->m_id, 0, 0, m_size.width(), m_size.height(), m_image.image_id); } CHECK_ST; if (ok) { VideoFrame src; void* source = NULL; if (vaMapBuffer(m_ctx.display, m_image.buf, &source)) return false; if (m_image.format.fourcc == VA_FOURCC_NV12) { init(&src, FMT_NV12, (unsigned char*)source, m_image.width, m_image.height, m_image.data_size, NULL, NULL, frame->aspect, frame->frame_rate); for (int i = 0; i < 2; i++) { src.pitches[i] = m_image.pitches[i]; src.offsets[i] = m_image.offsets[i]; } } else { // Our VideoFrame YV12 format, is really YUV420P/IYUV bool swap = m_image.format.fourcc == VA_FOURCC_YV12; init(&src, FMT_YV12, (unsigned char*)source, m_image.width, m_image.height, m_image.data_size, NULL, NULL, frame->aspect, frame->frame_rate); src.pitches[0] = m_image.pitches[0]; src.pitches[1] = m_image.pitches[swap ? 2 : 1]; src.pitches[2] = m_image.pitches[swap ? 1 : 2]; src.offsets[0] = m_image.offsets[0]; src.offsets[1] = m_image.offsets[swap ? 2 : 1]; src.offsets[2] = m_image.offsets[swap ? 1 : 2]; } m_copy->copy(frame, &src); if (vaUnmapBuffer(m_ctx.display, m_image.buf)) return false; if (m_deriveSupport) { vaDestroyImage(m_ctx.display, m_image.image_id ); m_image.image_id = VA_INVALID_ID; } return true; } LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to get image"); return false; }
static int vaapi_encode_output(AVCodecContext *avctx, VAAPIEncodePicture *pic, AVPacket *pkt) { VAAPIEncodeContext *ctx = avctx->priv_data; VACodedBufferSegment *buf_list, *buf; VAStatus vas; int err; err = vaapi_encode_wait(avctx, pic); if (err < 0) return err; buf_list = NULL; vas = vaMapBuffer(ctx->hwctx->display, pic->output_buffer, (void**)&buf_list); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to map output buffers: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } for (buf = buf_list; buf; buf = buf->next) { av_log(avctx, AV_LOG_DEBUG, "Output buffer: %u bytes " "(status %08x).\n", buf->size, buf->status); err = av_new_packet(pkt, buf->size); if (err < 0) goto fail; memcpy(pkt->data, buf->buf, buf->size); } if (pic->type == PICTURE_TYPE_IDR) pkt->flags |= AV_PKT_FLAG_KEY; pkt->pts = pic->pts; vas = vaUnmapBuffer(ctx->hwctx->display, pic->output_buffer); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to unmap output buffers: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } vaDestroyBuffer(ctx->hwctx->display, pic->output_buffer); pic->output_buffer = VA_INVALID_ID; av_log(avctx, AV_LOG_DEBUG, "Output read for pic %"PRId64"/%"PRId64".\n", pic->display_order, pic->encode_order); return 0; fail: if (pic->output_buffer != VA_INVALID_ID) { vaUnmapBuffer(ctx->hwctx->display, pic->output_buffer); vaDestroyBuffer(ctx->hwctx->display, pic->output_buffer); pic->output_buffer = VA_INVALID_ID; } return err; }
static int Extract( vlc_va_t *va, picture_t *p_picture, uint8_t *data ) { vlc_va_sys_t *sys = va->sys; VASurfaceID surface = (VASurfaceID)(uintptr_t)data; VAImage image; int ret = VLC_EGENERIC; #if VA_CHECK_VERSION(0,31,0) if (vaSyncSurface(sys->hw_ctx.display, surface)) #else if (vaSyncSurface(sys->hw_ctx.display, sys->hw_ctx.context_id, surface)) #endif return VLC_EGENERIC; if (!sys->do_derive || vaDeriveImage(sys->hw_ctx.display, surface, &image)) { /* Fallback if image derivation is not supported */ if (vaCreateImage(sys->hw_ctx.display, &sys->format, sys->width, sys->height, &image)) return VLC_EGENERIC; if (vaGetImage(sys->hw_ctx.display, surface, 0, 0, sys->width, sys->height, image.image_id)) goto error; } void *p_base; if (vaMapBuffer(sys->hw_ctx.display, image.buf, &p_base)) goto error; const unsigned i_fourcc = sys->format.fourcc; if( i_fourcc == VA_FOURCC_YV12 || i_fourcc == VA_FOURCC_IYUV ) { bool b_swap_uv = i_fourcc == VA_FOURCC_IYUV; uint8_t *pp_plane[3]; size_t pi_pitch[3]; for( int i = 0; i < 3; i++ ) { const int i_src_plane = (b_swap_uv && i != 0) ? (3 - i) : i; pp_plane[i] = (uint8_t*)p_base + image.offsets[i_src_plane]; pi_pitch[i] = image.pitches[i_src_plane]; } CopyFromYv12( p_picture, pp_plane, pi_pitch, sys->width, sys->height, &sys->image_cache ); } else { assert( i_fourcc == VA_FOURCC_NV12 ); uint8_t *pp_plane[2]; size_t pi_pitch[2]; for( int i = 0; i < 2; i++ ) { pp_plane[i] = (uint8_t*)p_base + image.offsets[i]; pi_pitch[i] = image.pitches[i]; } CopyFromNv12( p_picture, pp_plane, pi_pitch, sys->width, sys->height, &sys->image_cache ); } vaUnmapBuffer(sys->hw_ctx.display, image.buf); ret = VLC_SUCCESS; error: vaDestroyImage(sys->hw_ctx.display, image.image_id); return ret; }
VideoFrame VideoDecoderVAAPI::frame() { DPTR_D(VideoDecoderVAAPI); if (!d.frame->opaque || !d.frame->data[0]) return VideoFrame(); VASurfaceID surface_id = (VASurfaceID)(uintptr_t)d.frame->data[3]; VAStatus status = VA_STATUS_SUCCESS; if (display() == GLX || (copyMode() == ZeroCopy && display() == X11)) { surface_ptr p; std::list<surface_ptr>::iterator it = d.surfaces_used.begin(); for (; it != d.surfaces_used.end() && !p; ++it) { if((*it)->get() == surface_id) { p = *it; break; } } if (!p) { for (it = d.surfaces_free.begin(); it != d.surfaces_free.end() && !p; ++it) { if((*it)->get() == surface_id) { p = *it; break; } } } if (!p) { qWarning("VAAPI - Unable to find surface"); return VideoFrame(); } ((SurfaceInteropVAAPI*)d.surface_interop.data())->setSurface(p); VideoFrame f(d.width, d.height, VideoFormat::Format_RGB32); //p->width() f.setBytesPerLine(d.width*4); //used by gl to compute texture size f.setMetaData("surface_interop", QVariant::fromValue(d.surface_interop)); f.setTimestamp(double(d.frame->pkt_pts)/1000.0); return f; } #if VA_CHECK_VERSION(0,31,0) if ((status = vaSyncSurface(d.display->get(), surface_id)) != VA_STATUS_SUCCESS) { qWarning("vaSyncSurface(VADisplay:%p, VASurfaceID:%#x) == %#x", d.display->get(), surface_id, status); #else if (vaSyncSurface(d.display->get(), d.context_id, surface_id)) { qWarning("vaSyncSurface(VADisplay:%#x, VAContextID:%#x, VASurfaceID:%#x) == %#x", d.display, d.context_id, surface_id, status); #endif return VideoFrame(); } if (!d.disable_derive && d.supports_derive) { /* * http://web.archiveorange.com/archive/v/OAywENyq88L319OcRnHI * vaDeriveImage is faster than vaGetImage. But VAImage is uncached memory and copying from it would be terribly slow * TODO: copy from USWC, see vlc and https://github.com/OpenELEC/OpenELEC.tv/pull/2937.diff * https://software.intel.com/en-us/articles/increasing-memory-throughput-with-intel-streaming-simd-extensions-4-intel-sse4-streaming-load */ VA_ENSURE_TRUE(vaDeriveImage(d.display->get(), surface_id, &d.image), VideoFrame()); } else { VA_ENSURE_TRUE(vaGetImage(d.display->get(), surface_id, 0, 0, d.width, d.height, d.image.image_id), VideoFrame()); } void *p_base; VA_ENSURE_TRUE(vaMapBuffer(d.display->get(), d.image.buf, &p_base), VideoFrame()); VideoFormat::PixelFormat pixfmt = VideoFormat::Format_Invalid; bool swap_uv = false; switch (d.image.format.fourcc) { case VA_FOURCC_YV12: swap_uv |= d.disable_derive || !d.supports_derive; pixfmt = VideoFormat::Format_YUV420P; break; case VA_FOURCC_IYUV: swap_uv = true; pixfmt = VideoFormat::Format_YUV420P; break; case VA_FOURCC_NV12: pixfmt = VideoFormat::Format_NV12; break; default: break; } if (pixfmt == VideoFormat::Format_Invalid) { qWarning("unsupported vaapi pixel format: %#x", d.image.format.fourcc); return VideoFrame(); } const VideoFormat fmt(pixfmt); uint8_t *src[3]; int pitch[3]; for (int i = 0; i < fmt.planeCount(); ++i) { src[i] = (uint8_t*)p_base + d.image.offsets[i]; pitch[i] = d.image.pitches[i]; } VideoFrame frame(copyToFrame(fmt, d.surface_height, src, pitch, swap_uv)); VAWARN(vaUnmapBuffer(d.display->get(), d.image.buf)); if (!d.disable_derive && d.supports_derive) { vaDestroyImage(d.display->get(), d.image.image_id); d.image.image_id = VA_INVALID_ID; } return frame; } void VideoDecoderVAAPI::setDisplayPriority(const QStringList &priority) { DPTR_D(VideoDecoderVAAPI); d.display_priority.clear(); int idx = staticMetaObject.indexOfEnumerator("DisplayType"); const QMetaEnum me = staticMetaObject.enumerator(idx); foreach (const QString& disp, priority) { d.display_priority.push_back((DisplayType)me.keyToValue(disp.toUtf8().constData())); }
static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) { AVFilterContext *avctx = inlink->dst; AVFilterLink *outlink = avctx->outputs[0]; DeintVAAPIContext *ctx = avctx->priv; AVFrame *output_frame = NULL; VASurfaceID input_surface, output_surface; VASurfaceID backward_references[MAX_REFERENCES]; VASurfaceID forward_references[MAX_REFERENCES]; VAProcPipelineParameterBuffer params; VAProcFilterParameterBufferDeinterlacing *filter_params; VARectangle input_region; VABufferID params_id; VAStatus vas; void *filter_params_addr = NULL; int err, i, field, current_frame_index; av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n", av_get_pix_fmt_name(input_frame->format), input_frame->width, input_frame->height, input_frame->pts); if (ctx->queue_count < ctx->queue_depth) { ctx->frame_queue[ctx->queue_count++] = input_frame; if (ctx->queue_count < ctx->queue_depth) { // Need more reference surfaces before we can continue. return 0; } } else { av_frame_free(&ctx->frame_queue[0]); for (i = 0; i + 1 < ctx->queue_count; i++) ctx->frame_queue[i] = ctx->frame_queue[i + 1]; ctx->frame_queue[i] = input_frame; } current_frame_index = ctx->pipeline_caps.num_forward_references; input_frame = ctx->frame_queue[current_frame_index]; input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3]; for (i = 0; i < ctx->pipeline_caps.num_forward_references; i++) forward_references[i] = (VASurfaceID)(uintptr_t) ctx->frame_queue[current_frame_index - i - 1]->data[3]; for (i = 0; i < ctx->pipeline_caps.num_backward_references; i++) backward_references[i] = (VASurfaceID)(uintptr_t) ctx->frame_queue[current_frame_index + i + 1]->data[3]; av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for " "deinterlace input.\n", input_surface); av_log(avctx, AV_LOG_DEBUG, "Backward references:"); for (i = 0; i < ctx->pipeline_caps.num_backward_references; i++) av_log(avctx, AV_LOG_DEBUG, " %#x", backward_references[i]); av_log(avctx, AV_LOG_DEBUG, "\n"); av_log(avctx, AV_LOG_DEBUG, "Forward references:"); for (i = 0; i < ctx->pipeline_caps.num_forward_references; i++) av_log(avctx, AV_LOG_DEBUG, " %#x", forward_references[i]); av_log(avctx, AV_LOG_DEBUG, "\n"); for (field = 0; field < ctx->field_rate; field++) { output_frame = ff_get_video_buffer(outlink, ctx->output_width, ctx->output_height); if (!output_frame) { err = AVERROR(ENOMEM); goto fail; } output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3]; av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for " "deinterlace output.\n", output_surface); memset(¶ms, 0, sizeof(params)); input_region = (VARectangle) { .x = 0, .y = 0, .width = input_frame->width, .height = input_frame->height, }; params.surface = input_surface; params.surface_region = &input_region; params.surface_color_standard = vaapi_proc_colour_standard(input_frame->colorspace); params.output_region = NULL; params.output_background_color = 0xff000000; params.output_color_standard = params.surface_color_standard; params.pipeline_flags = 0; params.filter_flags = VA_FRAME_PICTURE; if (!ctx->auto_enable || input_frame->interlaced_frame) { vas = vaMapBuffer(ctx->hwctx->display, ctx->filter_buffer, &filter_params_addr); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to map filter parameter " "buffer: %d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } filter_params = filter_params_addr; filter_params->flags = 0; if (input_frame->top_field_first) { filter_params->flags |= field ? VA_DEINTERLACING_BOTTOM_FIELD : 0; } else { filter_params->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST; filter_params->flags |= field ? 0 : VA_DEINTERLACING_BOTTOM_FIELD; } filter_params_addr = NULL; vas = vaUnmapBuffer(ctx->hwctx->display, ctx->filter_buffer); if (vas != VA_STATUS_SUCCESS) av_log(avctx, AV_LOG_ERROR, "Failed to unmap filter parameter " "buffer: %d (%s).\n", vas, vaErrorStr(vas)); params.filters = &ctx->filter_buffer; params.num_filters = 1; params.forward_references = forward_references; params.num_forward_references = ctx->pipeline_caps.num_forward_references; params.backward_references = backward_references; params.num_backward_references = ctx->pipeline_caps.num_backward_references; } else { params.filters = NULL; params.num_filters = 0; } vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context, output_surface); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, VAProcPipelineParameterBufferType, sizeof(params), 1, ¶ms, ¶ms_id); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail_after_begin; } av_log(avctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n", params_id); vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, ¶ms_id, 1); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to render parameter buffer: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail_after_begin; } vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to start picture processing: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail_after_render; } if (ctx->hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) { vas = vaDestroyBuffer(ctx->hwctx->display, params_id); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to free parameter buffer: " "%d (%s).\n", vas, vaErrorStr(vas)); // And ignore. } } err = av_frame_copy_props(output_frame, input_frame); if (err < 0) goto fail; if (ctx->field_rate == 2) { if (field == 0) output_frame->pts = 2 * input_frame->pts; else output_frame->pts = input_frame->pts + ctx->frame_queue[current_frame_index + 1]->pts; } output_frame->interlaced_frame = 0; av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n", av_get_pix_fmt_name(output_frame->format), output_frame->width, output_frame->height, output_frame->pts); err = ff_filter_frame(outlink, output_frame); if (err < 0) break; } return err; fail_after_begin: vaRenderPicture(ctx->hwctx->display, ctx->va_context, ¶ms_id, 1); fail_after_render: vaEndPicture(ctx->hwctx->display, ctx->va_context); fail: if (filter_params_addr) vaUnmapBuffer(ctx->hwctx->display, ctx->filter_buffer); av_frame_free(&output_frame); return err; } static av_cold int deint_vaapi_init(AVFilterContext *avctx) { DeintVAAPIContext *ctx = avctx->priv; ctx->va_config = VA_INVALID_ID; ctx->va_context = VA_INVALID_ID; ctx->filter_buffer = VA_INVALID_ID; ctx->valid_ids = 1; return 0; }
static int vaapi_map_frame(AVHWFramesContext *hwfc, AVFrame *dst, const AVFrame *src, int flags) { AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx; VAAPIFramesContext *ctx = hwfc->internal->priv; VASurfaceID surface_id; VAImageFormat *image_format; VAAPISurfaceMap *map; VAStatus vas; void *address = NULL; int err, i; surface_id = (VASurfaceID)(uintptr_t)src->data[3]; av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id); if (!ctx->derive_works && (flags & VAAPI_MAP_DIRECT)) { // Requested direct mapping but it is not possible. return AVERROR(EINVAL); } if (dst->format == AV_PIX_FMT_NONE) dst->format = hwfc->sw_format; if (dst->format != hwfc->sw_format && (flags & VAAPI_MAP_DIRECT)) { // Requested direct mapping but the formats do not match. return AVERROR(EINVAL); } err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format); if (err < 0) { // Requested format is not a valid output format. return AVERROR(EINVAL); } map = av_malloc(sizeof(VAAPISurfaceMap)); if (!map) return AVERROR(ENOMEM); map->source = src; map->flags = flags; map->image.image_id = VA_INVALID_ID; vas = vaSyncSurface(hwctx->display, surface_id); if (vas != VA_STATUS_SUCCESS) { av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface " "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } // The memory which we map using derive need not be connected to the CPU // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the // memory is mappable but not cached, so normal memcpy()-like access is // very slow to read it (but writing is ok). It is possible to read much // faster with a copy routine which is aware of the limitation, but we // assume for now that the user is not aware of that and would therefore // prefer not to be given direct-mapped memory if they request read access. if (ctx->derive_works && ((flags & VAAPI_MAP_DIRECT) || !(flags & VAAPI_MAP_READ))) { vas = vaDeriveImage(hwctx->display, surface_id, &map->image); if (vas != VA_STATUS_SUCCESS) { av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from " "surface %#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } if (map->image.format.fourcc != image_format->fourcc) { av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x " "is in wrong format: expected %#08x, got %#08x.\n", surface_id, image_format->fourcc, map->image.format.fourcc); err = AVERROR(EIO); goto fail; } map->flags |= VAAPI_MAP_DIRECT; } else { vas = vaCreateImage(hwctx->display, image_format, hwfc->width, hwfc->height, &map->image); if (vas != VA_STATUS_SUCCESS) { av_log(hwfc, AV_LOG_ERROR, "Failed to create image for " "surface %#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } if (flags & VAAPI_MAP_READ) { vas = vaGetImage(hwctx->display, surface_id, 0, 0, hwfc->width, hwfc->height, map->image.image_id); if (vas != VA_STATUS_SUCCESS) { av_log(hwfc, AV_LOG_ERROR, "Failed to read image from " "surface %#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } } } vas = vaMapBuffer(hwctx->display, map->image.buf, &address); if (vas != VA_STATUS_SUCCESS) { av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface " "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail; } dst->width = src->width; dst->height = src->height; for (i = 0; i < map->image.num_planes; i++) { dst->data[i] = (uint8_t*)address + map->image.offsets[i]; dst->linesize[i] = map->image.pitches[i]; } if ( #ifdef VA_FOURCC_YV16 map->image.format.fourcc == VA_FOURCC_YV16 || #endif map->image.format.fourcc == VA_FOURCC_YV12) { // Chroma planes are YVU rather than YUV, so swap them. FFSWAP(uint8_t*, dst->data[1], dst->data[2]); }
qint64 VAApiWriter::write( const QByteArray &data ) { VideoFrame *videoFrame = ( VideoFrame * )data.data(); const VASurfaceID curr_id = ( unsigned long )videoFrame->data[ 3 ]; const int field = FFCommon::getField( videoFrame, deinterlace, 0, VA_TOP_FIELD, VA_BOTTOM_FIELD ); #ifdef HAVE_VPP if ( use_vpp ) { const bool do_vpp_deint = field != 0 && vpp_deint != VA_INVALID_ID; bool vpp_ok = false; if ( !do_vpp_deint ) { forward_reference = VA_INVALID_SURFACE; vpp_second = false; } if ( do_vpp_deint && forward_reference == VA_INVALID_SURFACE ) forward_reference = curr_id; if ( !vpp_second && forward_reference == curr_id ) return data.size(); if ( do_vpp_deint && !vpp_second ) { VAProcFilterParameterBufferDeinterlacing *deint_params = NULL; if ( vaMapBuffer( VADisp, vpp_deint, ( void ** )&deint_params ) == VA_STATUS_SUCCESS ) { deint_params->flags = field == VA_TOP_FIELD ? VPP_TFF : VPP_BFF; vaUnmapBuffer( VADisp, vpp_deint ); } } VABufferID pipeline_buf; if ( vaCreateBuffer( VADisp, context_vpp, VAProcPipelineParameterBufferType, sizeof( VAProcPipelineParameterBuffer ), 1, NULL, &pipeline_buf ) == VA_STATUS_SUCCESS ) { VAProcPipelineParameterBuffer *pipeline_param = NULL; if ( vaMapBuffer( VADisp, pipeline_buf, ( void ** )&pipeline_param ) == VA_STATUS_SUCCESS ) { memset( pipeline_param, 0, sizeof *pipeline_param ); pipeline_param->surface = curr_id; pipeline_param->output_background_color = 0xFF000000; if ( do_vpp_deint ) { pipeline_param->num_filters = 1; pipeline_param->filters = &vpp_deint; pipeline_param->num_forward_references = 1; pipeline_param->forward_references = &forward_reference; } vaUnmapBuffer( VADisp, pipeline_buf ); if ( vaBeginPicture( VADisp, context_vpp, id_vpp ) == VA_STATUS_SUCCESS ) { vpp_ok = vaRenderPicture( VADisp, context_vpp, &pipeline_buf, 1 ) == VA_STATUS_SUCCESS; vaEndPicture( VADisp, context_vpp ); } } if ( !vpp_ok ) vaDestroyBuffer( VADisp, pipeline_buf ); } if ( vpp_second ) forward_reference = curr_id; if ( do_vpp_deint ) vpp_second = !vpp_second; if ( ( ok = vpp_ok ) ) draw( id_vpp, do_vpp_deint ? 0 : field ); } else #endif draw( curr_id, field ); paused = false; return data.size(); }
OMX_ERRORTYPE OMXVideoDecoderVP9HWR::ProcessorInit(void) { unsigned int i = 0; for (i = 0; i < MAX_NATIVE_BUFFER_COUNT; i++) { extMIDs[i] = (vaapiMemId*)malloc(sizeof(vaapiMemId)); extMIDs[i]->m_usrAddr = NULL; extMIDs[i]->m_surface = new VASurfaceID; } initDecoder(); if (RAWDATA_MODE == mWorkingMode) { OMX_PARAM_PORTDEFINITIONTYPE paramPortDefinitionInput; memcpy(¶mPortDefinitionInput, this->ports[INPORT_INDEX]->GetPortDefinition(), sizeof(paramPortDefinitionInput)); extNativeBufferSize = INTERNAL_MAX_FRAME_WIDTH * INTERNAL_MAX_FRAME_HEIGHT * 1.5; extActualBufferStride = INTERNAL_MAX_FRAME_WIDTH; extActualBufferHeightStride = INTERNAL_MAX_FRAME_HEIGHT; for (i = 0; i < OUTPORT_ACTUAL_BUFFER_COUNT; i++ ) { extMIDs[i]->m_usrAddr = (unsigned char*)malloc(sizeof(unsigned char) * extNativeBufferSize); extMIDs[i]->m_render_done = true; extMIDs[i]->m_released = true; } extMappedNativeBufferCount = OUTPORT_ACTUAL_BUFFER_COUNT; return OMX_ErrorNone; } #ifdef DECODE_WITH_GRALLOC_BUFFER if (mOMXBufferHeaderTypePtrNum > MAX_NATIVE_BUFFER_COUNT) { LOGE("Actual OMX outport buffer header type num (%d) > MAX_NATIVE_BUFFER_COUNT (%d)", mOMXBufferHeaderTypePtrNum, MAX_NATIVE_BUFFER_COUNT); return OMX_ErrorOverflow; } OMX_PARAM_PORTDEFINITIONTYPE paramPortDefinitionInput; memcpy(¶mPortDefinitionInput, this->ports[INPORT_INDEX]->GetPortDefinition(), sizeof(paramPortDefinitionInput)); int surfaceWidth = mGraphicBufferParam.graphicBufferWidth; int surfaceHeight = mGraphicBufferParam.graphicBufferHeight; int surfaceStride = mGraphicBufferParam.graphicBufferStride; extNativeBufferSize = mGraphicBufferParam.graphicBufferStride * mGraphicBufferParam.graphicBufferHeight * 1.5; extActualBufferStride = surfaceStride; extActualBufferHeightStride = surfaceHeight; for (i = 0; i < mOMXBufferHeaderTypePtrNum; i++) { OMX_BUFFERHEADERTYPE *buf_hdr = mOMXBufferHeaderTypePtrArray[i]; extMIDs[i]->m_key = (unsigned int)(buf_hdr->pBuffer); extMIDs[i]->m_render_done = false; extMIDs[i]->m_released = true; VAStatus va_res; unsigned int buffer; VASurfaceAttrib attribs[2]; VASurfaceAttribExternalBuffers* surfExtBuf = new VASurfaceAttribExternalBuffers; int32_t format = VA_RT_FORMAT_YUV420; surfExtBuf->buffers= (unsigned long *)&buffer; surfExtBuf->num_buffers = 1; surfExtBuf->pixel_format = VA_FOURCC_NV12; surfExtBuf->width = surfaceWidth; surfExtBuf->height = surfaceHeight; surfExtBuf->data_size = surfaceStride * surfaceHeight * 1.5; surfExtBuf->num_planes = 2; surfExtBuf->pitches[0] = surfaceStride; surfExtBuf->pitches[1] = surfaceStride; surfExtBuf->pitches[2] = 0; surfExtBuf->pitches[3] = 0; surfExtBuf->offsets[0] = 0; surfExtBuf->offsets[1] = surfaceStride * surfaceHeight; surfExtBuf->offsets[2] = 0; surfExtBuf->offsets[3] = 0; surfExtBuf->flags = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC; surfExtBuf->buffers[0] = (unsigned int)buf_hdr->pBuffer; attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType; attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; attribs[0].value.type = VAGenericValueTypeInteger; attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC; attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor; attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; attribs[1].value.type = VAGenericValueTypePointer; attribs[1].value.value.p = (void *)surfExtBuf; va_res = vaCreateSurfaces(mVADisplay, format, surfaceWidth, surfaceHeight, extMIDs[i]->m_surface, 1, attribs, 2); if (va_res != VA_STATUS_SUCCESS) { LOGE("Failed to create vaSurface!"); return OMX_ErrorUndefined; } delete surfExtBuf; VAImage image; unsigned char* usrptr; va_res = vaDeriveImage(mVADisplay, *(extMIDs[i]->m_surface), &image); if (VA_STATUS_SUCCESS == va_res) { va_res = vaMapBuffer(mVADisplay, image.buf, (void **) &usrptr); if (VA_STATUS_SUCCESS == va_res) { extMIDs[i]->m_usrAddr = usrptr; vaUnmapBuffer(mVADisplay, image.buf); } vaDestroyImage(mVADisplay, image.image_id); } extMappedNativeBufferCount++; } return OMX_ErrorNone; #endif }
static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff ) { vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external); VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3]; #if VA_CHECK_VERSION(0,31,0) if( vaSyncSurface( p_va->p_display, i_surface_id ) ) #else if( vaSyncSurface( p_va->p_display, p_va->i_context_id, i_surface_id ) ) #endif return VLC_EGENERIC; if(p_va->b_supports_derive) { if(vaDeriveImage(p_va->p_display, i_surface_id, &(p_va->image)) != VA_STATUS_SUCCESS) return VLC_EGENERIC; } else { if( vaGetImage( p_va->p_display, i_surface_id, 0, 0, p_va->i_surface_width, p_va->i_surface_height, p_va->image.image_id) ) return VLC_EGENERIC; } void *p_base; if( vaMapBuffer( p_va->p_display, p_va->image.buf, &p_base ) ) return VLC_EGENERIC; const uint32_t i_fourcc = p_va->image.format.fourcc; if( i_fourcc == VA_FOURCC('Y','V','1','2') || i_fourcc == VA_FOURCC('I','4','2','0') ) { bool b_swap_uv = i_fourcc == VA_FOURCC('I','4','2','0'); uint8_t *pp_plane[3]; size_t pi_pitch[3]; for( int i = 0; i < 3; i++ ) { const int i_src_plane = (b_swap_uv && i != 0) ? (3 - i) : i; pp_plane[i] = (uint8_t*)p_base + p_va->image.offsets[i_src_plane]; pi_pitch[i] = p_va->image.pitches[i_src_plane]; } CopyFromYv12( p_picture, pp_plane, pi_pitch, p_va->i_surface_width, p_va->i_surface_height, &p_va->image_cache ); } else { assert( i_fourcc == VA_FOURCC('N','V','1','2') ); uint8_t *pp_plane[2]; size_t pi_pitch[2]; for( int i = 0; i < 2; i++ ) { pp_plane[i] = (uint8_t*)p_base + p_va->image.offsets[i]; pi_pitch[i] = p_va->image.pitches[i]; } CopyFromNv12( p_picture, pp_plane, pi_pitch, p_va->i_surface_width, p_va->i_surface_height, &p_va->image_cache ); } if( vaUnmapBuffer( p_va->p_display, p_va->image.buf ) ) return VLC_EGENERIC; if(p_va->b_supports_derive) { vaDestroyImage( p_va->p_display, p_va->image.image_id ); p_va->image.image_id = VA_INVALID_ID; } return VLC_SUCCESS; }
static void nv12_to_rgba(const VAImage vaImage, rfbClient *client, int ch_x, int ch_y, int ch_w, int ch_h) { DebugLog(("%s: converting region (%d, %d)-(%d, %d) from NV12->RGBA\n", __FUNCTION__, ch_x, ch_y, ch_w, ch_h)); VAStatus va_status; uint8_t *nv12_buf; va_status = vaMapBuffer(va_dpy, vaImage.buf, (void **)&nv12_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(DecodedData)"); /* adjust x, y, width, height of the affected area so * x, y, width and height are always even. */ if (ch_x % 2) { --ch_x; ++ch_w; } if (ch_y % 2) { --ch_y; ++ch_h; } if ((ch_x + ch_w) % 2) { ++ch_w; } if ((ch_y + ch_h) % 2) { ++ch_h; } /* point nv12_buf and dst to upper left corner of changed area */ uint8_t *nv12_y = &nv12_buf[vaImage.offsets[0] + vaImage.pitches[0] * ch_y + ch_x]; uint8_t *nv12_uv = &nv12_buf[vaImage.offsets[1] + vaImage.pitches[1] * (ch_y / 2) + ch_x]; uint32_t *dst = &((uint32_t*)client->frameBuffer)[client->width * ch_y + ch_x]; /* TODO: optimize R, G, B calculation. Possible ways to do this: * - use lookup tables * - convert from floating point to integer arithmetic * - use MMX/SSE to vectorize calculations * - use GPU (VA VPP, shader...) */ int src_x, src_y; for (src_y = 0; src_y < ch_h; src_y += 2) { for (src_x = 0; src_x < ch_w; src_x += 2) { uint8_t nv_u = nv12_uv[src_x]; uint8_t nv_v = nv12_uv[src_x + 1]; uint8_t nv_y[4] = { nv12_y[ src_x], nv12_y[ src_x + 1], nv12_y[vaImage.pitches[0] + src_x], nv12_y[vaImage.pitches[0] + src_x + 1] }; int i; for (i = 0; i < 4; ++i) { double R = 1.164 * (nv_y[i] - 16) + 1.596 * (nv_v - 128); double G = 1.164 * (nv_y[i] - 16) - 0.391 * (nv_u - 128) - 0.813 * (nv_v - 128); double B = 1.164 * (nv_y[i] - 16) + 2.018 * (nv_u - 128); /* clamp R, G, B values. For some Y, U, V combinations, * the results of the above calculations fall outside of * the range 0-255. */ if (R < 0.0) R = 0.0; if (G < 0.0) G = 0.0; if (B < 0.0) B = 0.0; if (R > 255.0) R = 255.0; if (G > 255.0) G = 255.0; if (B > 255.0) B = 255.0; dst[client->width * (i / 2) + src_x + (i % 2)] = 0 | ((unsigned int)(R + 0.5) << client->format.redShift) | ((unsigned int)(G + 0.5) << client->format.greenShift) | ((unsigned int)(B + 0.5) << client->format.blueShift); } } nv12_y += 2 * vaImage.pitches[0]; nv12_uv += vaImage.pitches[1]; dst += 2 * client->width; } CHECK_SURF(va_surface_id[sid]); va_status = vaUnmapBuffer(va_dpy, vaImage.buf); CHECK_VASTATUS(va_status, "vaUnmapBuffer(DecodedData)"); }
static void h264_decode_frame(int f_width, int f_height, char *framedata, int framesize, int slice_type) { VAStatus va_status; DebugLog(("%s: called for frame of %d bytes (%dx%d) slice_type=%d\n", __FUNCTION__, framesize, width, height, slice_type)); /* Initialize decode pipeline if necessary */ if ( (f_width > cur_width) || (f_height > cur_height) ) { if (va_dpy != NULL) h264_cleanup_decoder(); cur_width = f_width; cur_height = f_height; h264_init_decoder(f_width, f_height); rfbClientLog("%s: decoder initialized\n", __FUNCTION__); } /* Decode frame */ static VAPictureH264 va_picture_h264, va_old_picture_h264; /* The server should always send an I-frame when a new client connects * or when the resolution of the framebuffer changes, but we check * just in case. */ if ( (slice_type != SLICE_TYPE_I) && (num_frames == 0) ) { rfbClientLog("First frame is not an I frame !!! Skipping!!!\n"); return; } DebugLog(("%s: frame_id=%d va_surface_id[%d]=0x%x field_order_count=%d\n", __FUNCTION__, frame_id, sid, va_surface_id[sid], field_order_count)); va_picture_h264.picture_id = va_surface_id[sid]; va_picture_h264.frame_idx = frame_id; va_picture_h264.flags = 0; va_picture_h264.BottomFieldOrderCnt = field_order_count; va_picture_h264.TopFieldOrderCnt = field_order_count; /* Set up picture parameter buffer */ if (va_pic_param_buf_id[sid] == VA_INVALID_ID) { va_status = vaCreateBuffer(va_dpy, va_context_id, VAPictureParameterBufferType, sizeof(VAPictureParameterBufferH264), 1, NULL, &va_pic_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaCreateBuffer(PicParam)"); } CHECK_SURF(va_surface_id[sid]); VAPictureParameterBufferH264 *pic_param_buf = NULL; va_status = vaMapBuffer(va_dpy, va_pic_param_buf_id[sid], (void **)&pic_param_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(PicParam)"); SetVAPictureParameterBufferH264(pic_param_buf, f_width, f_height); memcpy(&pic_param_buf->CurrPic, &va_picture_h264, sizeof(VAPictureH264)); if (slice_type == SLICE_TYPE_P) { memcpy(&pic_param_buf->ReferenceFrames[0], &va_old_picture_h264, sizeof(VAPictureH264)); pic_param_buf->ReferenceFrames[0].flags = 0; } else if (slice_type != SLICE_TYPE_I) { rfbClientLog("Frame type %d not supported!!!\n"); return; } pic_param_buf->frame_num = frame_id; va_status = vaUnmapBuffer(va_dpy, va_pic_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaUnmapBuffer(PicParam)"); /* Set up IQ matrix buffer */ if (va_mat_param_buf_id[sid] == VA_INVALID_ID) { va_status = vaCreateBuffer(va_dpy, va_context_id, VAIQMatrixBufferType, sizeof(VAIQMatrixBufferH264), 1, NULL, &va_mat_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaCreateBuffer(IQMatrix)"); } CHECK_SURF(va_surface_id[sid]); VAIQMatrixBufferH264 *iq_matrix_buf = NULL; va_status = vaMapBuffer(va_dpy, va_mat_param_buf_id[sid], (void **)&iq_matrix_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(IQMatrix)"); static const unsigned char m_MatrixBufferH264[]= { /* ScalingList4x4[6][16] */ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, /* ScalingList8x8[2][64] */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; memcpy(iq_matrix_buf, m_MatrixBufferH264, 224); va_status = vaUnmapBuffer(va_dpy, va_mat_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaUnmapBuffer(IQMatrix)"); VABufferID buffer_ids[2]; buffer_ids[0] = va_pic_param_buf_id[sid]; buffer_ids[1] = va_mat_param_buf_id[sid]; CHECK_SURF(va_surface_id[sid]); va_status = vaRenderPicture(va_dpy, va_context_id, buffer_ids, 2); CHECK_VASTATUS(va_status, "vaRenderPicture"); /* Set up slice parameter buffer */ if (va_sp_param_buf_id[sid] == VA_INVALID_ID) { va_status = vaCreateBuffer(va_dpy, va_context_id, VASliceParameterBufferType, sizeof(VASliceParameterBufferH264), 1, NULL, &va_sp_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaCreateBuffer(SliceParam)"); } CHECK_SURF(va_surface_id[sid]); VASliceParameterBufferH264 *slice_param_buf = NULL; va_status = vaMapBuffer(va_dpy, va_sp_param_buf_id[sid], (void **)&slice_param_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(SliceParam)"); static int t2_first = 1; if (slice_type == SLICE_TYPE_I) { SetVASliceParameterBufferH264_Intra(slice_param_buf, t2_first); t2_first = 0; } else { SetVASliceParameterBufferH264(slice_param_buf); memcpy(&slice_param_buf->RefPicList0[0], &va_old_picture_h264, sizeof(VAPictureH264)); slice_param_buf->RefPicList0[0].flags = 0; } slice_param_buf->slice_data_bit_offset = 0; slice_param_buf->slice_data_size = framesize; va_status = vaUnmapBuffer(va_dpy, va_sp_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaUnmapBuffer(SliceParam)"); CHECK_SURF(va_surface_id[sid]); /* Set up slice data buffer and copy H.264 encoded data */ if (va_d_param_buf_id[sid] == VA_INVALID_ID) { /* TODO use estimation matching framebuffer dimensions instead of this large value */ va_status = vaCreateBuffer(va_dpy, va_context_id, VASliceDataBufferType, 4177920, 1, NULL, &va_d_param_buf_id[sid]); /* 1080p size */ CHECK_VASTATUS(va_status, "vaCreateBuffer(SliceData)"); } char *slice_data_buf; va_status = vaMapBuffer(va_dpy, va_d_param_buf_id[sid], (void **)&slice_data_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(SliceData)"); memcpy(slice_data_buf, framedata, framesize); CHECK_SURF(va_surface_id[sid]); va_status = vaUnmapBuffer(va_dpy, va_d_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaUnmapBuffer(SliceData)"); buffer_ids[0] = va_sp_param_buf_id[sid]; buffer_ids[1] = va_d_param_buf_id[sid]; CHECK_SURF(va_surface_id[sid]); va_status = vaRenderPicture(va_dpy, va_context_id, buffer_ids, 2); CHECK_VASTATUS(va_status, "vaRenderPicture"); va_status = vaEndPicture(va_dpy, va_context_id); CHECK_VASTATUS(va_status, "vaEndPicture"); /* Prepare next one... */ int sid_new = (sid + 1) % SURFACE_NUM; DebugLog(("%s: new Surface ID = %d\n", __FUNCTION__, sid_new)); va_status = vaBeginPicture(va_dpy, va_context_id, va_surface_id[sid_new]); CHECK_VASTATUS(va_status, "vaBeginPicture"); /* Get decoded data */ va_status = vaSyncSurface(va_dpy, va_surface_id[sid]); CHECK_VASTATUS(va_status, "vaSyncSurface"); CHECK_SURF(va_surface_id[sid]); curr_surface = va_surface_id[sid]; sid = sid_new; field_order_count += 2; ++frame_id; if (frame_id > 15) { frame_id = 0; } ++num_frames; memcpy(&va_old_picture_h264, &va_picture_h264, sizeof(VAPictureH264)); }
VideoFrame VideoDecoderVAAPI::frame() { DPTR_D(VideoDecoderVAAPI); if (!d.frame->opaque || !d.frame->data[0]) return VideoFrame(); VASurfaceID surface_id = (VASurfaceID)(uintptr_t)d.frame->data[3]; VAStatus status = VA_STATUS_SUCCESS; if (display() == GLX) { d.surface_interop->setSurface((va_surface_t*)d.frame->opaque, d.surface_width, d.surface_height); VideoFrame f(d.surface_width, d.surface_height, VideoFormat::Format_RGB32); f.setBytesPerLine(d.surface_width*4); //used by gl to compute texture size f.setSurfaceInterop(d.surface_interop); return f; } #if VA_CHECK_VERSION(0,31,0) if ((status = vaSyncSurface(d.display, surface_id)) != VA_STATUS_SUCCESS) { qWarning("vaSyncSurface(VADisplay:%p, VASurfaceID:%#x) == %#x", d.display, surface_id, status); #else if (vaSyncSurface(d.display, d.context_id, surface_id)) { qWarning("vaSyncSurface(VADisplay:%#x, VAContextID:%#x, VASurfaceID:%#x) == %#x", d.display, d.context_id, surface_id, status); #endif return VideoFrame(); } if (!d.disable_derive && d.supports_derive) { /* * http://web.archiveorange.com/archive/v/OAywENyq88L319OcRnHI * vaDeriveImage is faster than vaGetImage. But VAImage is uncached memory and copying from it would be terribly slow * TODO: copy from USWC, see vlc and https://github.com/OpenELEC/OpenELEC.tv/pull/2937.diff * https://software.intel.com/en-us/articles/increasing-memory-throughput-with-intel-streaming-simd-extensions-4-intel-sse4-streaming-load */ status = vaDeriveImage(d.display, surface_id, &d.image); if (status != VA_STATUS_SUCCESS) { qWarning("vaDeriveImage(VADisplay:%p, VASurfaceID:%#x, VAImage*:%p) == %#x", d.display, surface_id, &d.image, status); return VideoFrame(); } } else { status = vaGetImage(d.display, surface_id, 0, 0, d.surface_width, d.surface_height, d.image.image_id); if (status != VA_STATUS_SUCCESS) { qWarning("vaGetImage(VADisplay:%p, VASurfaceID:%#x, 0,0, %d, %d, VAImageID:%#x) == %#x", d.display, surface_id, d.surface_width, d.surface_height, d.image.image_id, status); return VideoFrame(); } } void *p_base; if ((status = vaMapBuffer(d.display, d.image.buf, &p_base)) != VA_STATUS_SUCCESS) { qWarning("vaMapBuffer(VADisplay:%p, VABufferID:%#x, pBuf:%p) == %#x", d.display, d.image.buf, &p_base, status); return VideoFrame(); } VideoFormat::PixelFormat pixfmt = VideoFormat::Format_Invalid; bool swap_uv = false; switch (d.image.format.fourcc) { case VA_FOURCC_YV12: swap_uv |= d.disable_derive || !d.supports_derive; pixfmt = VideoFormat::Format_YUV420P; break; case VA_FOURCC_IYUV: swap_uv = true; pixfmt = VideoFormat::Format_YUV420P; break; case VA_FOURCC_NV12: pixfmt = VideoFormat::Format_NV12; break; default: break; } if (pixfmt == VideoFormat::Format_Invalid) { qWarning("unsupported vaapi pixel format: %#x", d.image.format.fourcc); return VideoFrame(); } const VideoFormat fmt(pixfmt); uint8_t *src[3]; int pitch[3]; for (int i = 0; i < fmt.planeCount(); ++i) { src[i] = (uint8_t*)p_base + d.image.offsets[i]; pitch[i] = d.image.pitches[i]; } if (swap_uv) { std::swap(src[1], src[2]); std::swap(pitch[1], pitch[2]); } VideoFrame frame; if (d.copy_uswc && d.gpu_mem.isReady()) { int yuv_size = 0; if (pixfmt == VideoFormat::Format_NV12) yuv_size = pitch[0]*d.surface_height*3/2; else yuv_size = pitch[0]*d.surface_height + pitch[1]*d.surface_height/2 + pitch[2]*d.surface_height/2; // additional 15 bytes to ensure 16 bytes aligned QByteArray buf(15 + yuv_size, 0); const int offset_16 = (16 - ((uintptr_t)buf.data() & 0x0f)) & 0x0f; // plane 1, 2... is aligned? uchar* plane_ptr = (uchar*)buf.data() + offset_16; QVector<uchar*> dst(fmt.planeCount(), 0); for (int i = 0; i < dst.size(); ++i) { dst[i] = plane_ptr; // TODO: add VideoFormat::planeWidth/Height() ? const int plane_w = pitch[i];//(i == 0 || pixfmt == VideoFormat::Format_NV12) ? d.surface_width : fmt.chromaWidth(d.surface_width); const int plane_h = i == 0 ? d.surface_height : fmt.chromaHeight(d.surface_height); plane_ptr += pitch[i] * plane_h; d.gpu_mem.copyFrame(src[i], dst[i], plane_w, plane_h, pitch[i]); } frame = VideoFrame(buf, d.width, d.height, fmt); frame.setBits(dst); frame.setBytesPerLine(pitch); } else { frame = VideoFrame(d.width, d.height, fmt); frame.setBits(src); frame.setBytesPerLine(pitch); // TODO: why clone is faster()? frame = frame.clone(); } if ((status = vaUnmapBuffer(d.display, d.image.buf)) != VA_STATUS_SUCCESS) { qWarning("vaUnmapBuffer(VADisplay:%p, VABufferID:%#x) == %#x", d.display, d.image.buf, status); return VideoFrame(); } if (!d.disable_derive && d.supports_derive) { vaDestroyImage(d.display, d.image.image_id); d.image.image_id = VA_INVALID_ID; } return frame; } struct display_names_t { VideoDecoderVAAPI::DisplayType display; QString name; }; static const display_names_t display_names[] = { { VideoDecoderVAAPI::GLX, "GLX" }, { VideoDecoderVAAPI::X11, "X11" }, { VideoDecoderVAAPI::DRM, "DRM" } }; static VideoDecoderVAAPI::DisplayType displayFromName(QString name) { for (unsigned int i = 0; i < sizeof(display_names)/sizeof(display_names[0]); ++i) { if (name.toUpper().contains(display_names[i].name.toUpper())) { return display_names[i].display; } } return VideoDecoderVAAPI::X11; } static QString displayToName(VideoDecoderVAAPI::DisplayType t) { for (unsigned int i = 0; i < sizeof(display_names)/sizeof(display_names[0]); ++i) { if (t == display_names[i].display) { return display_names[i].name; } } return QString(); } void VideoDecoderVAAPI::setDisplayPriority(const QStringList &priority) { DPTR_D(VideoDecoderVAAPI); d.display_priority.clear(); foreach (QString disp, priority) { d.display_priority.push_back(displayFromName(disp)); }
VdpStatus vdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface, VdpYCbCrFormat destination_ycbcr_format, void *const *destination_data, uint32_t const *destination_pitches) { VdpStatus err_code; if (!destination_data || !destination_pitches) return VDP_STATUS_INVALID_POINTER; VdpVideoSurfaceData *srcSurfData = handle_acquire(surface, HANDLETYPE_VIDEO_SURFACE); if (NULL == srcSurfData) return VDP_STATUS_INVALID_HANDLE; VdpDeviceData *deviceData = srcSurfData->deviceData; VADisplay va_dpy = deviceData->va_dpy; if (deviceData->va_available) { VAImage q; vaDeriveImage(va_dpy, srcSurfData->va_surf, &q); if (VA_FOURCC('N', 'V', '1', '2') == q.format.fourcc && VDP_YCBCR_FORMAT_NV12 == destination_ycbcr_format) { uint8_t *img_data; vaMapBuffer(va_dpy, q.buf, (void **)&img_data); if (destination_pitches[0] == q.pitches[0] && destination_pitches[1] == q.pitches[1]) { const uint32_t sz = (uint32_t)q.width * (uint32_t)q.height; memcpy(destination_data[0], img_data + q.offsets[0], sz); memcpy(destination_data[1], img_data + q.offsets[1], sz / 2); } else { uint8_t *src = img_data + q.offsets[0]; uint8_t *dst = destination_data[0]; for (unsigned int y = 0; y < q.height; y ++) { // Y plane memcpy (dst, src, q.width); src += q.pitches[0]; dst += destination_pitches[0]; } src = img_data + q.offsets[1]; dst = destination_data[1]; for (unsigned int y = 0; y < q.height / 2; y ++) { // UV plane memcpy(dst, src, q.width); // q.width/2 samples of U and V each, hence q.width src += q.pitches[1]; dst += destination_pitches[1]; } } vaUnmapBuffer(va_dpy, q.buf); } else if (VA_FOURCC('N', 'V', '1', '2') == q.format.fourcc && VDP_YCBCR_FORMAT_YV12 == destination_ycbcr_format) { uint8_t *img_data; vaMapBuffer(va_dpy, q.buf, (void **)&img_data); // Y plane if (destination_pitches[0] == q.pitches[0]) { const uint32_t sz = (uint32_t)q.width * (uint32_t)q.height; memcpy(destination_data[0], img_data + q.offsets[0], sz); } else { uint8_t *src = img_data + q.offsets[0]; uint8_t *dst = destination_data[0]; for (unsigned int y = 0; y < q.height; y ++) { memcpy (dst, src, q.width); src += q.pitches[0]; dst += destination_pitches[0]; } } // unpack mixed UV to separate planes for (unsigned int y = 0; y < q.height/2; y ++) { uint8_t *src = img_data + q.offsets[1] + y * q.pitches[1]; uint8_t *dst_u = destination_data[1] + y * destination_pitches[1]; uint8_t *dst_v = destination_data[2] + y * destination_pitches[2]; for (unsigned int x = 0; x < q.width/2; x++) { *dst_v++ = *src++; *dst_u++ = *src++; } } vaUnmapBuffer(va_dpy, q.buf); } else { const char *c = (const char *)&q.format.fourcc; traceError("error (%s): not implemented conversion VA FOURCC %c%c%c%c -> %s\n", __func__, *c, *(c+1), *(c+2), *(c+3), reverse_ycbcr_format(destination_ycbcr_format)); vaDestroyImage(va_dpy, q.image_id); err_code = VDP_STATUS_INVALID_Y_CB_CR_FORMAT; goto quit; } vaDestroyImage(va_dpy, q.image_id); } else { // software fallback traceError("error (%s): not implemented software fallback\n", __func__); err_code = VDP_STATUS_ERROR; goto quit; } GLenum gl_error = glGetError(); if (GL_NO_ERROR != gl_error) { traceError("error (%s): gl error %d\n", __func__, gl_error); err_code = VDP_STATUS_ERROR; goto quit; } err_code = VDP_STATUS_OK; quit: handle_release(surface); return err_code; }