Last active
August 4, 2024 12:40
-
-
Save trikko/738d0a5e76458cab39218a5055f02416 to your computer and use it in GitHub Desktop.
How to render a video with raylib and gstreamer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "raylib.h" | |
#include <gst/gst.h> | |
#include <gst/app/gstappsink.h> | |
GstElement* createPipeline(const char* filename) | |
{ | |
GError *error; | |
gchar *pipelineString = g_strdup_printf ("filesrc location=%s ! tee name=t ! queue ! decodebin ! videoconvert ! appsink name=output caps=video/x-raw,format=RGBA,pixel-aspect-ratio=1/1 t. ! queue ! decodebin ! audioconvert ! audioresample ! autoaudiosink", filename); | |
GstElement *pipeline = gst_parse_launch (pipelineString, &error); | |
if (error != NULL) { | |
g_print ("Something goes wrong with pipeline initialization: %s\n", error->message); | |
g_clear_error (&error); | |
exit (-1); | |
} | |
g_free(pipelineString); | |
return pipeline; | |
} | |
void restartPipeline(GstElement *pipeline) | |
{ | |
gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, 0); | |
gst_element_set_state (pipeline, GST_STATE_PLAYING); | |
} | |
int main(int argc, char** argv) | |
{ | |
// Gstreamer Initialization | |
gst_init (&argc, &argv); | |
GstElement *pipeline = createPipeline("BigBuckBunny.mp4"); | |
GstElement *sink = gst_bin_get_by_name(GST_BIN (pipeline), "output"); | |
GstMapInfo mapInfo; | |
gint64 duration, position; | |
int frameWidth = 0, frameHeight = 0; | |
int renderWidth = 0, renderHeight = 0; | |
float renderScale = -1.0f; | |
int status = 0; | |
// Raylib Initialization | |
//-------------------------------------------------------------------------------------- | |
RenderTexture2D renderTexture; | |
int screenWidth = 800; | |
int screenHeight = 450; | |
SetConfigFlags(FLAG_WINDOW_RESIZABLE); | |
InitWindow(screenWidth, screenHeight, "raylib + gstreamer"); | |
// Start video | |
gst_element_set_state(pipeline, GST_STATE_PLAYING); | |
restartPipeline(pipeline); | |
// Main game loop | |
while (!WindowShouldClose()) // Detect window close button or ESC key | |
{ | |
// Update duration & position | |
gst_element_query_duration(pipeline, GST_FORMAT_TIME, &duration); | |
gst_element_query_position(pipeline, GST_FORMAT_TIME, &position); | |
// Keyboard events | |
//---------------------------------------------------------------------------------- | |
if (IsKeyPressed(KEY_SPACE)) | |
{ | |
GstState state; | |
gst_element_get_state (pipeline, &state, NULL, 1); | |
if (state == GST_STATE_PLAYING) | |
{ | |
status = 1; | |
gst_element_set_state(pipeline, GST_STATE_PAUSED); | |
} | |
else | |
{ | |
status = 0; | |
gst_element_set_state (pipeline, GST_STATE_PLAYING); | |
} | |
} | |
else if (IsKeyPressed(KEY_R)) | |
{ | |
status = 2; | |
restartPipeline(pipeline); | |
} | |
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) | |
{ | |
Vector2 mouse = GetMousePosition(); | |
if (mouse.x >= 5 && mouse.x <= screenWidth-5 && mouse.y>=screenHeight-15 && mouse.y<=screenHeight-5) | |
gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, (gint64)((1.0f*(mouse.x - 5)/(screenWidth-10))*duration)); | |
} | |
//---------------------------------------------------------------------------------- | |
// Draw | |
//---------------------------------------------------------------------------------- | |
BeginDrawing(); | |
ClearBackground(RAYWHITE); | |
// Read a sample, if available | |
GstSample* sample = gst_app_sink_try_pull_sample(GST_APP_SINK(sink), 1); | |
if (IsWindowResized()) renderScale = -1.0f; | |
// First time: get frame width & height from video sample | |
if (sample != NULL && frameWidth == 0) | |
{ | |
GstCaps *caps = gst_sample_get_caps (sample); | |
GstStructure *s = gst_caps_get_structure (caps, 0); | |
gst_structure_get_int (s, "width", &frameWidth); | |
gst_structure_get_int (s, "height", &frameHeight); | |
renderTexture = LoadRenderTexture(frameWidth, frameHeight); | |
} | |
// Scale video to fit window if needed | |
if (renderScale < 0 && frameWidth > 0) | |
{ | |
screenWidth = GetScreenWidth(); | |
screenHeight = GetScreenHeight(); | |
renderWidth = screenWidth; | |
renderHeight = (int)(screenWidth * 1.0f*frameHeight/frameWidth); | |
if (renderHeight > screenHeight) | |
{ | |
renderHeight = screenHeight; | |
renderWidth = (int)(screenHeight * 1.0f*frameWidth/frameHeight); | |
} | |
renderScale = 1.0f*renderWidth/frameWidth; | |
} | |
// Render a frame from video, if available. | |
if (sample != NULL) | |
{ | |
GstBuffer *buffer = gst_sample_get_buffer(sample); | |
GstMapInfo mapInfo; | |
if (gst_buffer_map (buffer, &mapInfo, GST_MAP_READ)) | |
{ | |
UpdateTexture(renderTexture.texture, mapInfo.data); | |
gst_buffer_unmap (buffer, &mapInfo); | |
} | |
gst_sample_unref(sample); | |
} | |
DrawTextureEx(renderTexture.texture, (Vector2){(screenWidth-renderWidth)/2, (screenHeight-renderHeight)/2 }, 0, 1.0f*renderWidth/frameWidth, WHITE); | |
// Draw progress | |
DrawRectangle(5, screenHeight-15, screenWidth-10, 10, RAYWHITE); | |
DrawRectangle(5, screenHeight-15, (int)((screenWidth-10)*(1.0f*position/duration)), 10, BLUE); | |
// Draw the texts | |
DrawRectangle(4,4,222,82,GRAY); | |
DrawRectangle(5,5,220,80,RAYWHITE); | |
DrawText("Press SPACE to play/pause video", 15, 15, 10, GRAY); | |
DrawText("Press R to restart video", 15, 30, 10, GRAY); | |
// Draw status | |
switch (status) | |
{ | |
case 0: DrawText("PLAYING", 15, 60, 20, GREEN); break; | |
case 1: DrawText("PAUSED", 15, 60, 20, BLUE); break; | |
case 2: DrawText("RESTARTED", 15, 60, 20, GREEN); break; | |
} | |
EndDrawing(); | |
//---------------------------------------------------------------------------------- | |
} | |
// De-Initialization | |
//-------------------------------------------------------------------------------------- | |
gst_element_set_state(pipeline, GST_STATE_NULL); | |
gst_object_unref(pipeline); | |
gst_deinit(); | |
UnloadRenderTexture(renderTexture); | |
CloseWindow(); // Close window and OpenGL context | |
//-------------------------------------------------------------------------------------- | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment