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