示例#1
0
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;
}
示例#12
0
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;
}