#include #include #include #include #include #include #include #include #include #define FOVY_2 20.0 #define Z_NEAR 3.0 GLfloat g_afAngle[3] = {0.0f, 0.0f, 0.0f}; gint g_iFrames = 0; GTimer* g_pTimerId = NULL; gboolean g_bKeepRunning = TRUE; gdouble g_fLastTimeStamp = 0.0f; gdouble g_fCurrentTimeStamp = 0.0f; gdouble g_fLastFullSecond = 0.0f; gboolean g_bLMBPressed = FALSE; GLuint g_uiFrameBuffer; GLuint g_uiColorBuffer; GLuint g_uiSomeTexture; gdouble g_fCurrentX; gdouble g_fCurrentY; gdouble g_fDeltaX; gdouble g_fDeltaY; gdouble g_fLastX; gdouble g_fLastY; gint g_iWidth = 1; gint g_iHeight = 1; GstElement* pPipeline = NULL; GstElement* pVideo = NULL; gboolean g_bGLInitialized = FALSE; gint next_power_of_two (gint iValue) { gint iResult = 1; while (iValue > iResult) iResult = iResult << 1; return iResult; } gboolean end_func (GtkWidget* pWidget, GdkEvent* pEvent, gpointer data) { gtk_main_quit (); return TRUE; } gboolean draw_handler (GtkWidget* pWidget) { if (!g_bLMBPressed) { g_afAngle[0] += 2.1f; g_afAngle[1] -= 1.6f; g_afAngle[2] += 1.1f; gtk_widget_queue_draw (pWidget); } return TRUE; } void realize (GtkWidget* pWidget, gpointer data) { GdkGLContext* pGlContext = gtk_widget_get_gl_context (pWidget); GdkGLDrawable* pGlDrawable = gtk_widget_get_gl_drawable (pWidget); /* OpenGL BEGIN */ if (!gdk_gl_drawable_gl_begin (pGlDrawable, pGlContext)) return; glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f (0.0f, 0.0f, 0.0f, 0.0f); glClearColor (0.0f, 0.0f, 0.0f, 0.0f); glClearDepth (1.0f); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); g_print ("OpenGL version: %s\n", glGetString (GL_VERSION)); g_print ("OpenGL vendor: %s\n", glGetString (GL_VENDOR)); g_print ("OpenGL renderer: %s\n", glGetString (GL_RENDERER)); gdk_gl_drawable_gl_end (pGlDrawable); /* OpenGL END */ } gboolean configure_event (GtkWidget* pWidget, GdkEventConfigure* pEvent, gpointer data) { GdkGLContext* pGlContext = gtk_widget_get_gl_context (pWidget); GdkGLDrawable* pGlDrawable = gtk_widget_get_gl_drawable (pWidget); GLsizei w = pWidget->allocation.width; GLsizei h = pWidget->allocation.height; /* OpenGL BEGIN */ if (!gdk_gl_drawable_gl_begin (pGlDrawable, pGlContext)) return FALSE; glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport (0, 0, w, h); glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective (2.0f * FOVY_2, (GLfloat) w / (GLfloat) h, 0.1f, 10.0f); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); gluLookAt (0.0f, 0.0f, Z_NEAR, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); glTranslatef (0.0f, 0.0f, -Z_NEAR); glEnable (GL_DEPTH_TEST); glEnable (GL_TEXTURE_2D); glLineWidth (2.5f); glEnable (GL_LIGHTING); glEnable (GL_LIGHT0); glPolygonMode (GL_BACK, GL_FILL); glPolygonMode (GL_FRONT, GL_FILL); glDisable (GL_CULL_FACE); glLightModelf (GL_LIGHT_MODEL_TWO_SIDE, 1.0f); glEnable (GL_LINE_SMOOTH); glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glGenTextures (1, &g_uiColorBuffer); glBindTexture (GL_TEXTURE_2D, g_uiColorBuffer); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, next_power_of_two (g_iWidth), next_power_of_two (g_iHeight), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); g_bGLInitialized = TRUE; gdk_gl_drawable_gl_end (pGlDrawable); /* OpenGL END */ return TRUE; } static gboolean expose_event(GtkWidget* pWidget, GdkEventExpose* pEvent, gpointer data) { GLfloat fAspectRatio = (GLfloat) g_iWidth / (GLfloat) g_iHeight; GdkGLContext* pGlContext = gtk_widget_get_gl_context (pWidget); GdkGLDrawable* pGlDrawable = gtk_widget_get_gl_drawable (pWidget); GLfloat afAlpha = 1.0f; GLfloat afMatDiffuseFront[4] = {0.5f * afAlpha, 0.25f * afAlpha, 0.125f * afAlpha, afAlpha}; GLfloat afMatAmbientFront[4] = {0.1f * afAlpha, 0.05f * afAlpha, 0.025f * afAlpha, afAlpha}; GLfloat afMatSpecularFront[4] = {0.5f * afAlpha, 0.45f * afAlpha, 0.4f * afAlpha, afAlpha}; GLfloat afMatDiffuseBack[4] = {0.0f * afAlpha, 0.0f * afAlpha, 0.5f * afAlpha, afAlpha}; GLfloat afMatAmbientBack[4] = {0.0f * afAlpha, 0.0f * afAlpha, 0.25f * afAlpha, afAlpha}; GLfloat afMatSpecularBack[4] = {0.4f * afAlpha, 0.4f * afAlpha, 0.5f * afAlpha, afAlpha}; GLfloat afVector[3]; GLfloat afMatrixBase[16]; gulong ulMilliSeconds = 0L; gdouble fFrameTimeDelta = 0.0f; gdouble fFullSecond = 0.0f; GLfloat afTexCoords[2] = {(GLfloat) g_iWidth / (GLfloat) next_power_of_two (g_iWidth), (GLfloat) g_iHeight / (GLfloat) next_power_of_two (g_iHeight)}; /* OpenGL BEGIN */ if (!gdk_gl_drawable_gl_begin (pGlDrawable, pGlContext)) return FALSE; /* draw a spinning rectangle */ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glGetFloatv (GL_MODELVIEW_MATRIX, afMatrixBase); afVector[0] = afMatrixBase[0]; afVector[1] = afMatrixBase[4]; afVector[2] = afMatrixBase[8]; glRotatef (g_afAngle[1] * 0.5f, afVector[0], afVector[1], afVector[2]); afVector[0] = afMatrixBase[1]; afVector[1] = afMatrixBase[5]; afVector[2] = afMatrixBase[9]; glRotatef (g_afAngle[0] * 0.5f, afVector[0], afVector[1], afVector[2]); g_afAngle[0] = g_afAngle[1] = g_afAngle[2] = 0.0f; glEnable (GL_TEXTURE_2D); glBindTexture (GL_TEXTURE_2D, g_uiColorBuffer); glBegin (GL_QUADS); glColor4f (1.0f * afAlpha, 0.0f * afAlpha, 1.0f * afAlpha, afAlpha); glMaterialfv (GL_FRONT, GL_DIFFUSE, afMatDiffuseFront); glMaterialfv (GL_FRONT, GL_AMBIENT, afMatAmbientFront); glMaterialfv (GL_FRONT, GL_SPECULAR, afMatSpecularFront); glMaterialfv (GL_BACK, GL_DIFFUSE, afMatDiffuseBack); glMaterialfv (GL_BACK, GL_AMBIENT, afMatAmbientBack); glMaterialfv (GL_BACK, GL_SPECULAR, afMatSpecularBack); glNormal3f (0.0f, 0.0f, 1.0f); glTexCoord2f (0.0f, 0.0f); glVertex3f (fAspectRatio, 1.0f, 0.0f); glTexCoord2f (afTexCoords[0], 0.0f); glVertex3f (-fAspectRatio, 1.0f, 0.0f); glTexCoord2f (afTexCoords[0], afTexCoords[1]); glVertex3f (-fAspectRatio, -1.0f, 0.0f); glTexCoord2f (0.0f, afTexCoords[1]); glVertex3f (fAspectRatio, -1.0f, 0.0f); glEnd (); if (gdk_gl_drawable_is_double_buffered (pGlDrawable)) gdk_gl_drawable_swap_buffers (pGlDrawable); else glFlush (); gdk_gl_drawable_gl_end (pGlDrawable); /* OpenGL END */ g_fCurrentTimeStamp = g_timer_elapsed (g_pTimerId, &ulMilliSeconds); fFrameTimeDelta = g_fCurrentTimeStamp - g_fLastTimeStamp; fFullSecond = g_fCurrentTimeStamp - g_fLastFullSecond; if (fFullSecond < 1.0f) g_iFrames++; else { g_print ("fps: %d, last frame-time: %f\n", g_iFrames, fFrameTimeDelta); g_iFrames = 0; g_fLastFullSecond = g_fCurrentTimeStamp; } g_fLastTimeStamp = g_fCurrentTimeStamp; return TRUE; } gboolean key_press_handler (GtkWidget* pWidget, GdkEventKey* pEvent, gpointer data) { if (pEvent->type == GDK_KEY_PRESS) { switch (pEvent->keyval) { case GDK_q : case GDK_Escape : end_func (pWidget, NULL, data); break; default : break; } } return TRUE; } gboolean button_handler (GtkWidget* pWidget, GdkEventButton* pEvent, gpointer data) { if (pEvent->button == 1 && pEvent->type == GDK_BUTTON_PRESS) { g_fCurrentX = pEvent->x; g_fCurrentY = pEvent->y; g_fLastX = g_fCurrentX; g_fLastY = g_fCurrentY; g_bLMBPressed = TRUE; } else if (pEvent->button == 1 && pEvent->type == GDK_BUTTON_RELEASE) g_bLMBPressed = FALSE; return TRUE; } gboolean motion_handler (GtkWidget* pWidget, GdkEventMotion *pEvent, gpointer data) { if (g_bLMBPressed) { g_fCurrentX = pEvent->x; g_fCurrentY = pEvent->y; g_fDeltaX = g_fLastX - g_fCurrentX; g_fDeltaY = g_fLastY - g_fCurrentY; g_fLastX = g_fCurrentX; g_fLastY = g_fCurrentY; g_afAngle[0] -= g_fDeltaX; g_afAngle[1] -= g_fDeltaY; gtk_widget_queue_draw (pWidget); } return TRUE; } /*---8< 8<---*/ static gboolean bus_call (GstBus* pBus, GstMessage* pMessage, gpointer data) { GMainLoop* pLoop = data; switch (GST_MESSAGE_TYPE (pMessage)) { case GST_MESSAGE_EOS: g_print ("End-of-stream\n"); g_main_loop_quit (pLoop); break; case GST_MESSAGE_ERROR: { gchar* pcDebug = NULL; GError* pError = NULL; gst_message_parse_error (pMessage, &pError, &pcDebug); g_free (pcDebug); g_print ("Error: %s\n", pError->message); g_error_free (pError); g_main_loop_quit (pLoop); break; } default: break; } return TRUE; } static void on_newpad (GstElement* pDecodeBin, GstPad* pPad, gboolean bLast, gpointer data) { GstCaps* pCaps = NULL; GstStructure* pStructure = NULL; GstPad* pVideoPad = NULL; /* only link once */ pVideoPad = gst_element_get_pad (pVideo, "sink"); if (GST_PAD_IS_LINKED (pVideoPad)) { g_object_unref (pVideoPad); return; } /* check media type */ pCaps = gst_pad_get_caps (pPad); pStructure = gst_caps_get_structure (pCaps, 0); if (!g_strrstr (gst_structure_get_name (pStructure), "video")) { gst_caps_unref (pCaps); gst_object_unref (pVideoPad); } gst_caps_unref (pCaps); gst_pad_link (pPad, pVideoPad); } static void on_handoff (GstElement* pFakeSink, GstBuffer* pBuffer, GstPad* pPad, GtkWidget* pWidget) { GstCaps* pCaps = NULL; GstStructure* pStructure = NULL; gint iWidth; gint iHeight; pCaps = gst_pad_get_negotiated_caps (pPad); if (pCaps) { pStructure = gst_caps_get_structure (pCaps, 0); gst_structure_get_int (pStructure, "width", &iWidth); gst_structure_get_int (pStructure, "height", &iHeight); } else g_print ("on_handoff() - could not get the negotiated pad\n"); if (!g_bGLInitialized) return; glBindTexture (GL_TEXTURE_2D, g_uiColorBuffer); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, next_power_of_two (iWidth), next_power_of_two (iHeight), 0, GL_RGB, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (pBuffer)); gtk_widget_queue_draw (pWidget); } gboolean spit_out_dims (GstPad* pVideoPad) { gint aiFrameRate[2] = {0, 0}; GstCaps* pCaps = NULL; GstStructure* pStructure = NULL; pCaps = gst_pad_get_negotiated_caps (pVideoPad); if (pCaps) { pStructure = gst_caps_get_structure (pCaps, 0); gst_structure_get_int (pStructure, "width", &g_iWidth); gst_structure_get_int (pStructure, "height", &g_iHeight); gst_structure_get_fraction (pStructure, "framerate", &aiFrameRate[0], &aiFrameRate[1]); g_print ("The video is %dx%d at %6.3f fps (%dx%d)\n", g_iWidth, g_iHeight, (float) aiFrameRate[0] / (float) aiFrameRate[1], next_power_of_two (g_iWidth), next_power_of_two (g_iHeight)); } else g_print ("get_dims() - could not get the negotiated pad\n"); /* remove callback after the first call */ return FALSE; } /*--->8 >8---*/ int main (int argc, char** argv) { GtkWidget* pWindow = NULL; GdkGLConfig* pGlConfig = NULL; GtkWidget* pDrawingArea = NULL; guint uiDrawHandlerId = 0; gint iWidth; gint iHeight; GMainLoop* pLoop = NULL; GstElement* pSource = NULL; GstElement* pDecoder = NULL; GstElement* pVideoConverter = NULL; GstElement* pVideoSink = NULL; GstPad* pVideoPad = NULL; gtk_init (&argc,&argv); gtk_gl_init (&argc,&argv); pGlConfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB | GDK_GL_MODE_ALPHA | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE); if (!pGlConfig) return 1; pWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); if (!pWindow) return 2; gtk_widget_add_events (pWindow, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); g_signal_connect (G_OBJECT (pWindow), "delete-event", G_CALLBACK (end_func), NULL); g_signal_connect (G_OBJECT (pWindow), "key-press-event", G_CALLBACK (key_press_handler), NULL); g_signal_connect (G_OBJECT (pWindow), "button-press-event", G_CALLBACK (button_handler), NULL); g_signal_connect (G_OBJECT (pWindow), "button-release-event", G_CALLBACK (button_handler), NULL); g_signal_connect (G_OBJECT (pWindow), "motion-notify-event", G_CALLBACK (motion_handler), (gpointer) pDrawingArea); pDrawingArea = gtk_drawing_area_new (); if (!pDrawingArea) return 3; iWidth = 800; iHeight = 600; gtk_widget_set_size_request (pDrawingArea, iWidth, iHeight); gtk_widget_set_gl_capability (pDrawingArea, pGlConfig, NULL, TRUE, GDK_GL_RGBA_TYPE); g_signal_connect_after (G_OBJECT (pDrawingArea), "realize", G_CALLBACK (realize), NULL); g_signal_connect (G_OBJECT (pDrawingArea), "configure-event", G_CALLBACK (configure_event), NULL); g_signal_connect (G_OBJECT (pDrawingArea), "expose-event", G_CALLBACK (expose_event), NULL); gtk_container_add (GTK_CONTAINER (pWindow), pDrawingArea); gtk_widget_show (pDrawingArea); gtk_window_set_decorated (GTK_WINDOW (pWindow), TRUE); gtk_window_set_resizable (GTK_WINDOW (pWindow), TRUE); gtk_widget_show (pWindow); uiDrawHandlerId = g_timeout_add (16, (GSourceFunc) draw_handler, pDrawingArea); g_pTimerId = g_timer_new (); /* init GStreamer */ gst_init (&argc, &argv); pLoop = g_main_loop_new (NULL, FALSE); /* make sure we have input */ if (argc != 2) { g_print ("Usage: %s \n", argv[0]); return -1; } /* setup */ pPipeline = gst_pipeline_new ("pipeline"); gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (pPipeline)), bus_call, pLoop); pSource = gst_element_factory_make ("filesrc", "source"); g_object_set (G_OBJECT (pSource), "location", argv[1], NULL); pDecoder = gst_element_factory_make ("decodebin", "decoder"); g_signal_connect (pDecoder, "new-decoded-pad", G_CALLBACK (on_newpad), NULL); gst_bin_add_many (GST_BIN (pPipeline), pSource, pDecoder, NULL); gst_element_link (pSource, pDecoder); /* create video output */ pVideo = gst_bin_new ("videobin"); pVideoConverter = gst_element_factory_make ("ffmpegcolorspace", "vconv"); pVideoPad = gst_element_get_pad (pVideoConverter, "sink"); pVideoSink = gst_element_factory_make ("fakesink", "sink"); g_object_set (G_OBJECT (pVideoSink), "sync", TRUE, "signal-handoffs", TRUE, NULL); g_signal_connect (pVideoSink, "handoff", G_CALLBACK (on_handoff), (gpointer) pDrawingArea); gst_bin_add_many (GST_BIN (pVideo), pVideoConverter, pVideoSink, NULL); gst_element_link_filtered (pVideoConverter, pVideoSink, gst_caps_new_simple ("video/x-raw-rgb", NULL)); gst_element_add_pad (pVideo, gst_ghost_pad_new ("sink", pVideoPad)); gst_object_unref (pVideoPad); gst_bin_add (GST_BIN (pPipeline), pVideo); g_timeout_add (1000, (GSourceFunc) spit_out_dims, pVideoPad); /* Now set to playing and iterate. */ g_print ("Setting to PLAYING\n"); gst_element_set_state (pPipeline, GST_STATE_PLAYING); g_print ("Running\n"); g_main_loop_run (pLoop); gtk_main (); glDeleteTextures (1, &g_uiColorBuffer); /* clean up nicely */ g_print ("Returned, stopping playback\n"); gst_element_set_state (pPipeline, GST_STATE_NULL); g_print ("Deleting pipeline\n"); gst_object_unref (GST_OBJECT (pPipeline)); return 0; }