]>
Commit | Line | Data |
---|---|---|
ddc90a8d | 1 | ///////////////////////////////////////////////////////////////////////////// |
0c5c0375 RN |
2 | // Name: unix/mediactrl.cpp |
3 | // Purpose: Built-in Media Backends for Unix | |
ddc90a8d RN |
4 | // Author: Ryan Norton <wxprojects@comcast.net> |
5 | // Modified by: | |
6 | // Created: 02/04/05 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) 2004-2005 Ryan Norton | |
9 | // Licence: wxWindows licence | |
10 | ///////////////////////////////////////////////////////////////////////////// | |
11 | ||
12 | //=========================================================================== | |
13 | // DECLARATIONS | |
14 | //=========================================================================== | |
15 | ||
16 | //--------------------------------------------------------------------------- | |
17 | // Pre-compiled header stuff | |
18 | //--------------------------------------------------------------------------- | |
19 | ||
20 | #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) | |
21 | #pragma implementation "mediactrl.h" | |
22 | #endif | |
23 | ||
24 | // For compilers that support precompilation, includes "wx.h". | |
25 | #include "wx/wxprec.h" | |
26 | ||
27 | #ifdef __BORLANDC__ | |
28 | #pragma hdrstop | |
29 | #endif | |
30 | ||
31 | //--------------------------------------------------------------------------- | |
32 | // Includes | |
33 | //--------------------------------------------------------------------------- | |
34 | #include "wx/mediactrl.h" | |
35 | ||
36 | //--------------------------------------------------------------------------- | |
37 | // Compilation guard | |
38 | //--------------------------------------------------------------------------- | |
39 | #if wxUSE_MEDIACTRL | |
40 | ||
0c5c0375 RN |
41 | //=========================================================================== |
42 | // BACKEND DECLARATIONS | |
43 | //=========================================================================== | |
44 | ||
45 | //--------------------------------------------------------------------------- | |
46 | // | |
47 | // wxGStreamerMediaBackend | |
48 | // | |
49 | // This won't compile/work without a little work yet... | |
50 | // Uses nanoseconds... | |
51 | //--------------------------------------------------------------------------- | |
52 | #if wxUSE_GSTREAMER | |
53 | ||
54 | //--------------------------------------------------------------------------- | |
55 | // GStreamer Includes | |
56 | //--------------------------------------------------------------------------- | |
57 | #include <gst/gst.h> | |
58 | #include <gst/xoverlay/xoverlay.h> | |
59 | ||
60 | #include <string.h> //strstr | |
61 | ||
62 | #include "wx/log.h" | |
63 | ||
64 | #ifdef __WXGTK__ | |
65 | //for <gdk/gdkx.h>/related for GDK_WINDOW_XWINDOW | |
66 | # include "wx/gtk/win_gtk.h" | |
67 | #endif | |
68 | ||
69 | class WXDLLIMPEXP_MEDIA wxGStreamerMediaBackend : public wxMediaBackend | |
70 | { | |
71 | public: | |
72 | ||
73 | wxGStreamerMediaBackend(); | |
74 | ~wxGStreamerMediaBackend(); | |
75 | ||
76 | virtual bool CreateControl(wxControl* ctrl, wxWindow* parent, | |
77 | wxWindowID id, | |
78 | const wxPoint& pos, | |
79 | const wxSize& size, | |
80 | long style, | |
81 | const wxValidator& validator, | |
82 | const wxString& name); | |
83 | ||
84 | virtual bool Play(); | |
85 | virtual bool Pause(); | |
86 | virtual bool Stop(); | |
87 | ||
88 | virtual bool Load(const wxString& fileName); | |
89 | virtual bool Load(const wxURI& location); | |
90 | ||
91 | virtual wxMediaState GetState(); | |
92 | ||
93 | virtual bool SetPosition(wxLongLong where); | |
94 | virtual wxLongLong GetPosition(); | |
95 | virtual wxLongLong GetDuration(); | |
96 | ||
97 | virtual void Move(int x, int y, int w, int h); | |
98 | wxSize GetVideoSize() const; | |
99 | ||
100 | virtual double GetPlaybackRate(); | |
101 | virtual bool SetPlaybackRate(double dRate); | |
102 | ||
103 | void Cleanup(); | |
104 | ||
105 | static void OnFinish(GstElement *play, gpointer data); | |
106 | static void OnError (GstElement *play, GstElement *src, | |
107 | GError *err, gchar *debug, | |
108 | gpointer data); | |
109 | ||
110 | GstElement* m_player; //GStreamer media element | |
111 | GstElement* m_audiosink; | |
112 | GstElement* m_videosink; | |
113 | ||
114 | DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend); | |
115 | }; | |
116 | ||
117 | ||
118 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
119 | // | |
120 | // wxGStreamerMediaBackend | |
121 | // | |
122 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
123 | ||
124 | IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend, wxMediaBackend); | |
125 | ||
126 | wxGStreamerMediaBackend::wxGStreamerMediaBackend() | |
127 | { | |
128 | } | |
129 | ||
130 | wxGStreamerMediaBackend::~wxGStreamerMediaBackend() | |
131 | { | |
132 | gst_element_set_state (m_player, GST_STATE_NULL); | |
133 | gst_object_unref (GST_OBJECT (m_player)); | |
134 | gst_object_unref (GST_OBJECT (m_videosink)); | |
135 | gst_object_unref (GST_OBJECT (m_audiosink)); | |
136 | } | |
137 | ||
138 | bool wxGStreamerMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent, | |
139 | wxWindowID id, | |
140 | const wxPoint& pos, | |
141 | const wxSize& size, | |
142 | long style, | |
143 | const wxValidator& validator, | |
144 | const wxString& name) | |
145 | { | |
146 | //init gstreamer | |
147 | gst_init(NULL, NULL); | |
148 | ||
149 | // | |
150 | // Create window | |
151 | // By default wxWindow(s) is created with a border - | |
152 | // so we need to get rid of those return Load( | |
153 | // | |
154 | // Since we don't have a child window like most other | |
155 | // backends, we don't need wxCLIP_CHILDREN | |
156 | // | |
157 | if ( ! | |
158 | ctrl->wxControl::Create(parent, id, pos, size, | |
159 | style, //remove borders??? | |
160 | validator, name) | |
161 | ) | |
162 | return false; | |
163 | ||
164 | m_player = gst_element_factory_make ("playbin", "play"); | |
165 | m_audiosink = gst_element_factory_make ("alsasink", "audiosink"); | |
166 | m_videosink = gst_element_factory_make ("xvimagesink", "videosink"); | |
167 | ||
168 | g_object_set (G_OBJECT (m_player), | |
169 | "video-sink", m_videosink, | |
170 | "audio-sink", m_audiosink, | |
171 | NULL); | |
172 | ||
173 | g_signal_connect (m_player, "eos", G_CALLBACK (OnError), this); | |
174 | g_signal_connect (m_player, "error", G_CALLBACK (OnFinish), this); | |
175 | ||
176 | if ( ! GST_IS_X_OVERLAY(m_videosink) ) | |
177 | return false; | |
178 | ||
179 | gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_videosink), | |
180 | #ifdef __WXGTK__ | |
181 | GDK_WINDOW_XWINDOW(ctrl->GetHandle()) | |
182 | #else | |
183 | ctrl->GetHandle() | |
184 | #endif | |
185 | ); | |
186 | return true; | |
187 | } | |
188 | ||
189 | void wxGStreamerMediaBackend::OnFinish(GstElement *play, gpointer data) | |
190 | { | |
191 | wxGStreamerMediaBackend* m_parent = (wxGStreamerMediaBackend*) data; | |
192 | ||
193 | wxMediaEvent theEvent(wxEVT_MEDIA_STOP, | |
194 | m_parent->m_ctrl->GetId()); | |
195 | m_parent->m_ctrl->ProcessEvent(theEvent); | |
196 | ||
197 | if(theEvent.IsAllowed()) | |
198 | { | |
199 | bool bOk = m_parent->Stop(); | |
200 | wxASSERT(bOk); | |
201 | ||
202 | //send the event to our child | |
203 | wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED, | |
204 | m_parent->m_ctrl->GetId()); | |
205 | m_parent->m_ctrl->ProcessEvent(theEvent); | |
206 | } | |
207 | } | |
208 | ||
209 | void wxGStreamerMediaBackend::OnError(GstElement *play, | |
210 | GstElement *src, | |
211 | GError *err, | |
212 | gchar *debug, | |
213 | gpointer data) | |
214 | { | |
215 | wxLogSysError(wxString::Format(wxT("Error in GStreamer Playback!\nError Message:%s"), wxString(err->message).c_str())); | |
216 | } | |
217 | ||
218 | ||
219 | bool wxGStreamerMediaBackend::Load(const wxString& fileName) | |
220 | { | |
221 | return Load( | |
222 | wxURI( | |
223 | wxString( wxT("file://") ) + fileName | |
224 | ) | |
225 | ); | |
226 | } | |
227 | ||
228 | bool wxGStreamerMediaBackend::Load(const wxURI& location) | |
229 | { | |
230 | Cleanup(); | |
231 | wxString locstring = location.BuildURI(); | |
232 | ||
233 | ||
234 | if ( GST_STATE(m_player) > GST_STATE_READY ) | |
235 | gst_element_set_state(m_player, GST_STATE_READY); | |
236 | ||
237 | g_object_set (G_OBJECT (m_player), "uri", locstring.c_str(), NULL); | |
238 | ||
239 | gst_x_overlay_expose(GST_X_OVERLAY(m_videosink)); | |
240 | ||
241 | return GST_STATE(m_player) == GST_STATE_READY; | |
242 | } | |
243 | ||
244 | bool wxGStreamerMediaBackend::Play() | |
245 | { | |
246 | if (gst_element_set_state (m_player, GST_STATE_PLAYING) | |
247 | != GST_STATE_SUCCESS) | |
248 | return false; | |
249 | return true; | |
250 | } | |
251 | ||
252 | bool wxGStreamerMediaBackend::Pause() | |
253 | { | |
254 | if (gst_element_set_state (m_player, GST_STATE_PAUSED) | |
255 | != GST_STATE_SUCCESS) | |
256 | return false; | |
257 | return true; | |
258 | } | |
259 | ||
260 | bool wxGStreamerMediaBackend::Stop() | |
261 | { | |
262 | if (gst_element_set_state (m_player, | |
263 | GST_STATE_READY) != GST_STATE_SUCCESS) | |
264 | return false; | |
265 | return true; | |
266 | } | |
267 | ||
268 | wxMediaState wxGStreamerMediaBackend::GetState() | |
269 | { | |
270 | switch(GST_STATE(m_player)) | |
271 | { | |
272 | case GST_STATE_PLAYING: | |
273 | return wxMEDIASTATE_PLAYING; | |
274 | case GST_STATE_PAUSED: | |
275 | return wxMEDIASTATE_PAUSED; | |
276 | default://case GST_STATE_READY: | |
277 | return wxMEDIASTATE_STOPPED; | |
278 | } | |
279 | } | |
280 | ||
281 | bool wxGStreamerMediaBackend::SetPosition(wxLongLong where) | |
282 | { | |
283 | return gst_element_seek (play, (GstSeekType) (GST_SEEK_METHOD_SET | | |
284 | GST_FORMAT_TIME | GST_SEEK_FLAG_FLUSH), | |
285 | where * GST_MSECOND ); | |
286 | } | |
287 | ||
288 | wxLongLong wxGStreamerMediaBackend::GetPosition() | |
289 | { | |
290 | gint64 pos; | |
291 | GstFormat fmtTime = GST_FORMAT_TIME; | |
292 | ||
293 | if (!gst_element_query (play, GST_QUERY_POSITION, &fmtTime, &pos)) | |
294 | return 0; | |
295 | return pos / GST_MSECOND ; | |
296 | } | |
297 | ||
298 | wxLongLong wxGStreamerMediaBackend::GetDuration() | |
299 | { | |
300 | gint64 length; | |
301 | GstFormat fmtTime = GST_FORMAT_TIME; | |
302 | ||
303 | if(!gst_element_query(m_player, GST_QUERY_TOTAL, &fmtTime, &length)) | |
304 | return 0; | |
305 | return length / GST_MSECOND ; | |
306 | } | |
307 | ||
308 | void wxGStreamerMediaBackend::Move(int x, int y, int w, int h) | |
309 | { | |
310 | } | |
311 | ||
312 | wxSize wxGStreamerMediaBackend::GetVideoSize() const | |
313 | { | |
314 | //TODO: check state | |
315 | //TODO: maybe cache size | |
316 | wxSize retSize = wxSize(0,0); | |
317 | ||
318 | const GList *list = NULL; | |
319 | g_object_get (G_OBJECT (m_player), "stream-info", &list, NULL); | |
320 | ||
321 | for ( ; list != NULL; list = list->next) | |
322 | { | |
323 | GObject *info = (GObject *) list->data; | |
324 | gint type; | |
325 | GParamSpec *pspec; | |
326 | GEnumValue *val; | |
327 | GstPad *pad = NULL; | |
328 | ||
329 | g_object_get (info, "type", &type, NULL); | |
330 | pspec = g_object_class_find_property ( | |
331 | G_OBJECT_GET_CLASS (info), "type"); | |
332 | val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type); | |
333 | ||
334 | if (strstr (val->value_name, "VIDEO")) | |
335 | { | |
336 | g_object_get (info, "object", &pad, NULL); | |
337 | pad = (GstPad *) GST_PAD_REALIZE (pad); | |
338 | wxAssert(pad); | |
339 | ||
340 | GstCaps* caps = GST_PAD_CAPS (pad); | |
341 | wxAssert(caps); | |
342 | ||
343 | const GstStructure *s; | |
344 | s = gst_caps_get_structure (caps, 0); | |
345 | wxAssert(s); | |
346 | ||
347 | gst_structure_get_int (s, "width", &retSize.x); | |
348 | gst_structure_get_int (s, "height", &retSize.y); | |
349 | ||
350 | const GValue *par; | |
351 | par = gst_structure_get_value (s, "pixel-aspect-ratio")); | |
352 | ||
353 | if (par) | |
354 | { | |
355 | int num = gst_value_get_fraction_numerator (par), | |
356 | den = gst_value_get_fraction_denominator (par); | |
357 | ||
358 | //TODO: maybe better fraction normalization... | |
359 | if (num > den) | |
360 | retSize.x = (int) ((float) num * retSize.x / den); | |
361 | else | |
362 | retSize.y = (int) ((float) den * retSize.y / num); | |
363 | } | |
364 | } | |
365 | } | |
366 | } | |
367 | ||
368 | double wxGStreamerMediaBackend::GetPlaybackRate() | |
369 | { | |
370 | //guess... | |
371 | GstClock* theClock = gst_element_get_clock(m_player); | |
372 | wxAssert(theClock); | |
373 | return gst_clock_get_speed(theClock); | |
374 | } | |
375 | ||
376 | bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate) | |
377 | { | |
378 | //guess... | |
379 | GstClock* theClock = gst_element_get_clock(m_player); | |
380 | wxAssert(theClock); | |
381 | return gst_clock_change_speed(theClock, GetPlaybackRate(), dRate)==dRate; | |
382 | } | |
383 | ||
384 | #endif //wxUSE_GSTREAMER | |
385 | ||
ddc90a8d RN |
386 | //in source file that contains stuff you don't directly use |
387 | #include <wx/html/forcelnk.h> | |
388 | FORCE_LINK_ME(basewxmediabackends); | |
389 | ||
390 | #endif //wxUSE_MEDIACTRL | |
391 | ||
392 | ||
393 | ||
394 | ||
395 |