#include #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}; GLfloat g_fScale = 1.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; gboolean g_bRMBPressed = FALSE; GLuint g_uiColorBuffer; 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* g_pPipeline = NULL; GstElement* pVideo = NULL; GstElement* g_pPipelineH264 = NULL; GstElement* pVideoH264 = NULL; gboolean g_bInitialized = FALSE; gchar* g_pcFrameBuffer = NULL; GMutex* g_pFrameBufferMutex = NULL; GLfloat g_afAlpha = 1.0f; GLfloat g_fMovieAspect = 1.0f; GLuint g_uiDisplayList = 0; GstBuffer* g_pTheBuffer = NULL; gint get_pad_width (GstPad* pVideoPad) { gint iWidth = 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", &iWidth); } else g_print ("gst_pad_width() - Could not get caps for the pad!\n"); return iWidth; } gint get_pad_height (GstPad* pVideoPad) { gint iHeight = 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, "height", &iHeight); } else g_print ("gst_pad_height() - Could not get caps for the pad!\n"); return iHeight; } gdouble get_pad_fps (GstPad* pVideoPad) { gint aiFps[2] = {0, 0}; GstCaps* pCaps = NULL; GstStructure* pStructure = NULL; gdouble fFpsRate = 0.0f; pCaps = gst_pad_get_negotiated_caps (pVideoPad); if (pCaps) { pStructure = gst_caps_get_structure (pCaps, 0); gst_structure_get_fraction (pStructure, "framerate", &aiFps[0], &aiFps[1]); fFpsRate = (gdouble) aiFps[0] / (gdouble) aiFps[1]; } else g_print ("gst_pad_fps() - Could not get caps for the pad!\n"); return fFpsRate; } gdouble get_pad_movie_aspect (GstPad* pVideoPad) { gint iWidth = 0; gint iHeight = 0; gint aiPixelAspect[2] = {0, 0}; gdouble fAspectRatio = 0.0f; 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", &iWidth); gst_structure_get_int (pStructure, "height", &iHeight); gst_structure_get_fraction (pStructure, "pixel-aspect-ratio", &aiPixelAspect[0], &aiPixelAspect[1]); if (aiPixelAspect[0] == 0) fAspectRatio = (gdouble) iWidth / (gdouble) g_iHeight; else { fAspectRatio = (gdouble) aiPixelAspect[0] / (gdouble) aiPixelAspect[1] * (gdouble) iWidth / (gdouble) iHeight; } } else g_print ("get_pad_movie_aspect() - Could not get caps for the pad!\n"); return fAspectRatio; } gint64 get_pipeline_current_position (GstElement* pPipeline) { GstFormat fmt = GST_FORMAT_TIME; gint64 iPosition = 0L; gst_element_query_position (pPipeline, &fmt, &iPosition); return iPosition; } gint64 get_pipeline_total_duration (GstElement* pPipeline) { GstFormat fmt = GST_FORMAT_TIME; gint64 iLength = 0L; gst_element_query_duration (pPipeline, &fmt, &iLength); return iLength; } void seek_to_time (GstElement* pPipeline, gint64 time_nanoseconds) { if (!gst_element_seek (pPipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, time_nanoseconds, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) { g_print ("Seek failed!\n"); } } gboolean end_func (GtkWidget* pWidget, GdkEvent* pEvent, gpointer data) { gtk_main_quit (); return TRUE; } gboolean draw_handler (GtkWidget* pWidget) { static gboolean bNeedsRedraw = FALSE; if (g_bInitialized && g_mutex_trylock (g_pFrameBufferMutex)) { glBindTexture (GL_TEXTURE_RECTANGLE_ARB, g_uiColorBuffer); glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, g_iWidth, g_iHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, g_pcFrameBuffer); g_mutex_unlock (g_pFrameBufferMutex); bNeedsRedraw = TRUE; } if (bNeedsRedraw) 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); 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 ); glEnable (GL_NORMALIZE); 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); } 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; 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, 50.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_RECTANGLE_ARB); glLineWidth (1.0f); 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); glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenTextures (1, &g_uiColorBuffer); glBindTexture (GL_TEXTURE_RECTANGLE_ARB, g_uiColorBuffer); glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, g_iWidth, g_iHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); g_uiDisplayList = glGenLists (1); gdk_gl_drawable_gl_end (pGlDrawable); return TRUE; } static gboolean expose_event(GtkWidget* pWidget, GdkEventExpose* pEvent, gpointer data) { GdkGLContext* pGlContext = gtk_widget_get_gl_context (pWidget); GdkGLDrawable* pGlDrawable = gtk_widget_get_gl_drawable (pWidget); GLfloat afMatDiffuseFront[4] = {1.0f * g_afAlpha, 1.0f * g_afAlpha, 1.0f * g_afAlpha, g_afAlpha}; GLfloat afMatDiffuseBack[4] = {0.0f * g_afAlpha, 0.0f * g_afAlpha, 0.0f * g_afAlpha, g_afAlpha}; GLfloat afVector[3]; GLfloat afMatrixBase[16]; gulong ulMilliSeconds = 0L; gdouble fFrameTimeDelta = 0.0f; gdouble fFullSecond = 0.0f; if (!gdk_gl_drawable_gl_begin (pGlDrawable, pGlContext)) return FALSE; glPushMatrix (); glTranslatef (0.0f, 0.0f, 0.5f); 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]); glScalef (g_fScale, g_fScale, g_fScale); glEnable (GL_TEXTURE_RECTANGLE_ARB); glBindTexture (GL_TEXTURE_RECTANGLE_ARB, g_uiColorBuffer); glMaterialfv (GL_FRONT, GL_DIFFUSE, afMatDiffuseFront); glMaterialfv (GL_BACK, GL_DIFFUSE, afMatDiffuseBack); glCallList (g_uiDisplayList); glPopMatrix (); if (gdk_gl_drawable_is_double_buffered (pGlDrawable)) gdk_gl_drawable_swap_buffers (pGlDrawable); else glFlush (); gdk_gl_drawable_gl_end (pGlDrawable); 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, GMainLoop* pLoop) { static gboolean bPlaying = TRUE; if (pEvent->type == GDK_KEY_PRESS) { switch (pEvent->keyval) { case GDK_q : case GDK_Escape : end_func (pWidget, NULL, NULL); break; case GDK_p : if (bPlaying) { gst_element_set_state (g_pPipelineH264, GST_STATE_PAUSED); g_print ("pipeline paused\n"); bPlaying = FALSE; } else { gst_element_set_state (g_pPipelineH264, GST_STATE_PLAYING); g_print ("pipeline playing\n"); bPlaying = TRUE; } break; case GDK_s : g_main_loop_quit (pLoop); g_print ("stopping playback\n"); gst_element_set_state (g_pPipelineH264, GST_STATE_NULL); g_print ("deleting pipeline\n"); gst_object_unref (GST_OBJECT (g_pPipelineH264)); pLoop = NULL; g_pPipelineH264 = NULL; break; default : break; } } return TRUE; } gboolean button_handler (GtkWidget* pWidget, GdkEventButton* pEvent, gpointer data) { switch (pEvent->button) { case 1: if (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->type == GDK_BUTTON_RELEASE) g_bLMBPressed = FALSE; break; case 3: if (pEvent->type == GDK_BUTTON_PRESS) { g_fCurrentX = pEvent->x; g_fCurrentY = pEvent->y; g_fLastX = g_fCurrentX; g_fLastY = g_fCurrentY; g_bRMBPressed = TRUE; } else if (pEvent->type == GDK_BUTTON_RELEASE) g_bRMBPressed = FALSE; break; } return TRUE; } gboolean scroll_handler (GtkWidget* pWidget, GdkEventScroll* pEvent, gpointer data) { switch (pEvent->direction) { case GDK_SCROLL_UP: if (g_afAlpha < 1.0f) g_afAlpha += 0.05f; break; case GDK_SCROLL_DOWN: if (g_afAlpha > 0.0f) g_afAlpha -= 0.05f; break; /* just silence gcc */ default: break; } 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); } if (g_bRMBPressed) { g_fCurrentY = pEvent->y; g_fDeltaY = g_fLastY - g_fCurrentY; g_fLastY = g_fCurrentY; g_fScale -= g_fDeltaY / 100.0f; gtk_widget_queue_draw (pWidget); } return TRUE; } 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_pad_added (GstElement* pDemuxer, GstPad* pPad, GstElement* pDecoder) { gchar* pcName = NULL; pcName = gst_pad_get_name (pPad); if (!g_ascii_strncasecmp (pcName, "video", 5)) if (!gst_element_link_pads (pDemuxer, pcName, pDecoder, "sink")) g_print ("could not link demuxer with decoder\n"); g_free (pcName); } static void on_handoff (GstElement* pFakeSink, GstBuffer* pBuffer, GstPad* pPad, GtkWidget* pWidget) { g_iWidth = get_pad_width (pPad); g_iHeight = get_pad_height (pPad); g_fMovieAspect = get_pad_movie_aspect (pPad); if (g_bInitialized) { g_mutex_lock (g_pFrameBufferMutex); g_memmove (g_pcFrameBuffer, GST_BUFFER_DATA (pBuffer), g_iWidth * g_iHeight * 3); g_mutex_unlock (g_pFrameBufferMutex); } } gboolean spit_out_clip_stats (GstPad* pVideoPad) { GLfloat afTexCoords[2] = {(GLfloat) g_iWidth, (GLfloat) g_iHeight}; g_print ("The video is %dx%d at %6.3f fps, movie-aspect is %3.2f:1\n", get_pad_width (pVideoPad), get_pad_height (pVideoPad), get_pad_fps (pVideoPad), get_pad_movie_aspect (pVideoPad)); g_mutex_lock (g_pFrameBufferMutex); g_pcFrameBuffer = g_malloc0 (g_iWidth * g_iHeight * 3); if (g_pcFrameBuffer) g_bInitialized = TRUE; g_mutex_unlock (g_pFrameBufferMutex); if (g_uiDisplayList) { glNewList (g_uiDisplayList, GL_COMPILE); glBegin (GL_QUADS); glNormal3f (0.0f, 0.0f, 1.0f); glTexCoord2f (afTexCoords[0], 0.0f); glVertex3f (g_fMovieAspect, 1.0f, 0.0f); glTexCoord2f (0.0f, 0.0f); glVertex3f (-g_fMovieAspect, 1.0f, 0.0f); glTexCoord2f (0.0f, afTexCoords[1]); glVertex3f (-g_fMovieAspect, -1.0f, 0.0f); glTexCoord2f (afTexCoords[0], afTexCoords[1]); glVertex3f (g_fMovieAspect, -1.0f, 0.0f); glEnd (); glEndList (); } else g_print ("configure_event() - Crap, no display-list was generated!\n"); /* remove callback after the first call */ return FALSE; } gboolean build_pipeline (gchar* pcFilename, GMainLoop** pLoopH264, GstElement** pPipelineH264, GstBusFunc busCallbackFunc, GCallback padAddedFunc, GstPad** pVideoPadH264, GCallback handoffHandlerFunc, gpointer pDrawingArea) { GstElement* pSourceH264 = NULL; GstElement* pDemuxerH264 = NULL; GstElement* pDecoderH264 = NULL; GstElement* pVideoConverterH264 = NULL; GstElement* pVideoSinkH264 = NULL; *pLoopH264 = g_main_loop_new (NULL, FALSE); *pPipelineH264 = gst_pipeline_new ("pipeline-h264"); gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (*pPipelineH264)), busCallbackFunc, *pLoopH264); pSourceH264 = gst_element_factory_make ("filesrc", "source-h264"); g_object_set (G_OBJECT (pSourceH264), "location", pcFilename, NULL); pDemuxerH264 = gst_element_factory_make ("qtdemux", "demuxer-h264"); pDecoderH264 = gst_element_factory_make ("ffdec_h264", "decoder-h264"); gst_bin_add_many (GST_BIN (*pPipelineH264), pSourceH264, pDemuxerH264, pDecoderH264, NULL); if (!gst_element_link_pads (pSourceH264, "src", pDemuxerH264, "sink")) g_print ("build_pipeline() - could not link filesrc with qtdemux\n"); g_signal_connect (pDemuxerH264, "pad-added", padAddedFunc, pDecoderH264); pVideoH264 = gst_bin_new ("videobin-h264"); pVideoConverterH264 = gst_element_factory_make ("ffmpegcolorspace", "vconv"); *pVideoPadH264 = gst_element_get_pad (pVideoConverterH264, "sink"); pVideoSinkH264 = gst_element_factory_make ("fakesink", "sink-h264"); g_object_set (G_OBJECT (pVideoSinkH264), "sync", TRUE, "signal-handoffs", TRUE, NULL); g_signal_connect (pVideoSinkH264, "handoff", handoffHandlerFunc, pDrawingArea); gst_bin_add_many (GST_BIN (pVideoH264), pVideoConverterH264, pVideoSinkH264, NULL); gst_element_link_filtered (pVideoConverterH264, pVideoSinkH264, gst_caps_new_simple ("video/x-raw-rgb", NULL)); gst_element_add_pad (pVideoH264, gst_ghost_pad_new ("sink", *pVideoPadH264)); gst_object_unref (*pVideoPadH264); gst_bin_add (GST_BIN (*pPipelineH264), pVideoH264); if (!gst_element_link_pads (pDecoderH264, "src", pVideoH264, "sink")) g_print ("build_pipeline() - could not link ffdec_h264 with ffmpegcolorspace\n"); return TRUE; } int main (int argc, char** argv) { GtkWidget* pWindow = NULL; GdkGLConfig* pGlConfig = NULL; GtkWidget* pDrawingArea = NULL; guint uiDrawHandlerId = 0; gint iWidth; gint iHeight; GMainLoop* pLoopH264 = NULL; GstPad* pVideoPadH264 = NULL; g_thread_init (NULL); gtk_init (&argc,&argv); gtk_gl_init (&argc,&argv); gst_init (&argc, &argv); g_pFrameBufferMutex = g_mutex_new (); 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), "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), "scroll-event", G_CALLBACK (scroll_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 = 400; 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_resizable (GTK_WINDOW (pWindow), FALSE); gtk_widget_set_app_paintable (pWindow, FALSE); gtk_window_set_decorated (GTK_WINDOW (pWindow), TRUE); gtk_widget_show (pWindow); uiDrawHandlerId = g_timeout_add (16, (GSourceFunc) draw_handler, pDrawingArea); g_pTimerId = g_timer_new (); /* make sure we got some input */ if (argc != 2) { g_print ("Usage: %s \n", argv[0]); return -1; } build_pipeline (argv[1], &pLoopH264, &g_pPipelineH264, bus_call, (GCallback) on_pad_added, &pVideoPadH264, (GCallback) on_handoff, (gpointer) pDrawingArea); g_signal_connect (G_OBJECT (pWindow), "key-press-event", G_CALLBACK (key_press_handler), pLoopH264); g_timeout_add (1000, (GSourceFunc) spit_out_clip_stats, pVideoPadH264); /* Now set to playing and iterate. */ gst_element_set_state (g_pPipelineH264, GST_STATE_PLAYING); g_main_loop_run (pLoopH264); gtk_main (); glDeleteTextures (1, &g_uiColorBuffer); if (g_bInitialized) { g_mutex_lock (g_pFrameBufferMutex); g_free (g_pcFrameBuffer); g_mutex_unlock (g_pFrameBufferMutex); } g_mutex_free (g_pFrameBufferMutex); if (g_pPipelineH264) { gst_element_set_state (g_pPipelineH264, GST_STATE_NULL); gst_object_unref (GST_OBJECT (g_pPipelineH264)); } return 0; }