Show gstreamer errors in wxGStreamerMediaBackend to the user.

If the media file fails to play, we really need to show to the user the
corresponding gstreamer error, otherwise there is no way to understand what
the problem is.

Collect the errors in gst_error_callback() and show them from the main thread
if we get a gstreamer error.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@71928 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2012-07-02 00:16:18 +00:00
parent 7ae14eb15f
commit 82cf5d5953
2 changed files with 78 additions and 19 deletions

View File

@ -576,6 +576,7 @@ GTK:
- Fix const methods display in assert dialog (vinayakgarg).
- Implement native tab art for wxAUI (Jens Lody and Teodor Petrov).
- Fix pasting large amounts of text (Bradley Hawkins).
- Show gstreamer errors if wxMediaCtrl fails to play file.
MSW:

View File

@ -36,6 +36,7 @@
#include "wx/filesys.h" // FileNameToURL()
#include "wx/thread.h" // wxMutex/wxMutexLocker
#include "wx/vector.h" // wxVector<wxString>
#ifdef __WXGTK__
#include <gtk/gtk.h>
@ -186,6 +187,7 @@ public:
virtual double GetVolume();
//------------implementation from now on-----------------------------------
bool CheckForErrors();
bool DoLoad(const wxString& locstring);
wxMediaCtrl* GetControl() { return m_ctrl; } // for C Callbacks
void HandleStateChange(GstElementState oldstate, GstElementState newstate);
@ -206,6 +208,23 @@ public:
wxMutex m_asynclock; // See "discussion of internals"
class wxGStreamerMediaEventHandler* m_eventHandler; // see below
// Mutex protecting just the variables below which are set from
// gst_error_callback() called from a different thread.
wxMutex m_mutexErr;
struct Error
{
Error(const gchar* message, const gchar* debug)
: m_message(message, wxConvUTF8),
m_debug(debug, wxConvUTF8)
{
}
wxString m_message,
m_debug;
};
wxVector<Error> m_errors;
friend class wxGStreamerMediaEventHandler;
friend class wxGStreamerLoadWaitTimer;
DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend)
@ -382,15 +401,10 @@ static void gst_error_callback(GstElement *WXUNUSED(play),
GstElement *WXUNUSED(src),
GError *err,
gchar *debug,
wxGStreamerMediaBackend* WXUNUSED(be))
wxGStreamerMediaBackend* be)
{
wxString sError;
sError.Printf(wxT("gst_error_callback\n")
wxT("Error Message:%s\nDebug:%s\n"),
(const wxChar*)wxConvUTF8.cMB2WX(err->message),
(const wxChar*)wxConvUTF8.cMB2WX(debug));
wxLogTrace(wxTRACE_GStreamer, sError);
wxLogSysError(sError);
wxMutexLocker lock(be->m_mutexErr);
be->m_errors.push_back(wxGStreamerMediaBackend::Error(err->message, debug));
}
}
@ -481,6 +495,15 @@ static gboolean gst_bus_async_callback(GstBus* WXUNUSED(bus),
GstMessage* message,
wxGStreamerMediaBackend* be)
{
if ( GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR )
{
GError* error;
gchar* debug;
gst_message_parse_error(message, &error, &debug);
gst_error_callback(NULL, NULL, error, debug, be);
return FALSE;
}
if(((GstElement*)GST_MESSAGE_SRC(message)) != be->m_playbin)
return TRUE;
if(be->m_asynclock.TryLock() != wxMUTEX_NO_ERROR)
@ -501,14 +524,7 @@ static gboolean gst_bus_async_callback(GstBus* WXUNUSED(bus),
gst_finish_callback(NULL, be);
break;
}
case GST_MESSAGE_ERROR:
{
GError* error;
gchar* debug;
gst_message_parse_error(message, &error, &debug);
gst_error_callback(NULL, NULL, error, debug, be);
break;
}
default:
break;
}
@ -970,6 +986,32 @@ wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
}
}
//-----------------------------------------------------------------------------
// wxGStreamerMediaBackend::CheckForErrors
//
// Reports any errors received from gstreamer. Should be called after any
// failure.
//-----------------------------------------------------------------------------
bool wxGStreamerMediaBackend::CheckForErrors()
{
wxMutexLocker lock(m_mutexErr);
if ( m_errors.empty() )
return false;
for ( unsigned n = 0; n < m_errors.size(); n++ )
{
const Error& err = m_errors[n];
wxLogTrace(wxTRACE_GStreamer,
"gst_error_callback: %s", err.m_debug);
wxLogError(_("Media playback error: %s"), err.m_message);
}
m_errors.clear();
return true;
}
//-----------------------------------------------------------------------------
// wxGStreamerMediaBackend::CreateControl
//
@ -1222,9 +1264,10 @@ bool wxGStreamerMediaBackend::DoLoad(const wxString& locstring)
GST_STATE_READY) == GST_STATE_FAILURE ||
!SyncStateChange(m_playbin, GST_STATE_READY))
{
wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ")
wxT("Could not set initial state to ready"));
return false;
CheckForErrors();
wxLogError(_("Failed to prepare playing \"%s\"."), locstring);
return false;
}
// free current media resources
@ -1244,11 +1287,18 @@ bool wxGStreamerMediaBackend::DoLoad(const wxString& locstring)
GST_STATE_PAUSED) == GST_STATE_FAILURE ||
!SyncStateChange(m_playbin, GST_STATE_PAUSED))
{
CheckForErrors();
return false; // no real error message needed here as this is
// generic failure 99% of the time (i.e. no
// source etc.) and has an error message
}
// It may happen that both calls above succeed but we actually had some
// errors during the pipeline setup and it doesn't play. E.g. this happens
// if XVideo extension is unavailable but xvimagesink is still used.
if ( CheckForErrors() )
return false;
NotifyMovieLoaded(); // Notify the user - all we can do for now
return true;
@ -1266,7 +1316,11 @@ bool wxGStreamerMediaBackend::Play()
{
if (gst_element_set_state (m_playbin,
GST_STATE_PLAYING) == GST_STATE_FAILURE)
{
CheckForErrors();
return false;
}
return true;
}
@ -1282,7 +1336,10 @@ bool wxGStreamerMediaBackend::Pause()
m_llPausedPos = wxGStreamerMediaBackend::GetPosition();
if (gst_element_set_state (m_playbin,
GST_STATE_PAUSED) == GST_STATE_FAILURE)
{
CheckForErrors();
return false;
}
return true;
}
@ -1302,6 +1359,7 @@ bool wxGStreamerMediaBackend::Stop()
GST_STATE_PAUSED) == GST_STATE_FAILURE ||
!SyncStateChange(m_playbin, GST_STATE_PAUSED))
{
CheckForErrors();
wxLogSysError(wxT("Could not set state to paused for Stop()"));
return false;
}