playback.d 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // quantumde1 developed software, licensed under MIT license.
  2. module graphics.playback;
  3. import std.stdio;
  4. import raylib;
  5. import raylib.rlgl;
  6. import core.stdc.stdlib;
  7. import core.stdc.string;
  8. import core.thread;
  9. import variables;
  10. import core.sync.mutex;
  11. import std.file;
  12. import system.abstraction;
  13. extern (C)
  14. {
  15. struct libvlc_instance_t;
  16. struct libvlc_media_t;
  17. struct libvlc_media_player_t;
  18. struct libvlc_event_manager_t;
  19. struct libvlc_event_t;
  20. long libvlc_media_player_get_length(libvlc_media_player_t* player);
  21. long libvlc_media_player_get_time(libvlc_media_player_t* player);
  22. void libvlc_media_player_set_time(libvlc_media_player_t* player, long time);
  23. libvlc_instance_t* libvlc_new(int argc, const(char)** argv);
  24. void libvlc_release(libvlc_instance_t* instance);
  25. libvlc_media_t* libvlc_media_new_location(libvlc_instance_t* instance, const(char)* mrl);
  26. void libvlc_media_release(libvlc_media_t* media);
  27. libvlc_media_player_t* libvlc_media_player_new_from_media(libvlc_media_t* media);
  28. void libvlc_media_player_release(libvlc_media_player_t* player);
  29. void libvlc_media_player_play(libvlc_media_player_t* player);
  30. void libvlc_media_player_stop(libvlc_media_player_t* player);
  31. int libvlc_media_player_get_state(libvlc_media_player_t* player);
  32. void libvlc_video_set_callbacks(libvlc_media_player_t* player,
  33. void* function(void*, void**),
  34. void function(void*, void*, void*),
  35. void* function(void*),
  36. void* opaque);
  37. void libvlc_video_set_format(libvlc_media_player_t* player,
  38. const(char)* chroma,
  39. uint width,
  40. uint height,
  41. uint pitch);
  42. void libvlc_video_get_size(libvlc_media_player_t* player,
  43. uint num,
  44. uint* px,
  45. uint* py);
  46. libvlc_event_manager_t* libvlc_media_player_event_manager(libvlc_media_player_t* player);
  47. void libvlc_event_attach(libvlc_event_manager_t* event_manager,
  48. int event_type,
  49. void function(const(libvlc_event_t)*, void*),
  50. void* user_data);
  51. int libvlc_event_detach(libvlc_event_manager_t* event_manager,
  52. int event_type,
  53. void function(const(libvlc_event_t)*, void*),
  54. void* user_data);
  55. }
  56. enum libvlc_state_t
  57. {
  58. libvlc_NothingSpecial = 0,
  59. libvlc_Opening,
  60. libvlc_Buffering,
  61. libvlc_Playing,
  62. libvlc_Paused,
  63. libvlc_Stopped,
  64. libvlc_Ended,
  65. libvlc_Error
  66. }
  67. enum libvlc_event_type_t
  68. {
  69. libvlc_MediaPlayerEndReached = 256
  70. }
  71. extern (C) void endReachedCallback(const(libvlc_event_t)* event, void* user_data)
  72. {
  73. auto callback = cast(void function(void*)) user_data;
  74. callback(user_data);
  75. }
  76. extern (C) void libvlc_media_player_set_media_player_end_reached_callback(
  77. libvlc_media_player_t* player,
  78. void function(void*), void* user_data)
  79. {
  80. auto event_manager = libvlc_media_player_event_manager(player);
  81. libvlc_event_attach(event_manager, libvlc_event_type_t.libvlc_MediaPlayerEndReached, &endReachedCallback,
  82. cast(void*) user_data);
  83. }
  84. struct Video
  85. {
  86. uint texW, texH;
  87. float scale;
  88. Mutex mutex;
  89. Texture2D texture;
  90. ubyte* buffer;
  91. bool needUpdate;
  92. libvlc_media_player_t* player;
  93. }
  94. extern (C) void* begin_vlc_rendering(void* data, void** p_pixels)
  95. {
  96. auto video = cast(Video*) data;
  97. video.mutex.lock();
  98. *p_pixels = video.buffer;
  99. return null;
  100. }
  101. extern (C) void end_vlc_rendering(void* data, void* id, void* p_pixels)
  102. {
  103. auto video = cast(Video*) data;
  104. video.needUpdate = true;
  105. video.mutex.unlock();
  106. }
  107. extern (C) void videoEndCallback(void* data)
  108. {
  109. videoFinished = true;
  110. debug debugWriteln("Video ended");
  111. }
  112. Video* add_new_video(libvlc_instance_t* libvlc, const(char)* src, const(char)* protocol)
  113. {
  114. auto video = cast(Video*) malloc(Video.sizeof);
  115. if (video is null)
  116. {
  117. debug debugWriteln("Failed to allocate memory for video.");
  118. return null;
  119. }
  120. video.mutex = new Mutex;
  121. auto location = cast(char*) malloc(strlen(protocol) + strlen(src) + 3);
  122. if (location is null)
  123. {
  124. debug debugWriteln("Failed to allocate memory for location.");
  125. free(video);
  126. return null;
  127. }
  128. sprintf(location, "%s://%s", protocol, src);
  129. auto media = libvlc_media_new_location(libvlc, location);
  130. free(location);
  131. if (media is null)
  132. {
  133. debug debugWriteln("Failed to create media.");
  134. free(video);
  135. return null;
  136. }
  137. video.player = libvlc_media_player_new_from_media(media);
  138. libvlc_media_release(media);
  139. if (video.player is null)
  140. {
  141. debug debugWriteln("Failed to create media player.");
  142. free(video);
  143. return null;
  144. }
  145. video.needUpdate = false;
  146. video.texW = 0;
  147. video.texH = 0;
  148. video.buffer = null;
  149. video.texture.id = 0;
  150. libvlc_video_set_callbacks(video.player, &begin_vlc_rendering, &end_vlc_rendering, null, video);
  151. libvlc_media_player_set_media_player_end_reached_callback(video.player, &videoEndCallback, video);
  152. return video;
  153. }
  154. void cleanup_video(Video* video)
  155. {
  156. if (video is null)
  157. return;
  158. libvlc_media_player_stop(video.player);
  159. libvlc_media_player_release(video.player);
  160. if (video.texture.id != 0)
  161. {
  162. UnloadTexture(video.texture);
  163. }
  164. video.mutex.destroy();
  165. if (video.buffer !is null)
  166. {
  167. MemFree(video.buffer);
  168. }
  169. free(video);
  170. }
  171. extern (C) void playVideoInternal(char* argv)
  172. {
  173. const(char)*[] vlcArgs;
  174. debug
  175. {
  176. vlcArgs = [
  177. "--verbose=1", "--no-xlib", "--drop-late-frames", "--live-caching=0",
  178. "--no-lua"
  179. ];
  180. }
  181. else
  182. {
  183. vlcArgs = [
  184. "--verbose=-1", "--no-xlib", "--drop-late-frames", "--live-caching=0",
  185. "--no-lua"
  186. ];
  187. }
  188. auto libvlc = libvlc_new(cast(int) vlcArgs.length, cast(const(char)**) vlcArgs.ptr);
  189. if (libvlc is null)
  190. {
  191. debug debugWriteln("Something went wrong with libvlc init. Turn on DEBUG in conf/build_type.conf at BUILD_TYPE field to get more logs.");
  192. videoFinished = true;
  193. return;
  194. }
  195. Video*[] video_list;
  196. auto new_video = add_new_video(libvlc, argv, "file");
  197. if (new_video is null)
  198. {
  199. libvlc_release(libvlc);
  200. return;
  201. }
  202. video_list ~= new_video;
  203. libvlc_media_player_play(new_video.player);
  204. debug debugWriteln("Video started playing");
  205. while (!WindowShouldClose())
  206. {
  207. auto player = new_video.player;
  208. if (libvlc_media_player_get_state(player) == libvlc_state_t.libvlc_Ended)
  209. {
  210. debug debugWriteln("Video reached end.");
  211. videoFinished = true;
  212. }
  213. if (videoFinished)
  214. {
  215. break;
  216. }
  217. BeginDrawing();
  218. ClearBackground(Colors.BLACK);
  219. UpdateMusicStream(music);
  220. foreach (video; video_list)
  221. {
  222. if (video.buffer is null)
  223. {
  224. if (libvlc_media_player_get_state(video.player) == libvlc_state_t.libvlc_Playing)
  225. {
  226. libvlc_video_get_size(video.player, 0, &video.texW, &video.texH);
  227. if (video.texW > 0 && video.texH > 0)
  228. {
  229. float screenWidth = cast(float) GetScreenWidth();
  230. float screenHeight = cast(float) GetScreenHeight();
  231. float videoAspectRatio = cast(float) video.texW / cast(float) video.texH;
  232. float screenAspectRatio = screenWidth / screenHeight;
  233. // Calculate scale based on aspect ratio
  234. video.scale = (videoAspectRatio < screenAspectRatio) ?
  235. (screenHeight / cast(float) video.texH) : (
  236. screenWidth / cast(float) video.texW);
  237. // Set video format only once
  238. if (video.texture.id == 0)
  239. {
  240. libvlc_video_set_format(video.player, "RV24", video.texW, video.texH, video.texW * 3);
  241. video.mutex.lock();
  242. // Load the texture and assign the ID to the texture struct
  243. video.texture.id = rlLoadTexture(null, video.texW, video.texH, PixelFormat
  244. .PIXELFORMAT_UNCOMPRESSED_R8G8B8, 1);
  245. video.texture.width = video.texW;
  246. video.texture.height = video.texH;
  247. video.texture.format = PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8;
  248. video.texture.mipmaps = 1;
  249. // Allocate buffer for video frame
  250. video.buffer = cast(ubyte*) MemAlloc(video.texW * video.texH * 3);
  251. video.needUpdate = false;
  252. video.mutex.unlock();
  253. debug debugWriteln("Video texture initialized");
  254. }
  255. }
  256. }
  257. }
  258. else
  259. {
  260. if (video.needUpdate)
  261. {
  262. video.mutex.lock();
  263. UpdateTexture(video.texture, video.buffer);
  264. video.needUpdate = false;
  265. video.mutex.unlock();
  266. }
  267. Vector2 position = {
  268. (GetScreenWidth() - video.texW * video.scale) * 0.5f, (
  269. GetScreenHeight() - video.texH * video.scale) * 0.5f
  270. };
  271. SetTextureFilter(video.texture, TextureFilter.TEXTURE_FILTER_BILINEAR);
  272. DrawTextureEx(video.texture, position, 0, video.scale, Colors.WHITE);
  273. }
  274. }
  275. if (IsKeyPressed(KeyboardKey.KEY_ENTER) || IsMouseButtonPressed(MouseButton.MOUSE_BUTTON_LEFT))
  276. {
  277. foreach (video; video_list)
  278. {
  279. cleanup_video(video);
  280. }
  281. videoFinished = true;
  282. video_list.length = 0;
  283. EndDrawing();
  284. libvlc_release(libvlc);
  285. return;
  286. }
  287. EndDrawing();
  288. }
  289. foreach (video; video_list)
  290. {
  291. cleanup_video(video);
  292. }
  293. videoFinished = true;
  294. video_list.length = 0;
  295. libvlc_release(libvlc);
  296. return;
  297. }
  298. void playVideo(string filename) {
  299. version (Posix)
  300. playVideoInternal(cast(char*)(getcwd() ~ "/" ~ filename));
  301. version (Windows)
  302. playVideoInternal(cast(char*)("/" ~ getcwd() ~ "/" ~ filename));
  303. }