libvideorender.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. #include"libvideorender.h"
  2. #include "../libvideoframework/videoutil.h"
  3. #include <string.h>
  4. //Refresh Event
  5. #ifndef REFRESH_EVENT
  6. #define REFRESH_EVENT (SDL_USEREVENT + 1)
  7. #endif
  8. #ifndef RVC_DEFAULT_DELAY_TIME
  9. #define RVC_DEFAULT_DELAY_TIME 1
  10. #endif
  11. VideoRenderImpl::VideoRenderImpl(videorender_callback_t* pCallback)
  12. {
  13. m_sdl_window = NULL;
  14. m_rending_texture = NULL;
  15. m_renderer = NULL;
  16. memcpy(&m_callback, pCallback, sizeof(videorender_callback_t));
  17. m_refresh_flag = false;
  18. m_bmoveable = false;
  19. m_refresh_thread = NULL;
  20. m_cx = SDL_WINDOWPOS_UNDEFINED;
  21. m_cy = SDL_WINDOWPOS_UNDEFINED;
  22. m_width = 0;
  23. m_height = 0;
  24. m_videoformat = VIDEO_FORMAT_RGB24;
  25. m_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS | SDL_WINDOW_HIDDEN | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU;
  26. }
  27. VideoRenderImpl::~VideoRenderImpl()
  28. {
  29. if (NULL != m_rending_texture) {
  30. SDL_DestroyTexture(m_rending_texture);
  31. m_rending_texture = NULL;
  32. //RenderLog("DestroyTexture.");
  33. }
  34. if (NULL != m_renderer) {
  35. SDL_DestroyRenderer(m_renderer);
  36. m_renderer = NULL;
  37. //RenderLog("DestroyRenderer.");
  38. }
  39. if (NULL != m_sdl_window) {
  40. SDL_DestroyWindow(m_sdl_window);
  41. m_sdl_window = NULL;
  42. //RenderLog("DestroyWindow.");
  43. }
  44. }
  45. int VideoRenderImpl::SetWindowProperty(videorender_param_t* tparam)
  46. {
  47. int iRet = -1;
  48. if (NULL == tparam){
  49. return iRet;
  50. }
  51. m_cx = tparam->icx;
  52. m_cy = tparam->icy;
  53. m_width = tparam->uwidth;
  54. m_height = tparam->uheight;
  55. m_videowidth = m_width;
  56. m_videoheight = m_height;
  57. if (tparam->uvideowidth > 0){
  58. m_videowidth = tparam->uvideowidth;
  59. }
  60. if (tparam->uvideoheight > 0){
  61. m_videoheight = tparam->uvideoheight;
  62. }
  63. m_flags |= tparam->uwinflags;
  64. m_videoformat = tparam->ivideoformat;
  65. iRet = 0;
  66. return iRet;
  67. }
  68. SDL_PixelFormatEnum VideoRenderImpl::GetPixelFormat()
  69. {
  70. SDL_PixelFormatEnum eType = SDL_PIXELFORMAT_BGR24;
  71. //SDL_PixelFormatEnum eType = SDL_PIXELFORMAT_BGR888;
  72. if (VIDEO_FORMAT_I420 == m_videoformat){
  73. eType = SDL_PIXELFORMAT_IYUV;
  74. }
  75. return eType;
  76. }
  77. int VideoRenderImpl::VideoRenderSetParam(videorender_param_t* tparam)
  78. {
  79. if (SetWindowProperty(tparam)){
  80. RenderLog(RENDER_LOG_ERROR, "SetWindowProperty failed for param error.");
  81. return -1;
  82. }
  83. if (NULL == m_sdl_window){
  84. //if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0){
  85. // m_callback->Debug("RENDER: Couldn't initialize SDL2: %s", SDL_GetError());
  86. // return -1;
  87. //}
  88. SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "1");
  89. m_sdl_window = SDL_CreateWindow(
  90. "rvc video", // window title
  91. m_cx, // initial x position
  92. m_cy, // initial y position
  93. m_width, // width, in pixels
  94. m_height, // height, in pixels
  95. m_flags
  96. );
  97. if (NULL == m_sdl_window){
  98. RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) Couldn't open window: %s", SDL_GetError());
  99. VideoRenderDestroy();
  100. return -2;
  101. }
  102. int display_index = SDL_GetWindowDisplayIndex(m_sdl_window);
  103. SDL_DisplayMode display_mode;
  104. int err = SDL_GetDesktopDisplayMode(display_index, &display_mode);
  105. if (0 == err){
  106. RenderLog(RENDER_LOG_DEBUG, "RENDER: video display %i -> %dx%dpx @ %dhz",
  107. display_index,
  108. display_mode.w,
  109. display_mode.h,
  110. display_mode.refresh_rate);
  111. }
  112. else {
  113. RenderLog(RENDER_LOG_ERROR, "RENDER: Couldn't determine display mode for video display %i", display_index);
  114. }
  115. if (m_width > display_mode.w) {
  116. m_width = display_mode.w;
  117. }
  118. if (m_height > display_mode.h) {
  119. m_height = display_mode.h;
  120. }
  121. SDL_SetWindowSize(m_sdl_window, m_width, m_height);
  122. }
  123. /* Allocate a renderer info struct*/
  124. SDL_RendererInfo* rend_info = (SDL_RendererInfo*)malloc(sizeof(SDL_RendererInfo));
  125. if (NULL == rend_info){
  126. RenderLog(RENDER_LOG_ERROR, "RENDER: Couldn't allocate memory for the renderer info data structure");
  127. VideoRenderDestroy();
  128. return -5;
  129. }
  130. static bool blog = true;
  131. /* Print the list of the available renderers*/
  132. if (blog) {
  133. RenderLog(RENDER_LOG_INFO, "RENDER: Available SDL2 rendering drivers:");
  134. }
  135. for (int i = 0; i < SDL_GetNumRenderDrivers(); i++){
  136. if (SDL_GetRenderDriverInfo(i, rend_info) < 0){
  137. RenderLog(RENDER_LOG_ERROR, "Couldn't get SDL2 render driver information: %s", SDL_GetError());
  138. }
  139. else {
  140. if (blog) {
  141. RenderLog(RENDER_LOG_INFO, " %2d: %s", i, rend_info->name);
  142. }
  143. //m_callback->Debug(" SDL_RENDERER_TARGETTEXTURE [%c]", (rend_info->flags & SDL_RENDERER_TARGETTEXTURE) ? 'X' : ' ');
  144. //m_callback->Debug(" SDL_RENDERER_SOFTWARE [%c]", (rend_info->flags & SDL_RENDERER_SOFTWARE) ? 'X' : ' ');
  145. //m_callback->Debug(" SDL_RENDERER_ACCELERATED [%c]", (rend_info->flags & SDL_RENDERER_ACCELERATED) ? 'X' : ' ');
  146. //m_callback->Debug(" SDL_RENDERER_PRESENTVSYNC [%c]", (rend_info->flags & SDL_RENDERER_PRESENTVSYNC) ? 'X' : ' ');
  147. }
  148. }
  149. free(rend_info);
  150. m_renderer = SDL_CreateRenderer(m_sdl_window, -1,
  151. SDL_RENDERER_TARGETTEXTURE |
  152. SDL_RENDERER_PRESENTVSYNC |
  153. SDL_RENDERER_ACCELERATED);
  154. if (m_renderer == NULL)
  155. {
  156. RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) Couldn't get a accelerated renderer: %s", SDL_GetError());
  157. RenderLog(RENDER_LOG_INFO, "RENDER: (SDL2) trying with a software renderer");
  158. m_renderer = SDL_CreateRenderer(m_sdl_window, -1,
  159. SDL_RENDERER_TARGETTEXTURE |
  160. SDL_RENDERER_SOFTWARE);
  161. if (m_renderer == NULL)
  162. {
  163. RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) Couldn't get a software renderer: %s", SDL_GetError());
  164. RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) giving up...");
  165. VideoRenderDestroy();
  166. return -3;
  167. }
  168. }
  169. /* Allocate a renderer info struct*/
  170. SDL_RendererInfo* render_info = (SDL_RendererInfo*)malloc(sizeof(SDL_RendererInfo));
  171. if (NULL == render_info){
  172. RenderLog(RENDER_LOG_ERROR, "RENDER: Couldn't allocate memory for the renderer info data structure");
  173. VideoRenderDestroy();
  174. return -5;
  175. }
  176. /* Print the name of the current rendering driver */
  177. if (SDL_GetRendererInfo(m_renderer, render_info) < 0){
  178. RenderLog(RENDER_LOG_ERROR, "Couldn't get SDL2 rendering driver information: %s", SDL_GetError());
  179. }
  180. if (blog) {
  181. RenderLog(RENDER_LOG_INFO, "RENDER: rendering driver in use: %s", render_info->name);
  182. }
  183. //m_callback->Debug(" SDL_RENDERER_TARGETTEXTURE [%c]", (render_info->flags & SDL_RENDERER_TARGETTEXTURE) ? 'X' : ' ');
  184. //m_callback->Debug(" SDL_RENDERER_SOFTWARE [%c]", (render_info->flags & SDL_RENDERER_SOFTWARE) ? 'X' : ' ');
  185. //m_callback->Debug(" SDL_RENDERER_ACCELERATED [%c]", (render_info->flags & SDL_RENDERER_ACCELERATED) ? 'X' : ' ');
  186. //m_callback->Debug(" SDL_RENDERER_PRESENTVSYNC [%c]", (render_info->flags & SDL_RENDERER_PRESENTVSYNC) ? 'X' : ' ');
  187. blog = false;
  188. free(render_info);
  189. SDL_RenderSetLogicalSize(m_renderer, m_videowidth, m_videoheight);
  190. SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_NONE);
  191. m_rending_texture = SDL_CreateTexture(m_renderer,
  192. GetPixelFormat(),
  193. SDL_TEXTUREACCESS_STREAMING,
  194. m_videowidth,
  195. m_videoheight);
  196. if (m_rending_texture == NULL){
  197. RenderLog(RENDER_LOG_ERROR, "RENDER: (SDL2) Couldn't get a texture for rendering: %s", SDL_GetError());
  198. VideoRenderDestroy();
  199. return -4;
  200. }
  201. return 0;
  202. }
  203. static int refresh_video(void *opaque)
  204. {
  205. VideoRenderImpl* pImpl = (VideoRenderImpl*)opaque;
  206. pImpl->RenderLog(RENDER_LOG_DEBUG, "function enter %s:%d", __FUNCTION__, __LINE__);
  207. while (pImpl->GetReFreshFlag()) {
  208. SDL_Event event;
  209. event.type = REFRESH_EVENT;
  210. SDL_PushEvent(&event);
  211. SDL_Delay(RVC_DEFAULT_DELAY_TIME);
  212. }
  213. pImpl->RenderLog(RENDER_LOG_DEBUG, "function leave %s:%d", __FUNCTION__, __LINE__);
  214. return 0;
  215. }
  216. static SDL_HitTestResult SDLCALL SDL_HitTestCallback(SDL_Window *win, const SDL_Point *area, void *data)
  217. {
  218. int w, h;
  219. const int RESIZE_BORDER = 8;
  220. const int DRAGGABLE_TITLE = 32;
  221. VideoRenderImpl* pImpl = (VideoRenderImpl*)data;
  222. SDL_GetWindowSize(win, &w, &h);
  223. if (area->x < RESIZE_BORDER) {
  224. if (area->y < RESIZE_BORDER) {
  225. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_RESIZE_TOPLEFT");
  226. return SDL_HITTEST_RESIZE_TOPLEFT;
  227. } else if (area->y >= (h-RESIZE_BORDER)) {
  228. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_RESIZE_BOTTOMLEFT");
  229. return SDL_HITTEST_RESIZE_BOTTOMLEFT;
  230. } else {
  231. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_RESIZE_LEFT");
  232. return SDL_HITTEST_RESIZE_LEFT;
  233. }
  234. } else if (area->x >= (w-RESIZE_BORDER)) {
  235. if (area->y < RESIZE_BORDER) {
  236. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_RESIZE_TOPRIGHT");
  237. return SDL_HITTEST_RESIZE_TOPRIGHT;
  238. } else if (area->y >= (h-RESIZE_BORDER)) {
  239. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_RESIZE_BOTTOMRIGHT");
  240. return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
  241. } else {
  242. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_RESIZE_RIGHT");
  243. return SDL_HITTEST_RESIZE_RIGHT;
  244. }
  245. } else if (area->y >= (h-RESIZE_BORDER)) {
  246. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_RESIZE_BOTTOM");
  247. return SDL_HITTEST_RESIZE_BOTTOM;
  248. } else if (area->y < RESIZE_BORDER) {
  249. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_RESIZE_TOP");
  250. return SDL_HITTEST_RESIZE_TOP;
  251. } else if (area->y < DRAGGABLE_TITLE) {
  252. pImpl->RenderLog(RENDER_LOG_DEBUG, "SDL_HITTEST_DRAGGABLE");
  253. return SDL_HITTEST_DRAGGABLE;
  254. }
  255. return SDL_HITTEST_NORMAL;
  256. }
  257. bool VideoRenderImpl::GetReFreshFlag()
  258. {
  259. return m_refresh_flag;
  260. }
  261. int VideoRenderImpl::StartVideoRender()
  262. {
  263. m_refresh_flag = true;
  264. m_refresh_thread = SDL_CreateThread(refresh_video,"refresh video thread", this);
  265. RenderLog(RENDER_LOG_DEBUG, "%s:%d m_refresh_thread is 0x%08x.",__FUNCTION__, __LINE__, m_refresh_thread);
  266. if (m_bmoveable){
  267. SDL_SetWindowHitTest(m_sdl_window, SDL_HitTestCallback, this);
  268. }
  269. return 0;
  270. }
  271. int VideoRenderImpl::ShowVideoWindow()
  272. {
  273. int iret = -1;
  274. if (NULL != m_sdl_window){
  275. SDL_ShowWindow(m_sdl_window);
  276. iret = 0;
  277. }
  278. return iret;
  279. }
  280. int VideoRenderImpl::HideVideoWindow()
  281. {
  282. int iret = -1;
  283. if (NULL != m_sdl_window) {
  284. SDL_HideWindow(m_sdl_window);
  285. iret = 0;
  286. }
  287. return iret;
  288. }
  289. int VideoRenderImpl::StopVideoRender()
  290. {
  291. HideVideoWindow();
  292. m_refresh_flag = false;
  293. RenderLog(RENDER_LOG_DEBUG, "%s:%d m_refresh_thread is 0x%08x.",__FUNCTION__, __LINE__, m_refresh_thread);
  294. if (NULL != m_refresh_thread){
  295. SDL_WaitThread(m_refresh_thread, NULL);
  296. m_refresh_thread = NULL;
  297. RenderLog(RENDER_LOG_DEBUG, "%s:%d video refresh thread exit.",__FUNCTION__, __LINE__);
  298. }
  299. return 0;
  300. }
  301. void VideoRenderImpl::VideoRenderDestroy()
  302. {
  303. delete this;
  304. }
  305. int VideoRenderImpl::RenderVideoFrame(video_frame* pframe, RVC_RendererFlip eFlipType)
  306. {
  307. int iret = -1;
  308. if (NULL == m_sdl_window || NULL == pframe || NULL == m_renderer || NULL == m_rending_texture){
  309. return iret;
  310. }
  311. SDL_Event event;
  312. SDL_WaitEvent(&event);
  313. if(REFRESH_EVENT == event.type){
  314. SDL_SetRenderDrawColor(m_renderer, 0, 0, 0, 255); /*black*/
  315. SDL_RenderClear(m_renderer);
  316. /* since data is continuous we can use SDL_UpdateTexture
  317. * instead of SDL_UpdateYUVTexture.
  318. * no need to use SDL_Lock/UnlockTexture (it doesn't seem faster)
  319. */
  320. //SDL_QueryTexture()
  321. if (VIDEO_FORMAT_RGB24 == pframe->format || VIDEO_FORMAT_I420 == pframe->format){
  322. if (VIDEO_FORMAT_RGB24 == pframe->format){
  323. SDL_UpdateTexture(m_rending_texture, NULL, pframe->data[0], pframe->width*3);
  324. }
  325. else{
  326. SDL_UpdateTexture(m_rending_texture, NULL, pframe->data[0], pframe->width);
  327. }
  328. if (RVC_FLIP_NONE == eFlipType){
  329. SDL_RenderCopy(m_renderer, m_rending_texture, NULL, NULL);
  330. }
  331. else{
  332. SDL_Point point = {m_videowidth/2, m_videoheight/2};
  333. SDL_RenderCopyEx(m_renderer, m_rending_texture, NULL, NULL, 0, &point, (SDL_RendererFlip)eFlipType);
  334. }
  335. }
  336. else{
  337. RenderLog(RENDER_LOG_ERROR, "%s:%d not support format, return", __FUNCTION__, __LINE__);
  338. return iret;
  339. }
  340. SDL_RenderPresent(m_renderer);
  341. SDL_Delay(RVC_DEFAULT_DELAY_TIME);
  342. }
  343. iret = 0;
  344. return iret;
  345. }
  346. void VideoRenderImpl::RenderLog(render_loglevel elevel, const char* fmt, ...)
  347. {
  348. if (m_callback.debug) {
  349. va_list arg;
  350. va_start(arg, fmt);
  351. if (*m_callback.debug){
  352. (*m_callback.debug)(elevel, m_callback.user_data, fmt, arg);
  353. }
  354. va_end(arg);
  355. }
  356. }
  357. void VideoRenderImpl::ConVert24to32(unsigned char* image_in, unsigned char* image_out, int w, int h)
  358. {
  359. for (int i = 0; i < h; i++)
  360. for (int j = 0; j < w; j++) {
  361. //Big Endian or Small Endian?
  362. //"ARGB" order:high bit -> low bit.
  363. //ARGB Format Big Endian (low address save high MSB, here is A) in memory : A|R|G|B
  364. //ARGB Format Little Endian (low address save low MSB, here is B) in memory : B|G|R|A
  365. if (SDL_BYTEORDER == SDL_LIL_ENDIAN) {
  366. //Little Endian (x86): R|G|B --> B|G|R|A
  367. image_out[(i * w + j) * 4 + 0] = image_in[(i * w + j) * 3 + 2];
  368. image_out[(i * w + j) * 4 + 1] = image_in[(i * w + j) * 3 + 1];
  369. image_out[(i * w + j) * 4 + 2] = image_in[(i * w + j) * 3];
  370. image_out[(i * w + j) * 4 + 3] = '0';
  371. }
  372. else {
  373. //Big Endian: R|G|B --> A|R|G|B
  374. image_out[(i * w + j) * 4] = '0';
  375. memcpy(image_out + (i * w + j) * 4 + 1, image_in + (i * w + j) * 3, 3);
  376. }
  377. }
  378. }