Ticket #2406: MacOSXDVDPlayer.cpp

File MacOSXDVDPlayer.cpp, 13.6 KB (added by awk@…, 18 years ago)
Line 
1/*
2    MacOSXDVDPlayer.cpp
3
4    (c) 2006 Andrew Kimpton
5    Part of the mythTV project
6
7    Starting point for the Mac OS X DVD Player
8*/
9
10#include <mythtv/mythcontext.h>
11#include <mythtv/libmythui/mythmainwindow.h>
12
13#include <Carbon/Carbon.h>
14
15#include "MacOSXDVDPlayer.h"
16
17MacOSXDVDPlayer *MacOSXDVDPlayer::sTheDVDPlayer = NULL;
18
19void MacOSXDVDPlayer::PlayDVD(const QString & inFilename)
20{
21//    VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::PlayDVD - inFilename = %1").arg(inFilename));
22   
23    // Construct a new DVD Player object
24    sTheDVDPlayer = new MacOSXDVDPlayer();
25    if (!sTheDVDPlayer->Initialize())
26        return;
27       
28    // Give it the Window details
29    sTheDVDPlayer->SetWindow(gContext->GetMainWindow());
30   
31    // Set it's volume info
32    sTheDVDPlayer->OpenMedia(inFilename);
33   
34    // And set it going
35    sTheDVDPlayer->Play();
36   
37    // The Play method installs new Carbon Event Handlers for mouse and key events in our window.
38    // So at this point we can return and just let the general app event loop behaviour run, none of
39    // Myth's native functions will be triggered since the event handling code here will receive all the events.
40    // ExitPlayer() can bell called to stop playback and return to Myth at any time.
41}
42
43void MacOSXDVDPlayer::ExitPlayer()
44{
45    DVDStop();
46    delete sTheDVDPlayer;
47    sTheDVDPlayer = NULL;
48}
49
50MacOSXDVDPlayer::MacOSXDVDPlayer() : mHaveMediaVolume(false), mHaveMediaFile(false), mWindowEventHandler(NULL), mDisplayID(NULL)
51{
52}
53
54MacOSXDVDPlayer::~MacOSXDVDPlayer()
55{
56    if (mWindowEventHandler)
57        RemoveEventHandler(mWindowEventHandler);
58    mWindowEventHandler = NULL;
59    if (mHaveMediaVolume || mHaveMediaFile)
60        CloseMedia();
61    DVDDispose();
62}
63
64void MyDVDErrorHandler (DVDErrorCode inErrorCode, UInt32 inRefCon)
65{
66    MacOSXDVDPlayer *aDVDPlayer = reinterpret_cast<MacOSXDVDPlayer*>(inRefCon);
67    if (aDVDPlayer)
68        aDVDPlayer->HandleDVDError(inErrorCode);
69}
70
71bool MacOSXDVDPlayer::Initialize()
72{
73    OSStatus anErr = DVDInitialize();
74   
75    if (anErr != noErr)
76    {
77        VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::Initialize - failed %1").arg(anErr));
78        return false;
79    }
80   
81    anErr = DVDSetFatalErrorCallBack (MyDVDErrorHandler, (UInt32) this);
82    return true;
83}
84
85void MacOSXDVDPlayer::HandleDVDError(DVDErrorCode inErrorCode)
86{
87    VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::HandleDVDError - error %1").arg(inErrorCode));
88}
89
90
91OSStatus MyWindowEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void * inUserData)
92{
93    MacOSXDVDPlayer *aDVDPlayer = reinterpret_cast<MacOSXDVDPlayer*>(inUserData);
94    if (aDVDPlayer)
95        return aDVDPlayer->WindowEventHandler(inHandlerCallRef, inEvent);
96    else
97        return eventNotHandledErr;
98}
99
100void MacOSXDVDPlayer::SetWindow(MythMainWindow *inWindow)
101{
102    WindowRef aMacWindow = (WindowRef) inWindow->handle();
103
104    OSStatus anErr = DVDSetVideoPort(GetWindowPort(aMacWindow));
105    if (anErr != noErr)
106        VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::SetWindow DVDSetVideoPort returned = %1").arg(anErr));
107   
108    GDHandle device;
109    anErr = GetWindowGreatestAreaDevice (aMacWindow, kWindowContentRgn, &device, NULL);
110    Boolean isSupported;
111    anErr = DVDSwitchToDevice (device, &isSupported);
112    if (anErr != noErr)
113        VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::SetWindow DVDSwitchToDevice returned = %1").arg(anErr));
114    if (!isSupported)
115        VERBOSE(VB_IMPORTANT, "MacOSXDVDPlayer::SetWindow - this display is not supported for DVDPlayback");
116
117    // Rect contentRect;
118    // RgnHandle contentRgn;
119    // GetWindowRegion(aMacWindow, kWindowContentRgn, contentRgn);
120    // GetRegionBounds(contentRegion);
121    HIViewRef contentView;
122    HIViewFindByID(HIViewGetRoot(aMacWindow), kHIViewWindowContentID, &contentView);
123    HIRect contentBounds;
124    HIViewGetBounds(contentView, &contentBounds);
125    Rect contentRect;
126    contentRect.left = CGRectGetMinX(contentBounds);
127    contentRect.right = CGRectGetMaxX(contentBounds);
128    contentRect.top = CGRectGetMinY(contentBounds);
129    contentRect.bottom = CGRectGetMaxY(contentBounds);
130   
131    anErr = DVDSetVideoBounds(&contentRect);
132    if (anErr != noErr)
133        VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::SetWindow DVDSetVideoBounds returned = %1").arg(anErr));
134
135    CGPoint pt;
136    pt.x = contentRect.left;
137    pt.y = contentRect.top;
138    CGDisplayCount ct;
139    CGGetDisplaysWithPoint(pt, 1, &mDisplayID, &ct);
140
141    if (!mDisplayID)
142        mDisplayID = CGMainDisplayID();
143   
144       
145    // Install a Carbon Event Handler on our window - this will catch mouse moved, mouse down and keyboard events and
146    // relay them to the DVDPlayback framework. It should also prevent the underlying MythTV interface from seeing the
147    // events
148    EventTypeSpec eventList[] = {
149        { kEventClassMouse, kEventMouseMoved },
150        { kEventClassMouse, kEventMouseDown },
151        { kEventClassKeyboard, kEventRawKeyDown}
152    };
153    anErr = InstallEventHandler(GetWindowEventTarget(aMacWindow), MyWindowEventHandler, sizeof(eventList)/sizeof(EventTypeSpec), eventList, this, &mWindowEventHandler);
154    if (anErr != noErr)
155        VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::SetWindow InstallEventHandler returned = %1").arg(anErr));
156}
157
158void MacOSXDVDPlayer::OpenMedia(const QString &inFilename)
159{
160    CFURLRef volumePath = NULL;
161    bool openMediaVolume = false;
162   
163    // If there is a leading dvd: then truncate that
164    QString mediaName = inFilename;
165    if (mediaName.startsWith("dvd:/"))
166    {
167        mediaName.remove("dvd:/");
168    }
169   
170    if (mediaName.startsWith("/dev"))
171    {
172        // We're playing a DVD on a disk
173        openMediaVolume = true;
174       
175        // Our name might will be /dev/rdisk<X> - but we need /dev/disk<x> - so what we do here
176        // is to remove the leading /dev/r if it exists and put it back as /dev/
177        if (mediaName.startsWith("/dev/r"))
178        {
179            mediaName.remove("/dev/r");
180            mediaName.prepend("/dev/");
181        }
182        // Now we have the BSD style name get the mount point using DiskArbitration
183        DASessionRef aDASession = DASessionCreate(kCFAllocatorDefault);
184        DADiskRef aDisk = DADiskCreateFromBSDName(kCFAllocatorDefault, aDASession, mediaName);
185        if (aDisk)
186        {
187            CFDictionaryRef aDiskDescription = DADiskCopyDescription(aDisk);
188           
189            if (aDiskDescription)
190            {
191                // Get the volume path
192                volumePath = (CFURLRef) CFDictionaryGetValue(aDiskDescription, kDADiskDescriptionVolumePathKey);
193                if (volumePath)
194                {
195                    CFRetain(volumePath);
196                }
197                CFRelease(aDiskDescription);
198            }
199        }
200        CFRelease(aDisk);
201        CFRelease(aDASession);
202    }
203   
204    if (volumePath)
205    {
206        FSRef mediaFSRef;
207        CFMutableStringRef video_ts_path = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFURLGetString(volumePath));
208        CFRelease(volumePath);
209        CFStringAppend(video_ts_path, CFSTR("VIDEO_TS"));
210        CFURLRef video_ts_url = CFURLCreateWithString(kCFAllocatorDefault, video_ts_path, NULL);
211        if (!CFURLGetFSRef(video_ts_url, &mediaFSRef))
212        {
213            VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::SetMedia - Could not get FSRef for path:"));
214            CFShow(video_ts_path);
215        }
216       
217        Boolean isValid;
218        OSStatus anErr = DVDIsValidMediaRef(&mediaFSRef, &isValid);
219        if (anErr != noErr)
220        {
221            VERBOSE(VB_IMPORTANT, "MacOSXDVDPlayer::SetMedia - DVDIsValidMediaRef Failed");
222        }
223        if (!isValid)
224        {
225            VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::SetMedia - DVDIsValidMediaRef media %1 is not value").arg(CFStringGetCStringPtr(video_ts_path, kCFStringEncodingMacRoman)));
226        }
227        if (mHaveMediaVolume || mHaveMediaFile)
228            CloseMedia();
229           
230        if (openMediaVolume)
231        {
232            anErr = DVDOpenMediaVolume(&mediaFSRef);
233            if (anErr != noErr)
234            {
235                VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::OpenMedia - cannot open media err = %1").arg(anErr));
236            }
237            else
238                mHaveMediaVolume = true;
239        }
240       
241        CFRelease(video_ts_path);
242    }
243}
244
245void MacOSXDVDPlayer::CloseMedia()
246{
247    if (mHaveMediaVolume)
248    {
249        DVDCloseMediaVolume();
250        mHaveMediaVolume = false;
251    }
252    if (mHaveMediaFile)
253    {
254        DVDCloseMediaFile();
255        mHaveMediaFile = false;
256    }
257}
258
259void MacOSXDVDPlayer::Play()
260{
261    OSStatus anErr = DVDPlay();
262    if (anErr != noErr)
263    {
264        VERBOSE(VB_IMPORTANT, QString("MacOSDVDPlayer::Play DVDPlay failed - %1").arg(anErr));
265    }
266}
267
268OSStatus MacOSXDVDPlayer::WindowEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent)
269{
270    (void)inHandlerCallRef;
271    OSStatus result = eventNotHandledErr;
272    DVDState currentDVDState;
273    DVDGetState(&currentDVDState);
274    Boolean onMenu = false;
275    DVDMenu aMenu;
276    DVDIsOnMenu (&onMenu, &aMenu);
277    if (onMenu)
278    {
279        mMostRecentMenu = aMenu;
280        if (!CGCursorIsVisible())
281            CGDisplayShowCursor(mDisplayID);
282    }
283    else
284    {
285        if (CGCursorIsVisible())
286            CGDisplayHideCursor(mDisplayID);
287    }
288    switch (GetEventClass(inEvent))
289    {
290        case kEventClassMouse:
291            {
292                Point portPt;
293                SInt32 index = kDVDButtonIndexNone;
294                GetEventParameter(inEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(portPt), NULL, &portPt);
295               
296                switch (GetEventKind(inEvent))
297                {
298                    case kEventMouseMoved:
299                        if (onMenu)
300                            DVDDoMenuMouseOver(portPt, &index);
301                        result = noErr;
302                        break;
303                    case kEventMouseDown:
304                        EventMouseButton mouseButton;
305                        GetEventParameter(inEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton);
306                        if (onMenu && (mouseButton == kEventMouseButtonPrimary))
307                            DVDDoMenuClick(portPt, &index);
308                        result = noErr;
309                        break;
310                    default:
311                        break;
312                }
313            }
314            break;
315        case kEventClassKeyboard:
316            {
317                switch (GetEventKind(inEvent))
318                {
319                    case kEventRawKeyDown:
320                    {
321                        enum {
322                            kEnter = 36,
323                            kSpacebar = 49,
324                            kEscape =  53,
325                            kDownArrow = 125,
326                            kUpArrow =  126,
327                            kLeftArrow = 123,
328                            kRightArrow = 124,
329                        };
330                        result = noErr;
331                        DVDUserNavigation theNavigation = 0;
332                        UInt32 keyCode;
333                        GetEventParameter(inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(keyCode), NULL, &keyCode);
334                        switch (keyCode)
335                        {
336                            case kEnter:
337                                theNavigation = kDVDUserNavigationEnter;
338                                break;
339                            case kEscape:
340                                if (onMenu && (mMostRecentMenu == kDVDMenuRoot))
341                                    ExitPlayer();
342                                else if (!onMenu && ((currentDVDState == kDVDStatePlaying) ||
343                                        (currentDVDState == kDVDStatePlayingStill) ||
344                                        (currentDVDState == kDVDStatePaused)))
345                                    DVDGoToMenu(mMostRecentMenu);
346                                else if (onMenu)
347                                    DVDReturnToTitle();
348                                break;
349                            case kSpacebar:
350                                if (currentDVDState == kDVDStatePlaying)
351                                    PausePlayback();
352                                else if (currentDVDState == kDVDStatePaused)
353                                    ResumePlayback();
354                                break;
355                            case kDownArrow:
356                                theNavigation = kDVDUserNavigationMoveDown;
357                                break;
358                            case kUpArrow:
359                                theNavigation = kDVDUserNavigationMoveUp;
360                                break;
361                            case kLeftArrow:
362                                theNavigation = kDVDUserNavigationMoveLeft;
363                                break;
364                            case kRightArrow:
365                                theNavigation = kDVDUserNavigationMoveRight;
366                                break;
367                            default:
368//                                VERBOSE(VB_IMPORTANT, QString("MacOSXDVDPlayer::WindowEventHandler - kEventRawKeyDown %1").arg(keyCode));
369                                break;
370                        }
371                        if (theNavigation != 0)
372                            DVDDoUserNavigation(theNavigation);
373                        break;
374                    }
375                    default:
376                        break;
377                }
378            }
379            break;
380        default:
381            break;
382    }
383    return result;
384}
385
386void MacOSXDVDPlayer::PausePlayback()
387{
388    DVDPause();
389}
390
391void MacOSXDVDPlayer::ResumePlayback()
392{
393    DVDResume();
394}