LLMediaImplGStreamer:: LLMediaImplGStreamer () : mediaData ( NULL ), mMediaRowbytes ( 1 ), mTextureFormatPrimary ( LL_MEDIA_BGRA ), mTextureFormatType ( LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV ), mPump ( NULL ), mPlaybin ( NULL ), mVideoSink ( NULL ) #ifdef LL_GST_SOUNDSINK ,mAudioSink ( NULL ) #endif // LL_GST_SOUNDSINK { DEBUGMSG("constructing media..."); setMediaDepth(4); // Create a pumpable main-loop for this media mPump = g_main_loop_new (NULL, FALSE); if (!mPump) { return; // error } // instantiate a playbin element to do the hard work mPlaybin = llgst_element_factory_make ("playbin", "play"); if (!mPlaybin) { // todo: cleanup pump return; // error } if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { // instantiate and connect a custom video sink mVideoSink = GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo")); if (!mVideoSink) { WARNMSG("Could not instantiate private-slvideo element."); // todo: cleanup. return; // error } g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); #ifdef LL_GST_SOUNDSINK // instantiate and connect a custom audio sink mAudioSink = GST_SLSOUND(llgst_element_factory_make ("private-slsound", "slsound")); if (!mAudioSink) { WARNMSG("Could not instantiate private-slsound element."); // todo: cleanup. return; // error } g_object_set(mPlaybin, "audio-sink", mAudioSink, NULL); #endif } }
bool MediaPluginGStreamer010::load() { if (!mDoneInit) return false; // error setStatus(STATUS_LOADING); DEBUGMSG("setting up media..."); mIsLooping = false; mVolume = (float) 0.1234567; // minor hack to force an initial volume update // Create a pumpable main-loop for this media mPump = g_main_loop_new (NULL, FALSE); if (!mPump) { setStatus(STATUS_ERROR); return false; // error } // instantiate a playbin element to do the hard work mPlaybin = gst_element_factory_make ("playbin", "play"); if (!mPlaybin) { setStatus(STATUS_ERROR); return false; // error } // get playbin's bus GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (mPlaybin)); if (!bus) { setStatus(STATUS_ERROR); return false; // error } mBusWatchID = gst_bus_add_watch (bus, llmediaimplgstreamer_bus_callback, this); gst_object_unref (bus); if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { // instantiate a custom video sink mVideoSink = GST_SLVIDEO(gst_element_factory_make ("private-slvideo", "slvideo")); if (!mVideoSink) { WARNMSG("Could not instantiate private-slvideo element."); // todo: cleanup. setStatus(STATUS_ERROR); return false; // error } // connect the pieces g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); } return true; }
static GstCaps * gst_slvideo_get_caps (GstBaseSink * bsink) { GstSLVideo *slvideo; slvideo = GST_SLVIDEO(bsink); return gst_caps_ref (slvideo->caps); }
static gboolean gst_slvideo_start (GstBaseSink * bsink) { gboolean ret = TRUE; GST_SLVIDEO(bsink); return ret; }
static gboolean gst_slvideo_start (GstBaseSink * bsink) { GstSLVideo *slvideo; gboolean ret = TRUE; slvideo = GST_SLVIDEO(bsink); return ret; }
static void gst_slvideo_finalize (GObject * object) { GstSLVideo *slvideo; slvideo = GST_SLVIDEO (object); if (slvideo->caps) { gst_caps_unref(slvideo->caps); } G_OBJECT_CLASS(parent_class)->finalize (object); }
LLMediaImplGStreamer:: LLMediaImplGStreamer () : mediaData ( NULL ), mMediaRowbytes ( 1 ), mTextureFormatPrimary ( LL_MEDIA_BGRA ), mTextureFormatType ( LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV ), mPump ( NULL ), mPlaybin ( NULL ), mVideoSink ( NULL ), mLastTitle ( "" ), mState( GST_STATE_NULL ), mPlayThread ( NULL ) { startup( NULL ); // Startup gstreamer if it hasn't been already. LL_DEBUGS("MediaManager") << "constructing media..." << LL_ENDL; mVolume = -1.0; // XXX Hack to make the vould change happend first time setMediaDepth(4); // Create a pumpable main-loop for this media mPump = g_main_loop_new (NULL, FALSE); if (!mPump) { return; // error } // instantiate a playbin element to do the hard work mPlaybin = gst_element_factory_make ("playbin", "play"); if (!mPlaybin) { // todo: cleanup pump return; // error } if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { // instantiate and connect a custom video sink LL_DEBUGS("MediaManager") << "extrenal video sink..." << LL_ENDL; // Plays inworld instead of in external player mVideoSink = GST_SLVIDEO(gst_element_factory_make ("private-slvideo", "slvideo")); if (!mVideoSink) { LL_WARNS("MediaImpl") << "Could not instantiate private-slvideo element." << LL_ENDL; // todo: cleanup. return; // error } g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); } }
static gboolean gst_slvideo_stop (GstBaseSink * bsink) { GstSLVideo *slvideo; slvideo = GST_SLVIDEO(bsink); // free-up retained frame buffer GST_OBJECT_LOCK(slvideo); slvideo->retained_frame_ready = FALSE; delete[] slvideo->retained_frame_data; slvideo->retained_frame_data = NULL; slvideo->retained_frame_allocbytes = 0; GST_OBJECT_UNLOCK(slvideo); return TRUE; }
static GstFlowReturn gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) { GstSLVideo *slvideo; g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); slvideo = GST_SLVIDEO(bsink); #if 0 fprintf(stderr, "\n\ntransferring a frame of %dx%d <- %p (%d)\n\n", slvideo->width, slvideo->height, GST_BUFFER_DATA(buf), slvideo->format); #endif if (GST_BUFFER_DATA(buf)) { // copy frame and frame info into neutral territory GST_OBJECT_LOCK(slvideo); slvideo->retained_frame_ready = TRUE; slvideo->retained_frame_width = slvideo->width; slvideo->retained_frame_height = slvideo->height; slvideo->retained_frame_format = slvideo->format; int rowbytes = SLVPixelFormatBytes[slvideo->retained_frame_format] * slvideo->retained_frame_width; int needbytes = rowbytes * slvideo->retained_frame_width; // resize retained frame hunk only if necessary if (needbytes != slvideo->retained_frame_allocbytes) { delete[] slvideo->retained_frame_data; slvideo->retained_frame_data = new unsigned char[needbytes]; slvideo->retained_frame_allocbytes = needbytes; } // copy the actual frame data to neutral territory - // flipped, for GL reasons for (int ypos=0; ypos<slvideo->height; ++ypos) { memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes], &(((unsigned char*)GST_BUFFER_DATA(buf))[ypos*rowbytes]), rowbytes); } // done with the shared data GST_OBJECT_UNLOCK(slvideo); } return GST_FLOW_OK; }
static GstStateChangeReturn gst_slvideo_change_state(GstElement * element, GstStateChange transition) { GstSLVideo *slvideo; GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; slvideo = GST_SLVIDEO (element); switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); if (ret == GST_STATE_CHANGE_FAILURE) return ret; switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: slvideo->fps_n = 0; slvideo->fps_d = 1; GST_VIDEO_SINK_WIDTH(slvideo) = 0; GST_VIDEO_SINK_HEIGHT(slvideo) = 0; break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return ret; }
/* this function handles the link with other elements */ static gboolean gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps) { GstSLVideo *filter; GstStructure *structure; GstCaps *intersection; GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); filter = GST_SLVIDEO(bsink); intersection = gst_caps_intersect (filter->caps, caps); if (gst_caps_is_empty (intersection)) { // no overlap between our caps and requested caps return FALSE; } gst_caps_unref(intersection); int width = 0; int height = 0; gboolean ret; const GValue *fps; const GValue *par; structure = gst_caps_get_structure (caps, 0); ret = gst_structure_get_int (structure, "width", &width); ret = ret && gst_structure_get_int (structure, "height", &height); fps = gst_structure_get_value (structure, "framerate"); ret = ret && (fps != NULL); par = gst_structure_get_value (structure, "pixel-aspect-ratio"); if (!ret) return FALSE; filter->width = width; filter->height = height; filter->fps_n = gst_value_get_fraction_numerator(fps); filter->fps_d = gst_value_get_fraction_denominator(fps); if (par) { filter->par_n = gst_value_get_fraction_numerator(par); filter->par_d = gst_value_get_fraction_denominator(par); } else { filter->par_n = 1; filter->par_d = 1; } GST_VIDEO_SINK_WIDTH(filter) = width; GST_VIDEO_SINK_HEIGHT(filter) = height; filter->format = SLV_PF_UNKNOWN; if (0 == strcmp(gst_structure_get_name(structure), "video/x-raw-rgb")) { int red_mask; int green_mask; int blue_mask; gst_structure_get_int(structure, "red_mask", &red_mask); gst_structure_get_int(structure, "green_mask", &green_mask); gst_structure_get_int(structure, "blue_mask", &blue_mask); if ((unsigned int)red_mask == 0xFF000000 && (unsigned int)green_mask == 0x00FF0000 && (unsigned int)blue_mask == 0x0000FF00) { filter->format = SLV_PF_RGBX; //fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n"); } else if ((unsigned int)red_mask == 0x0000FF00 && (unsigned int)green_mask == 0x00FF0000 && (unsigned int)blue_mask == 0xFF000000) { filter->format = SLV_PF_BGRX; //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n"); } } return TRUE; }
BOOL LLMediaImplGStreamer:: init () { static bool done_init = false; if (!done_init) { // Init the glib type system - we need it. g_type_init(); // Get symbols! if (! grab_gst_syms("libgstreamer-0.10.so.0", "libgstvideo-0.10.so.0", "libgstaudio-0.10.so.0") ) { llwarns << "Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled." << llendl; return FALSE; } if (llgst_segtrap_set_enabled) llgst_segtrap_set_enabled(FALSE); else llwarns << "gst_segtrap_set_enabled() is not available; Second Life automated crash-reporter may cease to function until next restart." << llendl; if (0 == llgst_init_check(NULL, NULL, NULL)) { llwarns << "GST init failed for unspecified reason." << llendl; return FALSE; } // Init our custom plugins - only really need do this once. gst_slvideo_init_class(); #if 0 gst_slsound_init_class(); #endif done_init = true; } mVolume = 0.1234567; // minor hack to force an initial volume update // Create a pumpable main-loop for this media mPump = g_main_loop_new (NULL, FALSE); if (!mPump) { return FALSE; } // instantiate a playbin element to do the hard work mPlaybin = llgst_element_factory_make ("playbin", "play"); if (!mPlaybin) { // todo: cleanup pump return FALSE; } if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { // instantiate and connect a custom video sink mVideoSink = GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo")); if (!mVideoSink) { llwarns << "Could not instantiate private-slvideo element." << llendl; // todo: cleanup. return FALSE; } g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); #ifdef LL_GST_SOUNDSINK // instantiate and connect a custom audio sink mAudioSink = GST_SLSOUND(llgst_element_factory_make ("private-slsound", "slsound")); if (!mAudioSink) { llwarns << "Could not instantiate private-slsound element." << llendl; // todo: cleanup. return FALSE; } g_object_set(mPlaybin, "audio-sink", mAudioSink, NULL); #endif } return LLMediaMovieBase::init(); }
bool MediaPluginGStreamer010::load() { if (!mDoneInit) return false; // error setStatus(STATUS_LOADING); DEBUGMSG("setting up media..."); mIsLooping = false; mVolume = 0.1234567; // minor hack to force an initial volume update // Create a pumpable main-loop for this media mPump = g_main_loop_new (NULL, FALSE); if (!mPump) { setStatus(STATUS_ERROR); return false; // error } // instantiate a playbin element to do the hard work mPlaybin = llgst_element_factory_make ("playbin", "play"); if (!mPlaybin) { setStatus(STATUS_ERROR); return false; // error } // get playbin's bus GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin)); if (!bus) { setStatus(STATUS_ERROR); return false; // error } mBusWatchID = llgst_bus_add_watch (bus, llmediaimplgstreamer_bus_callback, this); llgst_object_unref (bus); #if 0 // not quite stable/correct yet // get a visualizer element (bonus feature!) char* vis_name = getenv("LL_GST_VIS_NAME"); if (!vis_name || (vis_name && std::string(vis_name)!="none")) { if (vis_name) { mVisualizer = llgst_element_factory_make (vis_name, "vis"); } if (!mVisualizer) { mVisualizer = llgst_element_factory_make ("libvisual_jess", "vis"); if (!mVisualizer) { mVisualizer = llgst_element_factory_make ("goom", "vis"); if (!mVisualizer) { mVisualizer = llgst_element_factory_make ("libvisual_lv_scope", "vis"); if (!mVisualizer) { // That's okay, we don't NEED this. } } } } } #endif if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { // instantiate a custom video sink mVideoSink = GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo")); if (!mVideoSink) { WARNMSG("Could not instantiate private-slvideo element."); // todo: cleanup. setStatus(STATUS_ERROR); return false; // error } // connect the pieces g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); } if (mVisualizer) { g_object_set(mPlaybin, "vis-plugin", mVisualizer, NULL); } return true; }
static GstFlowReturn gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf) { gint width, height; GstStructure *structure = NULL; GstSLVideo *slvideo; slvideo = GST_SLVIDEO(bsink); // caps == requested caps // we can ignore these and reverse-negotiate our preferred dimensions with // the peer if we like - we need to do this to obey dynamic resize requests // flowing in from the app. structure = gst_caps_get_structure (caps, 0); if (!gst_structure_get_int(structure, "width", &width) || !gst_structure_get_int(structure, "height", &height)) { GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps); return GST_FLOW_NOT_NEGOTIATED; } GstBuffer *newbuf = gst_buffer_new(); bool made_bufferdata_ptr = false; #define MAXDEPTHHACK 4 GST_OBJECT_LOCK(slvideo); if (slvideo->resize_forced_always) // app is giving us a fixed size to work with { gint slwantwidth, slwantheight; slwantwidth = slvideo->resize_try_width; slwantheight = slvideo->resize_try_height; if (slwantwidth != width || slwantheight != height) { // don't like requested caps, we will issue our own suggestion - copy // the requested caps but substitute our own width and height and see // if our peer is happy with that. GstCaps *desired_caps; GstStructure *desired_struct; desired_caps = gst_caps_copy (caps); desired_struct = gst_caps_get_structure (desired_caps, 0); GValue value = {0}; g_value_init(&value, G_TYPE_INT); g_value_set_int(&value, slwantwidth); gst_structure_set_value (desired_struct, "width", &value); g_value_unset(&value); g_value_init(&value, G_TYPE_INT); g_value_set_int(&value, slwantheight); gst_structure_set_value (desired_struct, "height", &value); if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo), desired_caps)) { // todo: re-use buffers from a pool? // todo: set MALLOCDATA to null, set DATA to point straight to shm? // peer likes our cap suggestion DEBUGMSG("peer loves us :)"); GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK; GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps); made_bufferdata_ptr = true; } else { // peer hates our cap suggestion INFOMSG("peer hates us :("); gst_caps_unref(desired_caps); } } } GST_OBJECT_UNLOCK(slvideo); if (!made_bufferdata_ptr) // need to fallback to malloc at original size { GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK; GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps); } *buf = GST_BUFFER_CAST(newbuf); return GST_FLOW_OK; }
/* this function handles the link with other elements */ static gboolean gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps) { GstSLVideo *filter; GstStructure *structure; GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); filter = GST_SLVIDEO(bsink); int width, height; gboolean ret; const GValue *fps; const GValue *par; structure = llgst_caps_get_structure (caps, 0); ret = llgst_structure_get_int (structure, "width", &width); ret = ret && llgst_structure_get_int (structure, "height", &height); fps = llgst_structure_get_value (structure, "framerate"); ret = ret && (fps != NULL); par = llgst_structure_get_value (structure, "pixel-aspect-ratio"); if (!ret) return FALSE; INFOMSG("** filter caps set with width=%d, height=%d", width, height); GST_OBJECT_LOCK(filter); filter->width = width; filter->height = height; filter->fps_n = llgst_value_get_fraction_numerator(fps); filter->fps_d = llgst_value_get_fraction_denominator(fps); if (par) { filter->par_n = llgst_value_get_fraction_numerator(par); filter->par_d = llgst_value_get_fraction_denominator(par); } else { filter->par_n = 1; filter->par_d = 1; } GST_VIDEO_SINK_WIDTH(filter) = width; GST_VIDEO_SINK_HEIGHT(filter) = height; // crufty lump - we *always* accept *only* RGBX now. /* filter->format = SLV_PF_UNKNOWN; if (0 == strcmp(llgst_structure_get_name(structure), "video/x-raw-rgb")) { int red_mask; int green_mask; int blue_mask; llgst_structure_get_int(structure, "red_mask", &red_mask); llgst_structure_get_int(structure, "green_mask", &green_mask); llgst_structure_get_int(structure, "blue_mask", &blue_mask); if ((unsigned int)red_mask == 0xFF000000 && (unsigned int)green_mask == 0x00FF0000 && (unsigned int)blue_mask == 0x0000FF00) { filter->format = SLV_PF_RGBX; //fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n"); } else if ((unsigned int)red_mask == 0x0000FF00 && (unsigned int)green_mask == 0x00FF0000 && (unsigned int)blue_mask == 0xFF000000) { filter->format = SLV_PF_BGRX; //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n"); } }*/ filter->format = SLV_PF_RGBX; GST_OBJECT_UNLOCK(filter); return TRUE; }