]> git.sesse.net Git - vlc/blob - projects/activex/vlccontrol.cpp
a1e3c835994c3c5da41835278f70b11d7ad61afa
[vlc] / projects / activex / vlccontrol.cpp
1 /*****************************************************************************
2  * vlccontrol.cpp: ActiveX control for VLC
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  *
6  * Authors: Damien Fouilleul <Damien.Fouilleul@laposte.net>
7  *          Jean-Paul Saman <jpsaman@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include "plugin.h"
25 #include "vlccontrol.h"
26
27 #include "utils.h"
28
29 using namespace std;
30
31 VLCControl::~VLCControl()
32 {
33     if( _p_typeinfo )
34         _p_typeinfo->Release();
35 };
36
37 HRESULT VLCControl::getTypeInfo(void)
38 {
39     HRESULT hr = NOERROR;
40     if( NULL == _p_typeinfo )
41     {
42         ITypeLib *p_typelib;
43
44         hr = _p_instance->getTypeLib(LOCALE_USER_DEFAULT, &p_typelib);
45         if( SUCCEEDED(hr) )
46         {
47             hr = p_typelib->GetTypeInfoOfGuid(IID_IVLCControl, &_p_typeinfo);
48             if( FAILED(hr) )
49             {
50                 _p_typeinfo = NULL;
51             }
52             p_typelib->Release();
53         }
54     }
55     return hr;
56 };
57
58 STDMETHODIMP VLCControl::GetTypeInfoCount(UINT* pctInfo)
59 {
60     if( NULL == pctInfo )
61         return E_INVALIDARG;
62
63     if( SUCCEEDED(getTypeInfo()) )
64         *pctInfo = 1;
65     else
66         *pctInfo = 0;
67
68     return NOERROR;
69 };
70
71 STDMETHODIMP VLCControl::GetTypeInfo(UINT iTInfo, LCID lcid, LPTYPEINFO* ppTInfo)
72 {
73     if( NULL == ppTInfo )
74         return E_INVALIDARG;
75
76     if( SUCCEEDED(getTypeInfo()) )
77     {
78         _p_typeinfo->AddRef();
79         *ppTInfo = _p_typeinfo;
80         return NOERROR;
81     }
82     *ppTInfo = NULL;
83     return E_NOTIMPL;
84 };
85
86 STDMETHODIMP VLCControl::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames,
87         UINT cNames, LCID lcid, DISPID* rgDispID)
88 {
89     if( SUCCEEDED(getTypeInfo()) )
90     {
91         return DispGetIDsOfNames(_p_typeinfo, rgszNames, cNames, rgDispID);
92     }
93     return E_NOTIMPL;
94 };
95
96 STDMETHODIMP VLCControl::Invoke(DISPID dispIdMember, REFIID riid,
97         LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
98         VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
99 {
100     if( SUCCEEDED(getTypeInfo()) )
101     {
102         return DispInvoke(this, _p_typeinfo, dispIdMember, wFlags, pDispParams,
103                 pVarResult, pExcepInfo, puArgErr);
104     }
105     return E_NOTIMPL;
106 };
107
108 STDMETHODIMP VLCControl::get_Visible(VARIANT_BOOL *isVisible)
109 {
110     if( NULL == isVisible )
111         return E_POINTER;
112
113     *isVisible = _p_instance->getVisible() ? VARIANT_TRUE : VARIANT_FALSE;
114
115     return NOERROR;
116 };
117
118 STDMETHODIMP VLCControl::put_Visible(VARIANT_BOOL isVisible)
119 {
120     _p_instance->setVisible(isVisible != VARIANT_FALSE);
121
122     return NOERROR;
123 };
124
125 STDMETHODIMP VLCControl::play(void)
126 {
127     libvlc_instance_t* p_libvlc;
128     HRESULT result = _p_instance->getVLC(&p_libvlc);
129     if( SUCCEEDED(result) )
130     {
131         libvlc_exception_t ex;
132         libvlc_exception_init(&ex);
133
134         libvlc_playlist_play(p_libvlc, -1, 0, NULL, &ex);
135         if( libvlc_exception_raised(&ex) )
136         {
137             _p_instance->setErrorInfo(IID_IVLCControl,
138                 libvlc_exception_get_message(&ex));
139             libvlc_exception_clear(&ex);
140             return E_FAIL;
141         }
142         _p_instance->fireOnPlayEvent();
143         return NOERROR;
144     }
145     return result;
146 };
147
148 STDMETHODIMP VLCControl::pause(void)
149 {
150     libvlc_instance_t* p_libvlc;
151     HRESULT result = _p_instance->getVLC(&p_libvlc);
152     if( SUCCEEDED(result) )
153     {
154         libvlc_exception_t ex;
155         libvlc_exception_init(&ex);
156
157         libvlc_playlist_pause(p_libvlc, &ex);
158         if( libvlc_exception_raised(&ex) )
159         {
160             _p_instance->setErrorInfo(IID_IVLCControl,
161                 libvlc_exception_get_message(&ex));
162             libvlc_exception_clear(&ex);
163             return E_FAIL;
164         }
165         _p_instance->fireOnPauseEvent();
166         return NOERROR;
167     }
168     return result;
169 };
170
171 STDMETHODIMP VLCControl::stop(void)
172 {
173     libvlc_instance_t* p_libvlc;
174     HRESULT result = _p_instance->getVLC(&p_libvlc);
175     if( SUCCEEDED(result) )
176     {
177         libvlc_exception_t ex;
178         libvlc_exception_init(&ex);
179
180         libvlc_playlist_stop(p_libvlc, &ex);
181         if( libvlc_exception_raised(&ex) )
182         {
183             _p_instance->setErrorInfo(IID_IVLCControl,
184                 libvlc_exception_get_message(&ex));
185             libvlc_exception_clear(&ex);
186             return E_FAIL;
187         }
188         return NOERROR;
189     }
190     _p_instance->fireOnStopEvent();
191     return result;
192 };
193
194 STDMETHODIMP VLCControl::get_Playing(VARIANT_BOOL *isPlaying)
195 {
196     if( NULL == isPlaying )
197         return E_POINTER;
198
199     HRESULT result = NOERROR;
200     if( _p_instance->isRunning() )
201     {
202         libvlc_instance_t *p_libvlc;
203         result = _p_instance->getVLC(&p_libvlc);
204         if( SUCCEEDED(result) )
205         {
206             if( libvlc_playlist_isplaying(p_libvlc, NULL) )
207                 *isPlaying = VARIANT_TRUE;
208             else
209                 *isPlaying = VARIANT_FALSE;
210             return NOERROR;
211         }
212     }
213     *isPlaying = VARIANT_FALSE;
214     return result;
215 };
216
217 STDMETHODIMP VLCControl::get_Position(float *position)
218 {
219     if( NULL == position )
220         return E_POINTER;
221     *position = 0.0f;
222
223     libvlc_instance_t* p_libvlc;
224     HRESULT result = E_UNEXPECTED;
225     result = _p_instance->getVLC(&p_libvlc);
226     if( SUCCEEDED(result) )
227     {
228         libvlc_exception_t ex;
229         libvlc_exception_init(&ex);
230
231         libvlc_media_player_t *p_md;
232         p_md = libvlc_playlist_get_media_player(p_libvlc, &ex);
233         if( !libvlc_exception_raised(&ex) )
234         {
235             *position = libvlc_media_player_get_position(p_md, &ex);
236             libvlc_media_player_release(p_md);
237             if( ! libvlc_exception_raised(&ex) )
238             {
239                 return NOERROR;
240             }
241         }
242         _p_instance->setErrorInfo(IID_IVLCControl,
243                      libvlc_exception_get_message(&ex));
244         libvlc_exception_clear(&ex);
245         return E_FAIL;
246     }
247     return result;
248 };
249
250 STDMETHODIMP VLCControl::put_Position(float position)
251 {
252     HRESULT result = E_UNEXPECTED;
253     libvlc_instance_t* p_libvlc;
254     result = _p_instance->getVLC(&p_libvlc);
255     if( SUCCEEDED(result) )
256     {
257         libvlc_exception_t ex;
258         libvlc_exception_init(&ex);
259
260         libvlc_media_player_t *p_md;
261         p_md = libvlc_playlist_get_media_player(p_libvlc, &ex);
262         if( ! libvlc_exception_raised(&ex) )
263         {
264             libvlc_media_player_set_position(p_md, position, &ex);
265             libvlc_media_player_release(p_md);
266             if( ! libvlc_exception_raised(&ex) )
267             {
268                 return NOERROR;
269             }
270         }
271         _p_instance->setErrorInfo(IID_IVLCControl,
272                      libvlc_exception_get_message(&ex));
273         libvlc_exception_clear(&ex);
274         return E_FAIL;
275     }
276     return result;
277 };
278
279 STDMETHODIMP VLCControl::get_Time(int *seconds)
280 {
281     if( NULL == seconds )
282         return E_POINTER;
283
284     *seconds = 0;
285     libvlc_instance_t* p_libvlc;
286     HRESULT result = _p_instance->getVLC(&p_libvlc);
287     if( SUCCEEDED(result) )
288     {
289         libvlc_exception_t ex;
290         libvlc_exception_init(&ex);
291
292         libvlc_media_player_t *p_md;
293         p_md = libvlc_playlist_get_media_player(p_libvlc, &ex);
294         if( ! libvlc_exception_raised(&ex) )
295         {
296             *seconds = libvlc_media_player_get_time(p_md, &ex);
297             libvlc_media_player_release(p_md);
298             if( ! libvlc_exception_raised(&ex) )
299             {
300                 return NOERROR;
301             }
302         }
303         _p_instance->setErrorInfo(IID_IVLCControl,
304                      libvlc_exception_get_message(&ex));
305         libvlc_exception_clear(&ex);
306         return E_FAIL;
307     }
308     return result;
309 };
310
311 STDMETHODIMP VLCControl::put_Time(int seconds)
312 {
313     /* setTime function of the plugin sets the time. */
314     _p_instance->setTime(seconds);
315     return NOERROR;
316 };
317
318 STDMETHODIMP VLCControl::shuttle(int seconds)
319 {
320     libvlc_instance_t* p_libvlc;
321     HRESULT result = _p_instance->getVLC(&p_libvlc);
322     if( SUCCEEDED(result) )
323     {
324         libvlc_exception_t ex;
325         libvlc_exception_init(&ex);
326
327         libvlc_media_player_t *p_md;
328         p_md = libvlc_playlist_get_media_player(p_libvlc, &ex);
329         if( ! libvlc_exception_raised(&ex) )
330         {
331             if( seconds < 0 ) seconds = 0;
332             libvlc_media_player_set_time(p_md, (int64_t)seconds, &ex);
333             libvlc_media_player_release(p_md);
334             if( ! libvlc_exception_raised(&ex) )
335             {
336                 return NOERROR;
337             }
338         }
339         _p_instance->setErrorInfo(IID_IVLCControl,
340                      libvlc_exception_get_message(&ex));
341         libvlc_exception_clear(&ex);
342         return E_FAIL;
343     }
344     return result;
345
346 };
347
348 STDMETHODIMP VLCControl::fullscreen(void)
349 {
350     HRESULT result = E_UNEXPECTED;
351     if( _p_instance->isRunning() )
352     {
353         libvlc_instance_t *p_libvlc;
354         result = _p_instance->getVLC(&p_libvlc);
355         if( SUCCEEDED(result) )
356         {
357             if( libvlc_playlist_isplaying(p_libvlc, NULL) )
358             {
359                 libvlc_media_player_t *p_md =
360                     libvlc_playlist_get_media_player(p_libvlc, NULL);
361                 if( p_md )
362                 {
363                     libvlc_toggle_fullscreen(p_md, NULL);
364                     libvlc_media_player_release(p_md);
365                 }
366             }
367         }
368     }
369     return result;
370 };
371
372 STDMETHODIMP VLCControl::get_Length(int *seconds)
373 {
374     if( NULL == seconds )
375         return E_POINTER;
376     *seconds = 0;
377
378     libvlc_instance_t* p_libvlc;
379     HRESULT result = _p_instance->getVLC(&p_libvlc);
380     if( SUCCEEDED(result) )
381     {
382         libvlc_exception_t ex;
383         libvlc_exception_init(&ex);
384
385         libvlc_media_player_t *p_md;
386         p_md = libvlc_playlist_get_media_player(p_libvlc, &ex);
387         if( ! libvlc_exception_raised(&ex) )
388         {
389             *seconds = (double)libvlc_media_player_get_length(p_md, &ex);
390             libvlc_media_player_release(p_md);
391             if( ! libvlc_exception_raised(&ex) )
392             {
393                 return NOERROR;
394             }
395         }
396         _p_instance->setErrorInfo(IID_IVLCControl,
397                      libvlc_exception_get_message(&ex));
398         libvlc_exception_clear(&ex);
399         return E_FAIL;
400     }
401     return result;
402
403 };
404
405 STDMETHODIMP VLCControl::playFaster(void)
406 {
407     int32_t rate = 2;
408
409     HRESULT result = E_UNEXPECTED;
410     if( !_p_instance->isRunning() )
411         return result;
412
413     libvlc_instance_t* p_libvlc;
414     result = _p_instance->getVLC(&p_libvlc);
415     if( SUCCEEDED(result) )
416     {
417         libvlc_exception_t ex;
418         libvlc_exception_init(&ex);
419
420         libvlc_media_player_t *p_md;
421         p_md = libvlc_playlist_get_media_player(p_libvlc, &ex);
422         if( ! libvlc_exception_raised(&ex) )
423         {
424             libvlc_media_player_set_rate(p_md, rate, &ex);
425             libvlc_media_player_release(p_md);
426             if( ! libvlc_exception_raised(&ex) )
427             {
428                 return NOERROR;
429             }
430         }
431         _p_instance->setErrorInfo(IID_IVLCControl,
432                      libvlc_exception_get_message(&ex));
433         libvlc_exception_clear(&ex);
434         return E_FAIL;
435     }
436     return result;
437 };
438
439 STDMETHODIMP VLCControl::playSlower(void)
440 {
441     float rate = 0.5;
442
443     HRESULT result = E_UNEXPECTED;
444     if( !_p_instance->isRunning() )
445         return result;
446
447     libvlc_instance_t* p_libvlc;
448     result = _p_instance->getVLC(&p_libvlc);
449     if( SUCCEEDED(result) )
450     {
451         libvlc_exception_t ex;
452         libvlc_exception_init(&ex);
453
454         libvlc_media_player_t *p_md;
455         p_md = libvlc_playlist_get_media_player(p_libvlc, &ex);
456         if( ! libvlc_exception_raised(&ex) )
457         {
458             libvlc_media_player_set_rate(p_md, rate, &ex);
459             libvlc_media_player_release(p_md);
460             if( ! libvlc_exception_raised(&ex) )
461             {
462                 return NOERROR;
463             }
464         }
465         _p_instance->setErrorInfo(IID_IVLCControl,
466                      libvlc_exception_get_message(&ex));
467         libvlc_exception_clear(&ex);
468         return E_FAIL;
469     }
470     return result;
471 };
472
473 STDMETHODIMP VLCControl::get_Volume(int *volume)
474 {
475     if( NULL == volume )
476         return E_POINTER;
477
478     *volume  = _p_instance->getVolume();
479     return NOERROR;
480 };
481
482 STDMETHODIMP VLCControl::put_Volume(int volume)
483 {
484     _p_instance->setVolume(volume);
485     return NOERROR;
486 };
487
488 STDMETHODIMP VLCControl::toggleMute(void)
489 {
490     libvlc_instance_t* p_libvlc;
491     HRESULT result = _p_instance->getVLC(&p_libvlc);
492     if( SUCCEEDED(result) )
493     {
494         libvlc_exception_t ex;
495         libvlc_exception_init(&ex);
496
497         libvlc_audio_toggle_mute(p_libvlc, &ex);
498         if( libvlc_exception_raised(&ex) )
499         {
500             _p_instance->setErrorInfo(IID_IVLCControl,
501                          libvlc_exception_get_message(&ex));
502             libvlc_exception_clear(&ex);
503             return E_FAIL;
504         }
505         return NOERROR;
506     }
507     return result;
508 };
509
510 STDMETHODIMP VLCControl::setVariable(BSTR name, VARIANT value)
511 {
512     return E_INVALIDARG;
513 #if 0
514     if( 0 == SysStringLen(name) )
515         return E_INVALIDARG;
516
517     libvlc_instance_t *p_libvlc;
518     HRESULT hr = _p_instance->getVLC(&p_libvlc);
519     if( SUCCEEDED(hr) )
520     {
521         int codePage = _p_instance->getCodePage();
522         char *psz_varname = CStrFromBSTR(codePage, name);
523         if( NULL == psz_varname )
524             return E_OUTOFMEMORY;
525
526         int i_type;
527         vlc_value_t val;
528
529         if( VLC_SUCCESS == VLC_VariableType(i_vlc, psz_varname, &i_type) )
530         {
531             VARIANT arg;
532             VariantInit(&arg);
533
534             switch( i_type )
535             {
536                 case VLC_VAR_BOOL:
537                     hr = VariantChangeType(&arg, &value, 0, VT_BOOL);
538                     if( SUCCEEDED(hr) )
539                         val.b_bool = (VARIANT_TRUE == V_BOOL(&arg)) ? true : false;
540                     break;
541
542                 case VLC_VAR_INTEGER:
543                 case VLC_VAR_HOTKEY:
544                     hr = VariantChangeType(&arg, &value, 0, VT_I4);
545                     if( SUCCEEDED(hr) )
546                         val.i_int = V_I4(&arg);
547                     break;
548
549                 case VLC_VAR_FLOAT:
550                     hr = VariantChangeType(&arg, &value, 0, VT_R4);
551                     if( SUCCEEDED(hr) )
552                         val.f_float = V_R4(&arg);
553                     break;
554
555                 case VLC_VAR_STRING:
556                 case VLC_VAR_MODULE:
557                 case VLC_VAR_FILE:
558                 case VLC_VAR_DIRECTORY:
559                 case VLC_VAR_VARIABLE:
560                     hr = VariantChangeType(&arg, &value, 0, VT_BSTR);
561                     if( SUCCEEDED(hr) )
562                     {
563                         i_type = VLC_VAR_STRING;
564                         val.psz_string = CStrFromBSTR(codePage, V_BSTR(&arg));
565                         VariantClear(&arg);
566                     }
567                     break;
568
569                 case VLC_VAR_TIME:
570                     // use a double value to represent time (base is expressed in seconds)
571                     hr = VariantChangeType(&arg, &value, 0, VT_R8);
572                     if( SUCCEEDED(hr) )
573                         val.i_time = (signed __int64)(V_R8(&arg)*1000000.0);
574                     break;
575
576                 default:
577                     hr = DISP_E_TYPEMISMATCH;
578             }
579         }
580         else {
581             // no defined type, use type in VARIANT
582             hr = NO_ERROR;
583             switch( V_VT(&value) )
584             {
585                 case VT_BOOL:
586                     val.b_bool = (VARIANT_TRUE == V_BOOL(&value)) ? true : false;
587                     i_type = VLC_VAR_BOOL;
588                     break;
589                 case VT_I4:
590                     val.i_int = V_I4(&value);
591                     i_type = VLC_VAR_INTEGER;
592                     break;
593                 case VT_R4:
594                     val.f_float = V_R4(&value);
595                     i_type = VLC_VAR_FLOAT;
596                     break;
597                 case VT_BSTR:
598                     val.psz_string = CStrFromBSTR(codePage, V_BSTR(&value));
599                     i_type = VLC_VAR_STRING;
600                     break;
601                 case VT_R8:
602                     // use a double value to represent time (base is expressed in seconds)
603                     val.i_time = (signed __int64)(V_R8(&value)*1000000.0);
604                     i_type = VLC_VAR_TIME;
605                     break;
606                 default:
607                     hr = DISP_E_TYPEMISMATCH;
608             }
609         }
610         if( SUCCEEDED(hr) )
611         {
612             hr = (VLC_SUCCESS == VLC_VariableSet(i_vlc, psz_varname, val)) ? NOERROR : E_FAIL;
613
614             if( (VLC_VAR_STRING == i_type) && (NULL != val.psz_string) )
615                 CoTaskMemFree(val.psz_string);
616         }
617         CoTaskMemFree(psz_varname);
618     }
619     return hr;
620 #endif
621 };
622
623 STDMETHODIMP VLCControl::getVariable( BSTR name, VARIANT *value)
624 {
625     return E_INVALIDARG;
626 #if 0
627     if( NULL == value )
628         return E_POINTER;
629
630     VariantInit(value);
631
632     if( 0 == SysStringLen(name) )
633         return E_INVALIDARG;
634
635     libvlc_instance_t *p_libvlc;
636     HRESULT hr = _p_instance->getVLC(&p_libvlc);
637     if( SUCCEEDED(hr) )
638     {
639         UINT codePage = _p_instance->getCodePage();
640         char *psz_varname = CStrFromBSTR(codePage, name);
641         if( NULL == psz_varname )
642             return E_OUTOFMEMORY;
643
644         hr = E_INVALIDARG;
645
646         vlc_value_t val;
647         int i_type;
648
649         if( (VLC_SUCCESS == VLC_VariableGet(i_vlc, psz_varname, &val))
650          && (VLC_SUCCESS == VLC_VariableType(i_vlc, psz_varname, &i_type)) )
651         {
652             hr = NOERROR;
653             switch( i_type )
654             {
655                 case VLC_VAR_BOOL:
656                     V_VT(value) = VT_BOOL;
657                     V_BOOL(value) = val.b_bool ? VARIANT_TRUE : VARIANT_FALSE;
658                     break;
659
660                 case VLC_VAR_INTEGER:
661                 case VLC_VAR_HOTKEY:
662                     V_VT(value) = VT_I4;
663                     V_I4(value) = val.i_int;
664                     break;
665
666                 case VLC_VAR_FLOAT:
667                     V_VT(value) = VT_R4;
668                     V_R4(value) = val.f_float;
669                     break;
670
671                 case VLC_VAR_STRING:
672                 case VLC_VAR_MODULE:
673                 case VLC_VAR_FILE:
674                 case VLC_VAR_DIRECTORY:
675                 case VLC_VAR_VARIABLE:
676                     V_VT(value) = VT_BSTR;
677                     V_BSTR(value) = BSTRFromCStr(codePage, val.psz_string);
678                     if( NULL != val.psz_string)
679                         free(val.psz_string);
680                     break;
681
682                 case VLC_VAR_TIME:
683                     // use a double value to represent time (base is expressed in seconds)
684                     V_VT(value) = VT_R8;
685                     V_R8(value) = ((double)val.i_time)/1000000.0;
686                     break;
687
688                 default:
689                     hr = DISP_E_TYPEMISMATCH;
690             }
691         }
692         CoTaskMemFree(psz_varname);
693         return hr;
694     }
695     return hr;
696 #endif
697 };
698
699 void VLCControl::FreeTargetOptions(char **cOptions, int cOptionCount)
700 {
701     // clean up
702     if( NULL != cOptions )
703     {
704         for( int pos=0; pos<cOptionCount; ++pos )
705         {
706             char *cOption = cOptions[pos];
707             if( NULL != cOption )
708                 CoTaskMemFree(cOption);
709             else
710                 break;
711         }
712         CoTaskMemFree(cOptions);
713     }
714 };
715
716 static HRESULT parseStringOptions(int codePage, BSTR bstr, char*** cOptions, int *cOptionCount)
717 {
718     HRESULT hr = E_INVALIDARG;
719     if( SysStringLen(bstr) > 0 )
720     {
721         hr = E_OUTOFMEMORY;
722         char *s = CStrFromBSTR(codePage, bstr);
723         char *val = s;
724         if( val )
725         {
726             long capacity = 16;
727             char **options = (char **)CoTaskMemAlloc(capacity*sizeof(char *));
728             if( options )
729             {
730                 int nOptions = 0;
731
732                 char *end = val + strlen(val);
733                 while( val < end )
734                 {
735                     // skip leading blanks
736                     while( (val < end)
737                         && ((*val == ' ' ) || (*val == '\t')) )
738                         ++val;
739
740                     char *start = val;
741                     // skip till we get a blank character
742                     while( (val < end)
743                         && (*val != ' ' )
744                         && (*val != '\t') )
745                     {
746                         char c = *(val++);
747                         if( ('\'' == c) || ('"' == c) )
748                         {
749                             // skip till end of string
750                             while( (val < end) && (*(val++) != c ) );
751                         }
752                     }
753
754                     if( val > start )
755                     {
756                         if( nOptions == capacity )
757                         {
758                             capacity += 16;
759                             char **moreOptions = (char **)CoTaskMemRealloc(options, capacity*sizeof(char*));
760                             if( ! moreOptions )
761                             {
762                                 /* failed to allocate more memory */
763                                 CoTaskMemFree(s);
764                                 /* return what we got so far */
765                                 *cOptionCount = nOptions;
766                                 *cOptions = options;
767                                 return NOERROR;
768                             }
769                             options = moreOptions;
770                         }
771                         *(val++) = '\0';
772                         options[nOptions] = (char *)CoTaskMemAlloc(val-start);
773                         if( options[nOptions] )
774                         {
775                             memcpy(options[nOptions], start, val-start);
776                             ++nOptions;
777                         }
778                         else
779                         {
780                             /* failed to allocate memory */
781                             CoTaskMemFree(s);
782                             /* return what we got so far */
783                             *cOptionCount = nOptions;
784                             *cOptions = options;
785                             return NOERROR;
786                         }
787                     }
788                     else
789                         // must be end of string
790                         break;
791                 }
792                 *cOptionCount = nOptions;
793                 *cOptions = options;
794                 hr = NOERROR;
795             }
796             CoTaskMemFree(s);
797         }
798     }
799     return hr;
800 }
801
802 HRESULT VLCControl::CreateTargetOptions(int codePage, VARIANT *options, char ***cOptions, int *cOptionCount)
803 {
804     HRESULT hr = E_INVALIDARG;
805     if( VT_ERROR == V_VT(options) )
806     {
807         if( DISP_E_PARAMNOTFOUND == V_ERROR(options) )
808         {
809             // optional parameter not set
810             *cOptions = NULL;
811             *cOptionCount = 0;
812             return NOERROR;
813         }
814     }
815     else if( (VT_EMPTY == V_VT(options)) || (VT_NULL == V_VT(options)) )
816     {
817         // null parameter
818         *cOptions = NULL;
819         *cOptionCount = 0;
820         return NOERROR;
821     }
822     else if( VT_DISPATCH == V_VT(options) )
823     {
824         // if object is a collection, retrieve enumerator
825         VARIANT colEnum;
826         V_VT(&colEnum) = VT_UNKNOWN;
827         hr = GetObjectProperty(V_DISPATCH(options), DISPID_NEWENUM, colEnum);
828         if( SUCCEEDED(hr) )
829         {
830             IEnumVARIANT *enumVar;
831             hr = V_UNKNOWN(&colEnum)->QueryInterface(IID_IEnumVARIANT, (LPVOID *)&enumVar);
832             if( SUCCEEDED(hr) )
833             {
834                 long pos = 0;
835                 long capacity = 16;
836                 VARIANT option;
837
838                 *cOptions = (char **)CoTaskMemAlloc(capacity*sizeof(char *));
839                 if( NULL != *cOptions )
840                 {
841                     ZeroMemory(*cOptions, sizeof(char *)*capacity);
842                     while( SUCCEEDED(hr) && (S_OK == enumVar->Next(1, &option, NULL)) )
843                     {
844                         if( VT_BSTR == V_VT(&option) )
845                         {
846                             char *cOption = CStrFromBSTR(codePage, V_BSTR(&option));
847                             (*cOptions)[pos] = cOption;
848                             if( NULL != cOption )
849                             {
850                                 ++pos;
851                                 if( pos == capacity )
852                                 {
853                                     char **moreOptions = (char **)CoTaskMemRealloc(*cOptions, (capacity+16)*sizeof(char *));
854                                     if( NULL != moreOptions )
855                                     {
856                                         ZeroMemory(moreOptions+capacity, sizeof(char *)*16);
857                                         capacity += 16;
858                                         *cOptions = moreOptions;
859                                     }
860                                     else
861                                         hr = E_OUTOFMEMORY;
862                                 }
863                             }
864                             else
865                                 hr = ( SysStringLen(V_BSTR(&option)) > 0 ) ?
866                                     E_OUTOFMEMORY : E_INVALIDARG;
867                         }
868                         else
869                             hr = E_INVALIDARG;
870
871                         VariantClear(&option);
872                     }
873                     *cOptionCount = pos;
874                     if( FAILED(hr) )
875                     {
876                         // free already processed elements
877                         FreeTargetOptions(*cOptions, *cOptionCount);
878                     }
879                 }
880                 else
881                     hr = E_OUTOFMEMORY;
882
883                 enumVar->Release();
884             }
885         }
886         else
887         {
888             // coerce object into a string and parse it
889             VARIANT v_name;
890             VariantInit(&v_name);
891             hr = VariantChangeType(&v_name, options, 0, VT_BSTR);
892             if( SUCCEEDED(hr) )
893             {
894                 hr = parseStringOptions(codePage, V_BSTR(&v_name), cOptions, cOptionCount);
895                 VariantClear(&v_name);
896             }
897         }
898     }
899     else if( V_ISARRAY(options) )
900     {
901         // array parameter
902         SAFEARRAY *array = V_ISBYREF(options) ? *V_ARRAYREF(options) : V_ARRAY(options);
903
904         if( SafeArrayGetDim(array) != 1 )
905             return E_INVALIDARG;
906
907         long lBound = 0;
908         long uBound = 0;
909         SafeArrayGetLBound(array, 1, &lBound);
910         SafeArrayGetUBound(array, 1, &uBound);
911
912         // have we got any options
913         if( uBound >= lBound )
914         {
915             VARTYPE vType;
916             hr = SafeArrayGetVartype(array, &vType);
917             if( FAILED(hr) )
918                 return hr;
919
920             long pos;
921
922             // marshall options into an array of C strings
923             if( VT_VARIANT == vType )
924             {
925                 *cOptions = (char **)CoTaskMemAlloc(sizeof(char *)*(uBound-lBound+1));
926                 if( NULL == *cOptions )
927                     return E_OUTOFMEMORY;
928
929                 ZeroMemory(*cOptions, sizeof(char *)*(uBound-lBound+1));
930                 for(pos=lBound; (pos<=uBound) && SUCCEEDED(hr); ++pos )
931                 {
932                     VARIANT option;
933                     hr = SafeArrayGetElement(array, &pos, &option);
934                     if( SUCCEEDED(hr) )
935                     {
936                         if( VT_BSTR == V_VT(&option) )
937                         {
938                             char *cOption = CStrFromBSTR(codePage, V_BSTR(&option));
939                             (*cOptions)[pos-lBound] = cOption;
940                             if( NULL == cOption )
941                                 hr = ( SysStringLen(V_BSTR(&option)) > 0 ) ?
942                                     E_OUTOFMEMORY : E_INVALIDARG;
943                         }
944                         else
945                             hr = E_INVALIDARG;
946                         VariantClear(&option);
947                     }
948                 }
949             }
950             else if( VT_BSTR == vType )
951             {
952                 *cOptions = (char **)CoTaskMemAlloc(sizeof(char *)*(uBound-lBound+1));
953                 if( NULL == *cOptions )
954                     return E_OUTOFMEMORY;
955
956                 ZeroMemory(*cOptions, sizeof(char *)*(uBound-lBound+1));
957                 for(pos=lBound; (pos<=uBound) && SUCCEEDED(hr); ++pos )
958                 {
959                     BSTR option;
960                     hr = SafeArrayGetElement(array, &pos, &option);
961                     if( SUCCEEDED(hr) )
962                     {
963                         char *cOption = CStrFromBSTR(codePage, option);
964
965                         (*cOptions)[pos-lBound] = cOption;
966                         if( NULL == cOption )
967                             hr = ( SysStringLen(option) > 0 ) ?
968                                 E_OUTOFMEMORY : E_INVALIDARG;
969                         SysFreeString(option);
970                     }
971                 }
972             }
973             else
974             {
975                 // unsupported type
976                 return E_INVALIDARG;
977             }
978
979             *cOptionCount = pos-lBound;
980             if( FAILED(hr) )
981             {
982                 // free already processed elements
983                 FreeTargetOptions(*cOptions, *cOptionCount);
984             }
985         }
986         else
987         {
988             // empty array
989             *cOptions = NULL;
990             *cOptionCount = 0;
991             return NOERROR;
992         }
993     }
994     else if( VT_UNKNOWN == V_VT(options) )
995     {
996         // coerce object into a string and parse it
997         VARIANT v_name;
998         VariantInit(&v_name);
999         hr = VariantChangeType(&v_name, options, 0, VT_BSTR);
1000         if( SUCCEEDED(hr) )
1001         {
1002             hr = parseStringOptions(codePage, V_BSTR(&v_name), cOptions, cOptionCount);
1003             VariantClear(&v_name);
1004         }
1005     }
1006     else if( VT_BSTR == V_VT(options) )
1007     {
1008         hr = parseStringOptions(codePage, V_BSTR(options), cOptions, cOptionCount);
1009     }
1010     return hr;
1011 };
1012
1013 /*
1014 ** use VARIANT rather than a SAFEARRAY as argument type
1015 ** for compatibility with some scripting language (JScript)
1016 */
1017
1018 STDMETHODIMP VLCControl::addTarget(BSTR uri, VARIANT options, enum VLCPlaylistMode mode, int position)
1019 {
1020     if( 0 == SysStringLen(uri) )
1021         return E_INVALIDARG;
1022
1023     libvlc_instance_t *p_libvlc;
1024     HRESULT hr = _p_instance->getVLC(&p_libvlc);
1025     if( SUCCEEDED(hr) )
1026     {
1027         char *cUri = CStrFromBSTR(CP_UTF8, uri);
1028         if( NULL == cUri )
1029             return E_OUTOFMEMORY;
1030
1031         int cOptionsCount;
1032         char **cOptions;
1033
1034         if( FAILED(CreateTargetOptions(CP_UTF8, &options, &cOptions, &cOptionsCount)) )
1035             return E_INVALIDARG;
1036
1037         libvlc_exception_t ex;
1038         libvlc_exception_init(&ex);
1039
1040         position = libvlc_playlist_add_extended(p_libvlc, cUri, cUri,
1041                                                 cOptionsCount,
1042                                                 const_cast<const char**>(cOptions),
1043                                                 &ex);
1044
1045         FreeTargetOptions(cOptions, cOptionsCount);
1046         CoTaskMemFree(cUri);
1047
1048         if( libvlc_exception_raised(&ex) )
1049         {
1050             _p_instance->setErrorInfo(IID_IVLCPlaylist,
1051                 libvlc_exception_get_message(&ex));
1052             libvlc_exception_clear(&ex);
1053
1054             if( mode & VLCPlayListAppendAndGo )
1055                 _p_instance->fireOnStopEvent();
1056             return E_FAIL;
1057         }
1058
1059         if( mode & VLCPlayListAppendAndGo )
1060             _p_instance->fireOnPlayEvent();
1061         return NOERROR;
1062     }
1063     return hr;
1064 };
1065
1066 STDMETHODIMP VLCControl::get_PlaylistIndex(int *index)
1067 {
1068     return E_INVALIDARG;
1069 #if 0
1070     if( NULL == index )
1071         return E_POINTER;
1072
1073     libvlc_instance_t *p_libvlc;
1074     HRESULT result = _p_instance->getVLC(&p_libvlc);
1075     if( SUCCEEDED(result) )
1076     {
1077         *index = VLC_PlaylistIndex(i_vlc);
1078         return NOERROR;
1079     }
1080     *index = 0;
1081     return result;
1082 #endif
1083 };
1084
1085 STDMETHODIMP VLCControl::get_PlaylistCount(int *count)
1086 {
1087     if( NULL == count )
1088         return E_POINTER;
1089
1090     *count = 0;
1091     libvlc_instance_t* p_libvlc;
1092     HRESULT result = _p_instance->getVLC(&p_libvlc);
1093     if( SUCCEEDED(result) )
1094     {
1095         libvlc_exception_t ex;
1096         libvlc_exception_init(&ex);
1097
1098         *count = libvlc_playlist_items_count(p_libvlc, &ex);
1099         if( libvlc_exception_raised(&ex) )
1100         {
1101             _p_instance->setErrorInfo(IID_IVLCControl,
1102                 libvlc_exception_get_message(&ex));
1103             libvlc_exception_clear(&ex);
1104             return E_FAIL;
1105         }
1106         return NOERROR;
1107     }
1108     return result;
1109 };
1110
1111 STDMETHODIMP VLCControl::playlistNext(void)
1112 {
1113     libvlc_instance_t* p_libvlc;
1114     HRESULT result = _p_instance->getVLC(&p_libvlc);
1115     if( SUCCEEDED(result) )
1116     {
1117         libvlc_exception_t ex;
1118         libvlc_exception_init(&ex);
1119
1120         libvlc_playlist_next(p_libvlc, &ex);
1121         if( libvlc_exception_raised(&ex) )
1122         {
1123             _p_instance->setErrorInfo(IID_IVLCControl,
1124                 libvlc_exception_get_message(&ex));
1125             libvlc_exception_clear(&ex);
1126             return E_FAIL;
1127         }
1128         return NOERROR;
1129     }
1130     return result;
1131 };
1132
1133 STDMETHODIMP VLCControl::playlistPrev(void)
1134 {
1135     libvlc_instance_t* p_libvlc;
1136     HRESULT result = _p_instance->getVLC(&p_libvlc);
1137     if( SUCCEEDED(result) )
1138     {
1139         libvlc_exception_t ex;
1140         libvlc_exception_init(&ex);
1141
1142         libvlc_playlist_prev(p_libvlc, &ex);
1143         if( libvlc_exception_raised(&ex) )
1144         {
1145             _p_instance->setErrorInfo(IID_IVLCControl,
1146                 libvlc_exception_get_message(&ex));
1147             libvlc_exception_clear(&ex);
1148             return E_FAIL;
1149         }
1150         return NOERROR;
1151     }
1152     return result;
1153 };
1154
1155 STDMETHODIMP VLCControl::playlistClear(void)
1156 {
1157     libvlc_instance_t* p_libvlc;
1158     HRESULT result = _p_instance->getVLC(&p_libvlc);
1159     if( SUCCEEDED(result) )
1160     {
1161         libvlc_exception_t ex;
1162         libvlc_exception_init(&ex);
1163
1164         libvlc_playlist_clear(p_libvlc, &ex);
1165         if( libvlc_exception_raised(&ex) )
1166         {
1167             _p_instance->setErrorInfo(IID_IVLCControl,
1168                 libvlc_exception_get_message(&ex));
1169             libvlc_exception_clear(&ex);
1170             return E_FAIL;
1171         }
1172         return NOERROR;
1173     }
1174     return result;
1175 };
1176
1177 STDMETHODIMP VLCControl::get_VersionInfo(BSTR *version)
1178 {
1179     if( NULL == version )
1180         return E_POINTER;
1181
1182     const char *versionStr = libvlc_get_version();
1183     if( NULL != versionStr )
1184     {
1185         *version = BSTRFromCStr(CP_UTF8, versionStr);
1186         return (NULL == *version) ? E_OUTOFMEMORY : NOERROR;
1187     }
1188     *version = NULL;
1189     return E_FAIL;
1190 };
1191
1192 STDMETHODIMP VLCControl::get_MRL(BSTR *mrl)
1193 {
1194     if( NULL == mrl )
1195         return E_POINTER;
1196
1197     *mrl = SysAllocStringLen(_p_instance->getMRL(),
1198                 SysStringLen(_p_instance->getMRL()));
1199     return NOERROR;
1200 };
1201
1202 STDMETHODIMP VLCControl::put_MRL(BSTR mrl)
1203 {
1204     _p_instance->setMRL(mrl);
1205
1206     return S_OK;
1207 };
1208
1209 STDMETHODIMP VLCControl::get_AutoPlay(VARIANT_BOOL *autoplay)
1210 {
1211     if( NULL == autoplay )
1212         return E_POINTER;
1213
1214     *autoplay = _p_instance->getAutoPlay() ? VARIANT_TRUE: VARIANT_FALSE;
1215     return S_OK;
1216 };
1217
1218 STDMETHODIMP VLCControl::put_AutoPlay(VARIANT_BOOL autoplay)
1219 {
1220     _p_instance->setAutoPlay((VARIANT_FALSE != autoplay) ? TRUE: FALSE);
1221     return S_OK;
1222 };
1223
1224 STDMETHODIMP VLCControl::get_AutoLoop(VARIANT_BOOL *autoloop)
1225 {
1226     if( NULL == autoloop )
1227         return E_POINTER;
1228
1229     *autoloop = _p_instance->getAutoLoop() ? VARIANT_TRUE: VARIANT_FALSE;
1230     return S_OK;
1231 };
1232
1233 STDMETHODIMP VLCControl::put_AutoLoop(VARIANT_BOOL autoloop)
1234 {
1235     _p_instance->setAutoLoop((VARIANT_FALSE != autoloop) ? TRUE: FALSE);
1236     return S_OK;
1237 };