#include"libvideorender.h" #include "videoutil.h" #include //Refresh Event #ifndef REFRESH_EVENT #define REFRESH_EVENT (SDL_USEREVENT + 1) #endif #ifndef RVC_DEFAULT_DELAY_TIME #define RVC_DEFAULT_DELAY_TIME 1 #endif VideoRenderImpl::VideoRenderImpl(videorender_callback_t* pCallback) { m_sdl_window = NULL; m_rending_texture = NULL; m_renderer = NULL; memcpy(&m_callback, pCallback, sizeof(videorender_callback_t)); m_refresh_flag = false; m_bmoveable = false; m_refresh_thread = NULL; m_x = SDL_WINDOWPOS_UNDEFINED; m_y = SDL_WINDOWPOS_UNDEFINED; m_width = 0; m_height = 0; m_videoformat = VIDEO_FORMAT_RGB24; m_flags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_HIDDEN | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU; } VideoRenderImpl::~VideoRenderImpl() { if (NULL != m_rending_texture) { SDL_DestroyTexture(m_rending_texture); m_rending_texture = NULL; //RenderLog(RENDER_LOG_DEBUG, "DestroyTexture."); } if (NULL != m_renderer) { SDL_DestroyRenderer(m_renderer); m_renderer = NULL; //RenderLog(RENDER_LOG_DEBUG, "DestroyRenderer."); } if (NULL != m_sdl_window) { SDL_DestroyWindow(m_sdl_window); m_sdl_window = NULL; //RenderLog(RENDER_LOG_DEBUG, "DestroyWindow."); } } int VideoRenderImpl::SetWindowProperty(videorender_param_t* tparam) { int iRet = -1; if (NULL == tparam){ return iRet; } m_x = tparam->icx; m_y = tparam->icy; m_width = tparam->uwidth; m_height = tparam->uheight; m_videowidth = m_width; m_videoheight = m_height; if (tparam->uvideowidth > 0){ m_videowidth = tparam->uvideowidth; } if (tparam->uvideoheight > 0){ m_videoheight = tparam->uvideoheight; } m_videoformat = tparam->ivideoformat; iRet = 0; return iRet; } SDL_PixelFormatEnum VideoRenderImpl::GetPixelFormat() { SDL_PixelFormatEnum eType = SDL_PIXELFORMAT_BGR24; if (VIDEO_FORMAT_I420 == m_videoformat){ eType = SDL_PIXELFORMAT_IYUV; } return eType; } int VideoRenderImpl::VideoRenderSetParam(videorender_param_t* tparam) { if (SetWindowProperty(tparam)){ RenderLog(RENDER_LOG_ERROR, "SetWindowProperty failed for param error."); return -1; } if (NULL == m_sdl_window){ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); m_sdl_window = SDL_CreateWindow( "", // window title m_x, // initial x position m_y, // initial y position m_width, // width, in pixels m_height, // height, in pixels m_flags ); if (NULL == m_sdl_window){ RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) Couldn't open window: %s", SDL_GetError()); VideoRenderDestroy(); return -2; } int idisplay_index = SDL_GetWindowDisplayIndex(m_sdl_window); if (-1 == idisplay_index) { RenderLog(RENDER_LOG_ERROR, "RENDER: get window dispaly index failed!"); return -3; } SDL_DisplayMode display_mode; int err = SDL_GetDesktopDisplayMode(idisplay_index, &display_mode); if (0 == err){ RenderLog(RENDER_LOG_DEBUG, "RENDER: video display %i -> %dx%dpx @ %dhz", idisplay_index, display_mode.w, display_mode.h, display_mode.refresh_rate); } else { RenderLog(RENDER_LOG_ERROR, "RENDER: Couldn't determine display mode for video display %i", idisplay_index); } if (m_width > display_mode.w) { m_width = display_mode.w; } if (m_height > display_mode.h) { m_height = display_mode.h; } SDL_SetWindowSize(m_sdl_window, m_width, m_height); } static bool blog = true; /* Print the list of the available renderers*/ if (blog) { RenderLog(RENDER_LOG_DEBUG, "RENDER: Available SDL2 rendering drivers:"); } SDL_RendererInfo rend_info; for (int i = 0; i < SDL_GetNumRenderDrivers(); i++){ if (SDL_GetRenderDriverInfo(i, &rend_info) < 0){ RenderLog(RENDER_LOG_ERROR, "Couldn't get SDL2 render driver information: %s", SDL_GetError()); } else { if (blog) { RenderLog(RENDER_LOG_DEBUG, " %2d: %s", i, rend_info.name); } //RenderLog(RENDER_LOG_DEBUG," SDL_RENDERER_TARGETTEXTURE [%c]", (rend_info.flags & SDL_RENDERER_TARGETTEXTURE) ? 'X' : ' '); //RenderLog(RENDER_LOG_DEBUG," SDL_RENDERER_SOFTWARE [%c]", (rend_info.flags & SDL_RENDERER_SOFTWARE) ? 'X' : ' '); //RenderLog(RENDER_LOG_DEBUG," SDL_RENDERER_ACCELERATED [%c]", (rend_info.flags & SDL_RENDERER_ACCELERATED) ? 'X' : ' '); //RenderLog(RENDER_LOG_DEBUG," SDL_RENDERER_PRESENTVSYNC [%c]", (rend_info.flags & SDL_RENDERER_PRESENTVSYNC) ? 'X' : ' '); } } m_renderer = SDL_CreateRenderer(m_sdl_window, -1, SDL_RENDERER_TARGETTEXTURE | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); if (m_renderer == NULL) { RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) Couldn't get a accelerated renderer: %s", SDL_GetError()); RenderLog(RENDER_LOG_INFO, "RENDER: (SDL2) trying with a software renderer"); m_renderer = SDL_CreateRenderer(m_sdl_window, -1, SDL_RENDERER_TARGETTEXTURE | SDL_RENDERER_SOFTWARE); if (m_renderer == NULL){ RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) Couldn't get a software renderer: %s", SDL_GetError()); RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) giving up..."); VideoRenderDestroy(); return -3; } } /* Print the name of the current rendering driver */ SDL_RendererInfo render_info; if (SDL_GetRendererInfo(m_renderer, &render_info) < 0){ RenderLog(RENDER_LOG_ERROR, "Couldn't get SDL2 rendering driver information: %s", SDL_GetError()); } if (blog) { RenderLog(RENDER_LOG_DEBUG, "RENDER: rendering driver in use: %s", render_info.name); } //RenderLog(RENDER_LOG_DEBUG, " SDL_RENDERER_TARGETTEXTURE [%c]", (render_info.flags & SDL_RENDERER_TARGETTEXTURE) ? 'X' : ' '); //RenderLog(RENDER_LOG_DEBUG, " SDL_RENDERER_SOFTWARE [%c]", (render_info.flags & SDL_RENDERER_SOFTWARE) ? 'X' : ' '); //RenderLog(RENDER_LOG_DEBUG, " SDL_RENDERER_ACCELERATED [%c]", (render_info.flags & SDL_RENDERER_ACCELERATED) ? 'X' : ' '); //RenderLog(RENDER_LOG_DEBUG, " SDL_RENDERER_PRESENTVSYNC [%c]", (render_info.flags & SDL_RENDERER_PRESENTVSYNC) ? 'X' : ' '); blog = false; SDL_RenderSetLogicalSize(m_renderer, m_videowidth, m_videoheight); SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_NONE); m_rending_texture = SDL_CreateTexture(m_renderer, GetPixelFormat(), SDL_TEXTUREACCESS_STREAMING, m_videowidth, m_videoheight); if (m_rending_texture == NULL){ RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) Couldn't get a texture for rendering: %s", SDL_GetError()); VideoRenderDestroy(); return -4; } return 0; } static int refresh_video(void *opaque) { VideoRenderImpl* pImpl = (VideoRenderImpl*)opaque; while (pImpl->GetReFreshFlag()) { SDL_Event event; event.type = REFRESH_EVENT; SDL_PushEvent(&event); SDL_Delay(RVC_DEFAULT_DELAY_TIME); } return 0; } static SDL_HitTestResult SDLCALL SDL_HitTestCallback(SDL_Window *win, const SDL_Point *area, void *data) { VideoRenderImpl* pImpl = (VideoRenderImpl*)data; int iwidth = 0, iheight = 0; SDL_GetWindowSize(win, &iwidth, &iheight); pImpl->RenderLog(RENDER_LOG_DEBUG, "window width is %d, height is %d.", iwidth, iheight); pImpl->RenderLog(RENDER_LOG_DEBUG, "area->x = %d, area->y = %d.", area->x, area->y); return SDL_HITTEST_DRAGGABLE; } bool VideoRenderImpl::GetReFreshFlag() { return m_refresh_flag; } int VideoRenderImpl::StartVideoRender() { m_refresh_flag = true; m_refresh_thread = SDL_CreateThread(refresh_video,"refresh video thread", this); if (NULL == m_refresh_thread) { RenderLog(RENDER_LOG_ERROR, "SDL_Create refresh video thread failed: %s.", SDL_GetError()); return -1; } if (m_bmoveable){ if (0 != SDL_SetWindowHitTest(m_sdl_window, SDL_HitTestCallback, this)) { RenderLog(RENDER_LOG_ERROR, "SDL_SetWindowHitTest failed: %s.", SDL_GetError()); return -1; } } return 0; } int VideoRenderImpl::ShowVideoWindow() { int iret = -1; if (NULL != m_sdl_window){ Uint32 uflags = SDL_GetWindowFlags(m_sdl_window); if (uflags & SDL_WINDOW_HIDDEN) { SDL_ShowWindow(m_sdl_window); } iret = 0; } return iret; } int VideoRenderImpl::HideVideoWindow() { int iret = -1; if (NULL != m_sdl_window) { Uint32 uflags = SDL_GetWindowFlags(m_sdl_window); if (uflags & SDL_WINDOW_SHOWN) { SDL_HideWindow(m_sdl_window); } iret = 0; } return iret; } int VideoRenderImpl::StopVideoRender() { HideVideoWindow(); m_refresh_flag = false; if (NULL != m_refresh_thread){ SDL_WaitThread(m_refresh_thread, NULL); m_refresh_thread = NULL; } return 0; } void VideoRenderImpl::VideoRenderDestroy() { delete this; } int VideoRenderImpl::RenderVideoFrame(video_frame* pframe, RVC_RendererFlip eFlipType) { int iret = -1; if (NULL == m_sdl_window || NULL == pframe || NULL == m_renderer || NULL == m_rending_texture){ return iret; } SDL_Event event; SDL_WaitEvent(&event); if(REFRESH_EVENT == event.type){ SDL_SetRenderDrawColor(m_renderer, 0, 0, 0, 255); /*black*/ SDL_RenderClear(m_renderer); /* since data is continuous we can use SDL_UpdateTexture * instead of SDL_UpdateYUVTexture. * no need to use SDL_Lock/UnlockTexture (it doesn't seem faster) */ //SDL_QueryTexture() if (VIDEO_FORMAT_RGB24 == pframe->format || VIDEO_FORMAT_I420 == pframe->format){ if (VIDEO_FORMAT_RGB24 == pframe->format){ SDL_UpdateTexture(m_rending_texture, NULL, pframe->data[0], pframe->width*3); } else{ SDL_UpdateTexture(m_rending_texture, NULL, pframe->data[0], pframe->width); } if (RVC_FLIP_NONE == eFlipType){ SDL_RenderCopy(m_renderer, m_rending_texture, NULL, NULL); } else{ SDL_Point point = {m_videowidth/2, m_videoheight/2}; SDL_RenderCopyEx(m_renderer, m_rending_texture, NULL, NULL, 0, &point, (SDL_RendererFlip)eFlipType); } } else{ RenderLog(RENDER_LOG_ERROR, "%s:%d not support format, return", __FUNCTION__, __LINE__); return iret; } SDL_RenderPresent(m_renderer); } iret = 0; return iret; } void VideoRenderImpl::RenderLog(render_loglevel elevel, const char* fmt, ...) { if (m_callback.debug) { va_list arg; va_start(arg, fmt); if (*m_callback.debug){ (*m_callback.debug)(elevel, m_callback.user_data, fmt, arg); } va_end(arg); } }