Advertisement
Guest User

Untitled

a guest
Dec 23rd, 2017
281
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 15.23 KB | None | 0 0
  1. /*
  2.     libvlc with libsdl, tested on windows 7 and debian 8
  3.  
  4. // note : libvlc want pitches and lines to be multiple of 32
  5. //      : other libs are demanding somewhat the same things...(can even have an x,y offset)
  6. // note : libsdl needs continuous data for the update-texture API
  7. //        it also has an update-texture for y,u,v planes with pitches
  8. //        but both do not accept an x or y offset
  9. //        it also has the limitation that NV12 and NV21 are not supported for the per y,u,v update-texture
  10. //
  11. //   so, frames are/could be aligned to accommodate hardware and or codecs that assume things.
  12. //
  13. //   To counter the above limitations i made the streaming texture using the new aligned(frame) size
  14. //   , and specified a src-rect for the video when using render-copy.
  15. */
  16. #include <stdio.h>
  17. #include <stdint.h>
  18. #include <stdlib.h>
  19. #if defined(_WIN32)
  20. #if defined(_MSC_VER) && !defined(__cplusplus) // using visual studio and c ?
  21. #define inline __inline
  22. #if _MSC_VER <= 1700 // for VS 2012 and less
  23. // create a 'stdbool.h' file with this in it :
  24. // #pragma once
  25. // typedef int bool;
  26. // #define false 0
  27. // #define true 1
  28. // and move it to a place where the compiler/libvlc can find it
  29. #endif
  30. #endif
  31. #include "SDL.h"
  32. #include <windows.h>
  33. #else
  34. #include "SDL2/SDL.h"
  35. #endif
  36. #include "vlc/vlc.h"
  37.  
  38. #define WIDTH 640
  39. #define HEIGHT 360
  40. #define ALIGN32(type,x) ((((type)x) + (type)31) & ~(type)31)
  41.  
  42. // all we use
  43. typedef struct vid_context
  44. {
  45.     libvlc_instance_t *vlc_inst;    // 'main' libvlc instance
  46.     libvlc_media_player_t *vlc_mp;  // media player that will hold our media object created from 'file/url'
  47.     //
  48.     SDL_Window *sdl_win;
  49.     SDL_Renderer *sdl_ren;          // a place to draw
  50.     SDL_RendererInfo sdl_ren_info;  // contains the formats natively supported by current renderer
  51.     SDL_Texture *sdl_text;          // a streaming texture, to upload pixel data each frame
  52.     SDL_Rect sdl_aspect_ratio;
  53.     SDL_cond *sdl_gui_cond;         // a condition to wake up libsdl's GUI thread (no SDL_Delay(x) in poll loop)
  54.     SDL_mutex *sdl_gui_cond_lock;   // a condition needs a lock
  55.     SDL_bool sdl_gui_conditioned;   // condition fired up?
  56.     //
  57.     SDL_mutex *vid_lock;            // pixel data access is unique
  58.     Uint32 vid_sdl_format;          // format we requested from libvlc for streaming texture(may not be the same as source)
  59.     void* vid_data;                 // allocated memory for libvlc
  60.     void* vid_planes[3];            // pointers to the memory planes
  61.     int vid_pitch[3];
  62.     int w_video;                    // reported width video
  63.     int h_video;                    // reported height video
  64.     int w_frame;                    // aligned width video
  65.     int h_frame;                    // aligned height video
  66.     SDL_Rect box;                   // the rectangle in the frame that contains the video pixels
  67.     Uint32 vid_sdl_event;           // a registered libsdl user event
  68. } vid_context;
  69.  
  70. void *vlc_lock_cb(void *opaque, void **planes)
  71. {
  72.     vid_context *c = (vid_context *)opaque;
  73.     SDL_LockMutex(c->vid_lock);
  74.     planes[0] = c->vid_planes[0], planes[1] = c->vid_planes[1], planes[2] = c->vid_planes[2];
  75.     return NULL; // Picture identifier, not used here.
  76. }
  77.  
  78. void vlc_unlock_cb(void *opaque, void *id, void *const *planes)
  79. {
  80.     vid_context *c = (vid_context *)opaque;
  81.     SDL_UnlockMutex(c->vid_lock);
  82. }
  83.  
  84. void vlc_display_cb(void *opaque, void *id)
  85. {
  86.     vid_context *c = (vid_context *)opaque;
  87.     // inform SDL's thread that the frame needs to be displayed
  88.     SDL_Event ev;
  89.     ev.type = c->vid_sdl_event;
  90.     ev.user.code = 2;
  91.     SDL_PushEvent(&ev);
  92.     // wake-up SDL's thread
  93.     SDL_LockMutex(c->sdl_gui_cond_lock);
  94.     c->sdl_gui_conditioned = SDL_TRUE;
  95.     SDL_CondSignal(c->sdl_gui_cond);
  96.     SDL_UnlockMutex(c->sdl_gui_cond_lock);
  97. }
  98.  
  99. // should do: create a _vlc_to_sdl_format(), _sdl_to_vlc_chroma() and a _sdl_get_closest_supported(src_format) function
  100. unsigned vlc_format_cb(void **opaque, char *chroma, unsigned *width, unsigned *height
  101.                                     , unsigned *pitches, unsigned *lines)
  102. {
  103.     vid_context *c = (vid_context *)*opaque;
  104.     Uint32 i;
  105.     SDL_Event ev;
  106.     bool vlc_is_iyuv = (SDL_memcmp(chroma, "I420", 4) == 0)
  107.         , vlc_is_yv12 = (SDL_memcmp(chroma, "YV12", 4) == 0);
  108.     printf("[-] format : in  chroma = %s, size = %u/%u\n", chroma, *width, *height);
  109.     // no size or an unexpected callback
  110.     if ((*width) == 0 || (*height) == 0 || c->vid_data != NULL)
  111.         return 0;
  112.  
  113.     // check if SDL's current renderer supports I420(IYUV) or YV12
  114.     for ( i=0; c->vid_sdl_format == SDL_PIXELFORMAT_UNKNOWN && i < c->sdl_ren_info.num_texture_formats; i++) {
  115.         if (c->sdl_ren_info.texture_formats[i] == SDL_PIXELFORMAT_IYUV && vlc_is_iyuv)
  116.             c->vid_sdl_format = SDL_PIXELFORMAT_IYUV;
  117.         else if (c->sdl_ren_info.texture_formats[i] == SDL_PIXELFORMAT_YV12 && vlc_is_yv12)
  118.             c->vid_sdl_format = SDL_PIXELFORMAT_YV12;
  119.     }
  120.     // else default to rgb
  121.     if (c->vid_sdl_format == SDL_PIXELFORMAT_UNKNOWN) {
  122.         if (SDL_memcmp(chroma, "BGRA", 4) != 0)
  123.             SDL_memcpy(chroma, "RV32", 4); // "RV32"(24 pushed into 32),"BGRA"
  124.         c->vid_sdl_format = SDL_PIXELFORMAT_BGRA32;
  125.     }
  126.  
  127.     // set pitches/lines, allocate and copy settings
  128.     if (c->vid_sdl_format == SDL_PIXELFORMAT_BGRA32) {
  129.         pitches[0] = ALIGN32(unsigned, (*width) * 4);
  130.         pitches[1] = pitches[2] = 0;
  131.         lines[0] = ALIGN32(unsigned, (*height));
  132.         lines[1] = lines[2] = 0;
  133.         c->w_frame = pitches[0] / 4;
  134.         c->h_frame = lines[0];
  135.         c->vid_data = SDL_malloc((pitches[0] * lines[0]) + 31);
  136.         c->vid_planes[0] = (void*)ALIGN32(uintptr_t, c->vid_data);
  137.         c->vid_planes[1] = c->vid_planes[2] = NULL;
  138.     } else { // I420(IYUV) and YV12
  139.         pitches[0] = ALIGN32(unsigned, (*width));
  140.         pitches[1] = pitches[2] = pitches[0] / 2;
  141.         lines[0] = ALIGN32(unsigned, (*height));
  142.         lines[1] = lines[2] = lines[0] / 2;
  143.         c->w_frame = pitches[0];
  144.         c->h_frame = lines[0];
  145.         c->vid_data = SDL_malloc((pitches[0] * lines[0]) + (pitches[1] * lines[1]) + (pitches[2] * lines[2]) + 31);
  146.         c->vid_planes[0] = (void*)ALIGN32(uintptr_t, c->vid_data);
  147.         c->vid_planes[1] = (void*)((uintptr_t)c->vid_planes[0] + (pitches[0] * lines[0]));
  148.         c->vid_planes[2] = (void*)((uintptr_t)c->vid_planes[1] + (pitches[1] * lines[1]));
  149.     }
  150.     c->vid_pitch[0] = (int)pitches[0], c->vid_pitch[1] = c->vid_pitch[2] = (int)pitches[1];
  151.     c->box.w = c->w_video = (int)*width;
  152.     c->box.h = c->h_video = (int)*height;
  153.     c->box.x = c->box.y = 0;
  154.  
  155.     // inform SDL's thread the video and frame size are known
  156.     ev.type = c->vid_sdl_event;
  157.     ev.user.code = 1;
  158.     SDL_PushEvent(&ev);
  159.  
  160.     printf("[-] format : out chroma = %s, size = %d/%d, frame = %d/%d, format = %s\n"
  161.         , chroma, c->w_video, c->h_video, c->w_frame, c->h_frame, SDL_GetPixelFormatName(c->vid_sdl_format));
  162.     return (c->vid_data != NULL);
  163. }
  164.  
  165. void sdl_renderer_change(vid_context *vid)
  166. {
  167.     if (vid->sdl_text != NULL) SDL_DestroyTexture(vid->sdl_text);
  168.     if (vid->sdl_ren != NULL)  SDL_DestroyRenderer(vid->sdl_ren);
  169.  
  170.     vid->sdl_ren = SDL_CreateRenderer(vid->sdl_win, -1, 0);
  171.     SDL_GetRendererInfo(vid->sdl_ren, &vid->sdl_ren_info);
  172.     if (vid->sdl_text != NULL)
  173.         vid->sdl_text = SDL_CreateTexture( vid->sdl_ren, vid->vid_sdl_format, SDL_TEXTUREACCESS_STREAMING, vid->w_frame, vid->h_frame);
  174. }
  175.  
  176. void sdl_draw_frame(vid_context *vid)
  177. {
  178.     SDL_SetRenderDrawBlendMode(vid->sdl_ren, SDL_BLENDMODE_NONE);
  179.     SDL_SetRenderDrawColor(vid->sdl_ren, 0, 0, 0, SDL_ALPHA_OPAQUE);
  180.     SDL_RenderClear(vid->sdl_ren);
  181.  
  182.     if (vid->sdl_text != NULL) {
  183.         SDL_LockMutex(vid->vid_lock);
  184.         SDL_UpdateTexture(vid->sdl_text, NULL, vid->vid_planes[0], vid->vid_pitch[0]);
  185.         SDL_UnlockMutex(vid->vid_lock);
  186.         SDL_RenderCopy(vid->sdl_ren, vid->sdl_text, &vid->box, &vid->sdl_aspect_ratio);
  187.     }
  188.  
  189.     // draw something on top of the video
  190.     {
  191.         SDL_Rect rc = { 4, 4, 32, 32};
  192.         SDL_SetRenderDrawBlendMode(vid->sdl_ren, SDL_BLENDMODE_BLEND);
  193.         SDL_SetRenderDrawColor(vid->sdl_ren, 22, 80, 43, 127);
  194.         SDL_RenderFillRect(vid->sdl_ren, &rc);
  195.         SDL_SetRenderDrawColor(vid->sdl_ren, 70, 255, 0, 63);
  196.         SDL_RenderDrawRect(vid->sdl_ren, &rc);
  197.         rc.x+=4, rc.y+=4, rc.w-=8, rc.h-=8;
  198.         SDL_RenderDrawRect(vid->sdl_ren, &rc);
  199.         rc.x+=4, rc.y+=4, rc.w-=8, rc.h-=8;
  200.         SDL_RenderDrawRect(vid->sdl_ren, &rc);
  201.     }
  202.  
  203.     SDL_RenderPresent(vid->sdl_ren);
  204. }
  205.  
  206. int init_libs(vid_context *vid, const char *url, int win_width, int win_height)
  207. {
  208.     libvlc_media_t *vlc_m;
  209.     char const *vlc_argv[] = {
  210.         "--ignore-config",
  211.         //"--verbose=2"
  212.     };
  213.     int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);
  214.     Uint32 i;
  215.  
  216.     if (SDL_Init(SDL_INIT_VIDEO) < 0) {
  217.         printf("Could not initialize SDL: %s.\n", SDL_GetError());
  218.         return EXIT_FAILURE;
  219.     }
  220.     SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
  221.     SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); // direct3d, opengl, software
  222.  
  223.     vid->sdl_win = SDL_CreateWindow("ArgvPlayer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED
  224.                                     , win_width, win_height, SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
  225.     if (vid->sdl_win == NULL) {
  226.         printf("Could not create window: %s\n", SDL_GetError());
  227.         return EXIT_FAILURE;
  228.     }
  229.     vid->sdl_ren = SDL_CreateRenderer(vid->sdl_win, -1, 0);
  230.     if (vid->sdl_ren == NULL) {
  231.         printf("Could not create renderer: %s\n", SDL_GetError());
  232.         return EXIT_FAILURE;
  233.     }
  234.  
  235.     SDL_GetRendererInfo(vid->sdl_ren, &vid->sdl_ren_info);
  236.     printf("[-] renderer name = %s, num_texture_formats = %u\n"
  237.         , vid->sdl_ren_info.name, vid->sdl_ren_info.num_texture_formats);
  238.     for ( i=0; i < vid->sdl_ren_info.num_texture_formats; i++)
  239.         printf("[-] %u = %s\n", i+1, SDL_GetPixelFormatName(vid->sdl_ren_info.texture_formats[i]));
  240.    
  241.     vid->sdl_text = NULL;
  242.     vid->vid_data = NULL;
  243.     vid->vid_sdl_format = SDL_PIXELFORMAT_UNKNOWN;
  244.     vid->vid_sdl_event = SDL_RegisterEvents(1);
  245.     vid->vid_lock = SDL_CreateMutex();
  246.     vid->sdl_gui_cond_lock = SDL_CreateMutex();
  247.     if (vid->vid_lock == NULL || vid->sdl_gui_cond_lock == NULL) {
  248.         printf("Could not create mutex: %s\n", SDL_GetError());
  249.         return EXIT_FAILURE;
  250.     }
  251.     vid->sdl_gui_cond = SDL_CreateCond();
  252.     if (vid->sdl_gui_cond == NULL) {
  253.         printf("Could not create condition: %s\n", SDL_GetError());
  254.         return EXIT_FAILURE;
  255.     }
  256.     vid->sdl_gui_conditioned = SDL_FALSE;
  257.    
  258.     vid->vlc_inst = libvlc_new(vlc_argc, vlc_argv);
  259.     if (vid->vlc_inst == NULL) {
  260.         printf("Could not initialize LibVLC: %s.\n", libvlc_errmsg());
  261.         return EXIT_FAILURE;
  262.     }
  263.  
  264.     if (SDL_strstr(url,"://") != NULL)
  265.         vlc_m = libvlc_media_new_location(vid->vlc_inst, url);
  266.     else
  267.         vlc_m = libvlc_media_new_path(vid->vlc_inst, url);
  268.     if (vlc_m == NULL) {
  269.         printf("Could not create new media for LibVLC: %s.\n", libvlc_errmsg());
  270.         return EXIT_FAILURE;
  271.     }
  272.     libvlc_media_parse(vlc_m);
  273.  
  274.     vid->vlc_mp = libvlc_media_player_new_from_media(vlc_m);
  275.     if (vid->vlc_mp == NULL) {
  276.         printf("Could not create new media player for LibVLC: %s.\n", libvlc_errmsg());
  277.         return EXIT_FAILURE;
  278.     }
  279.     libvlc_media_release(vlc_m); // media_player holds a reference now
  280.  
  281.     libvlc_video_set_callbacks(vid->vlc_mp, vlc_lock_cb, vlc_unlock_cb, vlc_display_cb, vid);
  282.     libvlc_video_set_format_callbacks(vid->vlc_mp, vlc_format_cb, NULL);
  283.  
  284.     libvlc_audio_set_volume(vid->vlc_mp, 25);
  285.     libvlc_media_player_play(vid->vlc_mp);
  286.     return 0;
  287. }
  288.  
  289. int main(int argc, char *argv[])
  290. {
  291.     vid_context vid_ctx;
  292.     SDL_Event ev;
  293.     Uint32 fullscreen = 0, done = 0, do_draw = 0;
  294.     SDL_Rect view_tmp;
  295.     float r;
  296. #if defined(_WIN32)
  297.     AllocConsole();
  298.     freopen("CONOUT$", "w", stdout);
  299. #endif
  300.  
  301.     if (argc < 2) {
  302.         printf("Usage: %s <filename/url>\n", argv[0]);
  303.         SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Input", "Give me a <filename/url>", NULL);
  304.         return EXIT_FAILURE;
  305.     }
  306.     if (init_libs( &vid_ctx, argv[1] , WIDTH, HEIGHT) != 0) {
  307.         SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Oopsie Doopsie", "Don't shoot the messenger", NULL);
  308.         return EXIT_FAILURE;
  309.     }
  310.  
  311.     // Main loop.
  312.     while (!done)
  313.     {
  314.         while (SDL_PollEvent( &ev )) {
  315.             switch (ev.type) {
  316.             case SDL_QUIT: done = 1; break;
  317.             case SDL_KEYDOWN:
  318.                 switch(ev.key.keysym.sym) {
  319.                 case SDLK_ESCAPE: done = 1; break;
  320.                 case SDLK_SPACE:
  321.                     if (libvlc_media_player_can_pause(vid_ctx.vlc_mp))
  322.                         libvlc_media_player_pause(vid_ctx.vlc_mp);
  323.                     break;
  324.                 default: break;
  325.                 }
  326.                 break;
  327.             case SDL_MOUSEWHEEL:
  328.                 libvlc_audio_set_volume(vid_ctx.vlc_mp, libvlc_audio_get_volume(vid_ctx.vlc_mp) + (ev.wheel.y * 4));
  329.                 break;
  330.             case SDL_MOUSEBUTTONUP:
  331.                 if (ev.button.clicks == 2)
  332.                     SDL_SetWindowFullscreen(vid_ctx.sdl_win, (fullscreen ^= 1 ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
  333.                 break;
  334.             case SDL_WINDOWEVENT:
  335.                 switch(ev.window.event) {
  336.                 case SDL_WINDOWEVENT_SIZE_CHANGED: // keep video's aspect ratio
  337.                     SDL_RenderGetViewport(vid_ctx.sdl_ren, &view_tmp);
  338.                     vid_ctx.sdl_aspect_ratio.x = vid_ctx.sdl_aspect_ratio.y = 0
  339.                         , vid_ctx.sdl_aspect_ratio.w = view_tmp.w
  340.                         , vid_ctx.sdl_aspect_ratio.h = view_tmp.h;
  341.                     r = ((float)view_tmp.w / view_tmp.h) - ((float)vid_ctx.w_video / vid_ctx.h_video);
  342.                     if (r > 0.0f) {
  343.                         vid_ctx.sdl_aspect_ratio.w = (int)(((float)view_tmp.h / vid_ctx.h_video) * (float)vid_ctx.w_video);
  344.                         vid_ctx.sdl_aspect_ratio.x = (int)((float)(view_tmp.w - vid_ctx.sdl_aspect_ratio.w) * .5f);
  345.                     } else if (r < 0.0f) {
  346.                         vid_ctx.sdl_aspect_ratio.h = (int)(((float)view_tmp.w / vid_ctx.w_video) * (float)vid_ctx.h_video);
  347.                         vid_ctx.sdl_aspect_ratio.y = (int)((float)(view_tmp.h - vid_ctx.sdl_aspect_ratio.h) * .5f);
  348.                     }
  349.                     break;
  350.                 case SDL_WINDOWEVENT_RESIZED:
  351.                     sdl_renderer_change(&vid_ctx);
  352.                 case SDL_WINDOWEVENT_EXPOSED:
  353.                     if (do_draw == 0)
  354.                         do_draw = 2;
  355.                 default:
  356.                     break;
  357.                 }
  358.                 break;
  359.             case SDL_RENDER_DEVICE_RESET:
  360.                 // note SDL : Must ignore existing textures pointers(all is lost), and recreate and reload all textures
  361.                 if (vid_ctx.sdl_text != NULL)
  362.                     vid_ctx.sdl_text = SDL_CreateTexture(vid_ctx.sdl_ren, vid_ctx.vid_sdl_format
  363.                                             , SDL_TEXTUREACCESS_STREAMING, vid_ctx.w_frame, vid_ctx.h_frame);
  364.             case SDL_RENDER_TARGETS_RESET:
  365.                 // note SDL : The render targets have been reset and their contents need to be updated
  366.                 //            , the textures are re-allocated but the image data in them is lost.
  367.                 if (do_draw == 0)
  368.                     do_draw = 2;
  369.                 break;
  370.             default:
  371.                 if (ev.type == vid_ctx.vid_sdl_event) {
  372.                     if (ev.user.code == 2) {
  373.                         if (do_draw != 1)
  374.                             do_draw = 1;
  375.                         else
  376.                             printf("[x] SDL dropped a frame\n");
  377.                     }
  378.                     else if (ev.user.code == 1) {
  379.                         SDL_SetWindowSize(vid_ctx.sdl_win, vid_ctx.w_video, vid_ctx.h_video);
  380.                         vid_ctx.sdl_text = SDL_CreateTexture( vid_ctx.sdl_ren, vid_ctx.vid_sdl_format
  381.                                             , SDL_TEXTUREACCESS_STREAMING, vid_ctx.w_frame, vid_ctx.h_frame);
  382.                     }
  383.                 }
  384.                 break;
  385.             } // end of switch(ev.type)
  386.         } // end of while poll sdl event
  387.  
  388.  
  389.         if (do_draw != 0) {
  390.             do_draw = 0;
  391.             sdl_draw_frame(&vid_ctx);
  392.         }
  393.         // wait for the next frame
  394.         SDL_LockMutex(vid_ctx.sdl_gui_cond_lock);
  395.         if (!vid_ctx.sdl_gui_conditioned)
  396.             SDL_CondWaitTimeout(vid_ctx.sdl_gui_cond, vid_ctx.sdl_gui_cond_lock, 100);
  397.         vid_ctx.sdl_gui_conditioned = SDL_FALSE;
  398.         SDL_UnlockMutex(vid_ctx.sdl_gui_cond_lock);
  399.     }
  400.  
  401.     // Stop and clean up libVLC.
  402.     libvlc_media_player_stop(vid_ctx.vlc_mp);
  403.     libvlc_media_player_release(vid_ctx.vlc_mp);
  404.     libvlc_release(vid_ctx.vlc_inst);
  405.     // Close window and clean up libSDL.
  406.     SDL_DestroyMutex(vid_ctx.vid_lock);
  407.     SDL_DestroyMutex(vid_ctx.sdl_gui_cond_lock);
  408.     SDL_DestroyCond(vid_ctx.sdl_gui_cond);
  409.     if (vid_ctx.vid_data)
  410.         SDL_free(vid_ctx.vid_data);
  411.     if (vid_ctx.sdl_text)
  412.         SDL_DestroyTexture(vid_ctx.sdl_text);
  413.     SDL_DestroyRenderer(vid_ctx.sdl_ren);
  414.     SDL_DestroyWindow(vid_ctx.sdl_win);
  415.     SDL_Quit();
  416.     return 0;
  417. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement