void* SurfaceInteropVAAPI::mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); int nb_fmts = vaMaxNumImageFormats(m_surface->vadisplay()); //av_mallocz_array VAImageFormat *p_fmt = (VAImageFormat*)calloc(nb_fmts, sizeof(*p_fmt)); if (!p_fmt) { return NULL; } if (vaQueryImageFormats(m_surface->vadisplay(), p_fmt, &nb_fmts)) { free(p_fmt); return NULL; } VAImage image; for (int i = 0; i < nb_fmts; i++) { if (p_fmt[i].fourcc == VA_FOURCC_YV12 || p_fmt[i].fourcc == VA_FOURCC_IYUV || p_fmt[i].fourcc == VA_FOURCC_NV12) { qDebug("vaCreateImage: %c%c%c%c", p_fmt[i].fourcc<<24>>24, p_fmt[i].fourcc<<16>>24, p_fmt[i].fourcc<<8>>24, p_fmt[i].fourcc>>24); if (vaCreateImage(m_surface->vadisplay(), &p_fmt[i], m_surface->width(), m_surface->height(), &image) != VA_STATUS_SUCCESS) { image.image_id = VA_INVALID_ID; qDebug("vaCreateImage error: %c%c%c%c", p_fmt[i].fourcc<<24>>24, p_fmt[i].fourcc<<16>>24, p_fmt[i].fourcc<<8>>24, p_fmt[i].fourcc>>24); continue; } /* Validate that vaGetImage works with this format */ if (vaGetImage(m_surface->vadisplay(), m_surface->get(), 0, 0, m_surface->width(), m_surface->height(), image.image_id) != VA_STATUS_SUCCESS) { vaDestroyImage(m_surface->vadisplay(), image.image_id); qDebug("vaGetImage error: %c%c%c%c", p_fmt[i].fourcc<<24>>24, p_fmt[i].fourcc<<16>>24, p_fmt[i].fourcc<<8>>24, p_fmt[i].fourcc>>24); image.image_id = VA_INVALID_ID; continue; }
bool VaapiSurface::putImage(VaapiImage * image) { VAImageID imageID; VAStatus status; uint32_t width, height; if (!image) return false; width = image->getWidth(); height = image->getHeight(); if (width != m_width || height != m_height) { ERROR("Image resolution does not match with surface"); return false; } imageID = image->getID(); status = vaGetImage(m_display->getID(), m_ID, 0, 0, width, height, imageID); if (!checkVaapiStatus(status, "vaPutImage()")) return false; return true; }
/** * gst_vaapi_surface_get_image * @surface: a #GstVaapiSurface * @image: a #GstVaapiImage * * Retrieves surface data into a #GstVaapiImage. The @image must have * a format supported by the @surface. * * Return value: %TRUE on success */ gboolean gst_vaapi_surface_get_image (GstVaapiSurface * surface, GstVaapiImage * image) { GstVaapiDisplay *display; VAImageID image_id; VAStatus status; guint width, height; g_return_val_if_fail (surface != NULL, FALSE); g_return_val_if_fail (image != NULL, FALSE); display = GST_VAAPI_OBJECT_DISPLAY (surface); if (!display) return FALSE; width = GST_VAAPI_IMAGE_WIDTH (image); height = GST_VAAPI_IMAGE_HEIGHT (image); if (width != surface->width || height != surface->height) return FALSE; image_id = GST_VAAPI_OBJECT_ID (image); if (image_id == VA_INVALID_ID) return FALSE; GST_VAAPI_DISPLAY_LOCK (display); status = vaGetImage (GST_VAAPI_DISPLAY_VADISPLAY (display), GST_VAAPI_OBJECT_ID (surface), 0, 0, width, height, image_id); GST_VAAPI_DISPLAY_UNLOCK (display); if (!vaapi_check_status (status, "vaGetImage()")) return FALSE; return TRUE; }
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; }
bool EglVaapiImage::blt(const VideoFrameRawData& src) { if (!m_inited) { ERROR("call init before blt!"); return false; } if (m_acquired) vaReleaseBufferHandle(m_display, m_image.buf); VAStatus vaStatus = vaGetImage(m_display, src.internalID, 0, 0, src.width, src.height, m_image.image_id); // incomplete data yet m_frameInfo.timeStamp = src.timeStamp; m_frameInfo.flags = src.flags; return checkVaapiStatus(vaStatus, "vaGetImage"); }
bool EglVaapiImage::blt(const SharedPtr<VideoFrame>& src) { if (!m_inited) { ERROR("call init before blt!"); return false; } if (m_acquired) vaReleaseBufferHandle(m_display, m_image.buf); VAStatus vaStatus = vaGetImage(m_display, (VASurfaceID)src->surface, src->crop.x, src->crop.y, src->crop.width, src->crop.height, m_image.image_id); // incomplete data yet m_frameInfo.timeStamp = src->timeStamp; m_frameInfo.flags = src->flags; return checkVaapiStatus(vaStatus, "vaGetImage"); }
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 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 int CreateSurfaces( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma, int i_width, int i_height ) { assert( i_width > 0 && i_height > 0 ); /* */ p_va->p_surface = calloc( p_va->i_surface_count, sizeof(*p_va->p_surface) ); if( !p_va->p_surface ) return VLC_EGENERIC; p_va->image.image_id = VA_INVALID_ID; p_va->i_context_id = VA_INVALID_ID; /* Create surfaces */ VASurfaceID pi_surface_id[p_va->i_surface_count]; if( vaCreateSurfaces( p_va->p_display, i_width, i_height, VA_RT_FORMAT_YUV420, p_va->i_surface_count, pi_surface_id ) ) { for( int i = 0; i < p_va->i_surface_count; i++ ) p_va->p_surface[i].i_id = VA_INVALID_SURFACE; goto error; } for( int i = 0; i < p_va->i_surface_count; i++ ) { vlc_va_surface_t *p_surface = &p_va->p_surface[i]; p_surface->i_id = pi_surface_id[i]; p_surface->i_refcount = 0; p_surface->i_order = 0; } /* Create a context */ if( vaCreateContext( p_va->p_display, p_va->i_config_id, i_width, i_height, VA_PROGRESSIVE, pi_surface_id, p_va->i_surface_count, &p_va->i_context_id ) ) { p_va->i_context_id = VA_INVALID_ID; goto error; } /* Find and create a supported image chroma */ int i_fmt_count = vaMaxNumImageFormats( p_va->p_display ); VAImageFormat *p_fmt = calloc( i_fmt_count, sizeof(*p_fmt) ); if( !p_fmt ) goto error; if( vaQueryImageFormats( p_va->p_display, p_fmt, &i_fmt_count ) ) { free( p_fmt ); goto error; } VAImage testImage; if(vaDeriveImage(p_va->p_display, pi_surface_id[0], &testImage) == VA_STATUS_SUCCESS) { p_va->b_supports_derive = true; vaDestroyImage(p_va->p_display, testImage.image_id); } vlc_fourcc_t i_chroma = 0; VAImageFormat fmt; for( int i = 0; i < i_fmt_count; i++ ) { if( p_fmt[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || p_fmt[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) || p_fmt[i].fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) ) { if( vaCreateImage( p_va->p_display, &p_fmt[i], i_width, i_height, &p_va->image ) ) { p_va->image.image_id = VA_INVALID_ID; continue; } /* Validate that vaGetImage works with this format */ if( vaGetImage( p_va->p_display, pi_surface_id[0], 0, 0, i_width, i_height, p_va->image.image_id) ) { vaDestroyImage( p_va->p_display, p_va->image.image_id ); p_va->image.image_id = VA_INVALID_ID; continue; } i_chroma = VLC_CODEC_YV12; fmt = p_fmt[i]; break; } } free( p_fmt ); if( !i_chroma ) goto error; *pi_chroma = i_chroma; if(p_va->b_supports_derive) { vaDestroyImage( p_va->p_display, p_va->image.image_id ); p_va->image.image_id = VA_INVALID_ID; } if( unlikely(CopyInitCache( &p_va->image_cache, i_width )) ) goto error; /* Setup the ffmpeg hardware context */ *pp_hw_ctx = &p_va->hw_ctx; memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) ); p_va->hw_ctx.display = p_va->p_display; p_va->hw_ctx.config_id = p_va->i_config_id; p_va->hw_ctx.context_id = p_va->i_context_id; /* */ p_va->i_surface_chroma = i_chroma; p_va->i_surface_width = i_width; p_va->i_surface_height = i_height; return VLC_SUCCESS; error: DestroySurfaces( p_va ); return VLC_EGENERIC; }
static void* putsurface_thread(void *data) { int width=win_width, height=win_height; void *drawable = data; int quit = 0; VAStatus vaStatus; int row_shift = 0; int index = 0; unsigned int frame_num=0, start_time, putsurface_time; VARectangle cliprects[2]; /* client supplied clip list */ int continue_display = 0; if (drawable == drawable_thread0) printf("Enter into thread0\n\n"); if (drawable == drawable_thread1) printf("Enter into thread1\n\n"); putsurface_time = 0; while (!quit) { VASurfaceID surface_id = VA_INVALID_SURFACE; while (surface_id == VA_INVALID_SURFACE) surface_id = get_next_free_surface(&index); if (verbose) printf("Thread: %p Display surface 0x%x,\n", drawable, surface_id); if (multi_thread) upload_surface(va_dpy, surface_id, box_width, row_shift, display_field); if (check_event) pthread_mutex_lock(&gmutex); start_time = get_tick_count(); if ((continue_display == 0) && getenv("FRAME_STOP")) { char c; printf("Press any key to display frame %d...(c/C to continue)\n", frame_num); c = getchar(); if (c == 'c' || c == 'C') continue_display = 1; } if (test_color_conversion) { static int _put_surface_count = 0; if (_put_surface_count++ %50 == 0) { printf("do additional colorcoversion from %s to %s\n", map_vafourcc_to_str(csc_src_fourcc), map_vafourcc_to_str(csc_dst_fourcc)); } // get image from surface, csc_src_fourcc to csc_dst_fourcc conversion happens vaStatus = vaGetImage(va_dpy, surface_id, 0, 0, surface_width, surface_height, csc_dst_fourcc_image.image_id); CHECK_VASTATUS(vaStatus,"vaGetImage"); // render csc_dst_fourcc image to temp surface vaStatus = vaPutImage(va_dpy, csc_render_surface, csc_dst_fourcc_image.image_id, 0, 0, surface_width, surface_height, 0, 0, surface_width, surface_height); CHECK_VASTATUS(vaStatus,"vaPutImage"); // render the temp surface, it should be same with original surface without color conversion test vaStatus = vaPutSurface(va_dpy, csc_render_surface, CAST_DRAWABLE(drawable), 0,0,surface_width,surface_height, 0,0,width,height, (test_clip==0)?NULL:&cliprects[0], (test_clip==0)?0:2, display_field); CHECK_VASTATUS(vaStatus,"vaPutSurface"); } else { vaStatus = vaPutSurface(va_dpy, surface_id, CAST_DRAWABLE(drawable), 0,0,surface_width,surface_height, 0,0,width,height, (test_clip==0)?NULL:&cliprects[0], (test_clip==0)?0:2, display_field); CHECK_VASTATUS(vaStatus,"vaPutSurface"); } putsurface_time += (get_tick_count() - start_time); if (check_event) pthread_mutex_unlock(&gmutex); pthread_mutex_unlock(&surface_mutex[index]); /* locked in get_next_free_surface */ if ((frame_num % 0xff) == 0) { fprintf(stderr, "%.2f FPS \r", 256000.0 / (float)putsurface_time); putsurface_time = 0; update_clipbox(cliprects, width, height); } if (check_event) check_window_event(win_display, drawable, &width, &height, &quit); if (multi_thread) { /* reload surface content */ row_shift++; if (row_shift==(2*box_width)) row_shift= 0; } if (frame_rate != 0) /* rough framerate control */ usleep(1000/frame_rate*1000); frame_num++; if (frame_num >= frame_num_total) quit = 1; } if (drawable == drawable_thread1) pthread_exit(NULL); return 0; }
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; }
bool VAAPIContext::InitImage(const void *buf) { if (!buf) return false; if (!m_dispType == kVADisplayX11) return true; int num_formats = 0; int max_formats = vaMaxNumImageFormats(m_ctx.display); VAImageFormat *formats = new VAImageFormat[max_formats]; INIT_ST; va_status = vaQueryImageFormats(m_ctx.display, formats, &num_formats); CHECK_ST; const vaapi_surface *surf = (vaapi_surface*)buf; unsigned int deriveImageFormat = 0; if (vaDeriveImage(m_ctx.display, surf->m_id, &m_image) == VA_STATUS_SUCCESS) { m_deriveSupport = true; deriveImageFormat = m_image.format.fourcc; vaDestroyImage(m_ctx.display, m_image.image_id); } int nv12support = -1; for (int i = 0; i < num_formats; i++) { if (formats[i].fourcc == VA_FOURCC_YV12 || formats[i].fourcc == VA_FOURCC_IYUV || formats[i].fourcc == VA_FOURCC_NV12) { if (vaCreateImage(m_ctx.display, &formats[i], m_size.width(), m_size.height(), &m_image)) { m_image.image_id = VA_INVALID_ID; continue; } if (vaGetImage(m_ctx.display, surf->m_id, 0, 0, m_size.width(), m_size.height(), m_image.image_id)) { vaDestroyImage(m_ctx.display, m_image.image_id); m_image.image_id = VA_INVALID_ID; continue; } if (formats[i].fourcc == VA_FOURCC_NV12) { // mark as NV12 as supported, but favor other formats first nv12support = i; vaDestroyImage(m_ctx.display, m_image.image_id); m_image.image_id = VA_INVALID_ID; continue; } break; } } if (m_image.image_id == VA_INVALID_ID && nv12support >= 0) { // only nv12 is supported, use that format if (vaCreateImage(m_ctx.display, &formats[nv12support], m_size.width(), m_size.height(), &m_image)) { m_image.image_id = VA_INVALID_ID; } } else if (m_deriveSupport && deriveImageFormat != m_image.format.fourcc) { // only use vaDerive if it's giving us a format we can handle natively m_deriveSupport = false; } delete [] formats; if (m_image.image_id == VA_INVALID_ID) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create software image."); return false; } LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("InitImage: id %1, width %2 height %3 " "format %4 vaDeriveSupport:%5") .arg(m_image.image_id).arg(m_image.width).arg(m_image.height) .arg(m_image.format.fourcc).arg(m_deriveSupport)); if (m_deriveSupport) { vaDestroyImage(m_ctx.display, m_image.image_id ); m_image.image_id = VA_INVALID_ID; } return true; }
/** Finds a supported image chroma */ static int FindFormat(vlc_va_sys_t *sys) { int count = vaMaxNumImageFormats(sys->hw_ctx.display); VAImageFormat *fmts = malloc(count * sizeof (*fmts)); if (unlikely(fmts == NULL)) return VLC_ENOMEM; if (vaQueryImageFormats(sys->hw_ctx.display, fmts, &count)) { free(fmts); return VLC_EGENERIC; } sys->format.fourcc = 0; for (int i = 0; i < count; i++) { unsigned fourcc = fmts[i].fourcc; if (fourcc != VA_FOURCC_YV12 && fourcc != VA_FOURCC_IYUV && fourcc != VA_FOURCC_NV12) continue; VAImage image; if (vaCreateImage(sys->hw_ctx.display, &fmts[i], sys->width, sys->height, &image)) continue; /* Validate that vaGetImage works with this format */ int val = vaGetImage(sys->hw_ctx.display, sys->surfaces[0], 0, 0, sys->width, sys->height, image.image_id); vaDestroyImage(sys->hw_ctx.display, image.image_id); if (val != VA_STATUS_SUCCESS) continue; /* Mark NV12 as supported, but favor other formats first */ sys->format = fmts[i]; if (fourcc != VA_FOURCC_NV12) break; } free(fmts); if (sys->format.fourcc == 0) return VLC_EGENERIC; /* None of the formats work */ VAImage image; /* Use vaDerive() iif it supports the best selected format */ sys->do_derive = false; if (vaDeriveImage(sys->hw_ctx.display, sys->surfaces[0], &image) == VA_STATUS_SUCCESS) { if (image.format.fourcc == sys->format.fourcc) { sys->do_derive = true; sys->format = image.format; } vaDestroyImage(sys->hw_ctx.display, image.image_id); } return VLC_SUCCESS; }
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())); }
int get_image(VASurfaceID surface, Image *dst_img) { VAAPIContext * const vaapi = vaapi_get_context(); VAImage image; VAImageFormat *image_format = NULL; VAStatus status; Image bound_image; int i, is_bound_image = 0, is_derived_image = 0, error = -1; image.image_id = VA_INVALID_ID; image.buf = VA_INVALID_ID; if (!image_format) { status = vaDeriveImage(vaapi->display, surface, &image); if (vaapi_check_status(status, "vaDeriveImage()")) { if (image.image_id != VA_INVALID_ID && image.buf != VA_INVALID_ID) { D(bug("using vaDeriveImage()\n")); is_derived_image = 1; image_format = &image.format; } else { D(bug("vaDeriveImage() returned success but VA image is invalid. Trying vaGetImage()\n")); } } } if (!image_format) { for (i = 0; image_formats[i] != 0; i++) { if (get_image_format(vaapi, image_formats[i], &image_format)) break; } } if (!image_format) goto end; D(bug("selected %s image format for getimage\n", string_of_VAImageFormat(image_format))); if (!is_derived_image) { status = vaCreateImage(vaapi->display, image_format, vaapi->picture_width, vaapi->picture_height, &image); if (!vaapi_check_status(status, "vaCreateImage()")) goto end; D(bug("created image with id 0x%08x and buffer id 0x%08x\n", image.image_id, image.buf)); VARectangle src_rect; src_rect.x = 0; src_rect.y = 0; src_rect.width = vaapi->picture_width; src_rect.height = vaapi->picture_height; D(bug("src rect (%d,%d):%ux%u\n", src_rect.x, src_rect.y, src_rect.width, src_rect.height)); status = vaGetImage( vaapi->display, vaapi->surface_id, src_rect.x, src_rect.y, src_rect.width, src_rect.height, image.image_id ); if (!vaapi_check_status(status, "vaGetImage()")) { vaDestroyImage(vaapi->display, image.image_id); goto end; } } if (bind_image(&image, &bound_image) < 0) goto end; is_bound_image = 1; if (image_convert(dst_img, &bound_image) < 0) goto end; error = 0; end: if (is_bound_image) { if (release_image(&image) < 0) error = -1; } if (image.image_id != VA_INVALID_ID) { status = vaDestroyImage(vaapi->display, image.image_id); if (!vaapi_check_status(status, "vaDestroyImage()")) error = -1; } return error; }
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) { 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)); }
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]); }