Основные задачи программирования

В этой главе Вы узнаете об основных операциях, которые необходимо реализовать для игры Видео DVD носителей. Для игры Видео DVD использования носителей DVD Playback Services необходимо будет выполнить эту последовательность операций:

  1. Начните новый сеанс воспроизведения DVD.

  2. Установите окно, которое будет использоваться для воспроизведения видео.

  3. Установите устройство видеодисплея для окна.

  4. Установите границы видео области в окне.

  5. Откройте папку носителей DVD-Video.

  6. Играйте носители и реагируйте на пользовательские действия.

  7. Закройте папку носителей.

  8. Закончите сеанс воспроизведения DVD.

Запуск сеанса воспроизведения

Инициализация

Когда Ваше приложение готово начать сеанс воспроизведения DVD, первый шаг должен вызвать DVDInitialize функция. Код результата указывает, доступен ли API для использования. Если код результата noErr, Вы готовы продолжиться к следующему шагу.

Если DVD Playback Services не может быть инициализирована, в этом примере приложение выходит:

OSStatus err = DVDInitialize();
if (err != noErr) {
    NSLog(@"DVDInitialize returned %d", err);
    [NSApp terminate:self];
}

Регистрация обработчика ошибок

Если ошибка I/O лишает возможности играть носители, DVD Playback Services использует механизм обратного вызова для уведомления приложения. Когда оптический диск грязен или поврежден, обычно эта ошибка происходит. Если Вы не регистрируете функцию обратного вызова для получения этой ошибки, приложение может неожиданно отказать. Ваш ошибочный обратный вызов должен сделать две вещи: (1) уведомляют пользователя, что фатальная ошибка произошла, и (2) заканчивают сеанс воспроизведения (см. Окончание Сеанса Воспроизведения).

Эта строка кода показывает, как зарегистрировать функцию обратного вызова для обработки фатальной ошибки:

OSStatus err = DVDSetFatalErrorCallBack (MyDVDErrorHandler, (UInt32) self);

В этом примере обратный вызов передает код ошибки методу, обрабатывающему ошибку:

void MyDVDErrorHandler (DVDErrorCode inErrorCode, UInt32 inRefCon) {
    Controller *controller = (Controller *)inRefCon;
    [controller handleDVDError:inErrorCode];
}
 
- (void) handleDVDError:(DVDErrorCode)error {
    NSLog(@"fatal error %d", error);
    [NSApp terminate:self];
}

Создание видеоокна

Как только сеанс активен, Ваше приложение должно создать окно для воспроизведения видео. Обычно характеристики Вашего видеоокна указаны в Интерфейсном файле пера Разработчика, и окно инстанцируют во время выполнения когда Ваши загрузки приложения перо.

Рекомендуемый тип окна является окном документа с изменением масштаба, минимизируйте и измените размеры средств управления. Рекомендуемый начальный размер представления является 640 x 480, который соответствует стандартному форматному соотношению DVD 4:3.

Если видеоокно является окном Cocoa, Вы устанавливаете окно воспроизведения видео с DVDSetVideoWindowID функция. В CocoaDVDPlayer видеоокно является подклассом NSWindow, имеющего метод для получения числа окна. Вот строка кода, устанавливающего окно:

OSStatus err = DVDSetVideoWindowID ([self windowNumber]);

Вот строка кода, устанавливающего видеоокно в приложении Углерода. Окно является переменной типа WindowRef):

OSStatus err = DVDSetVideoPort (GetWindowPort (myWindow));

Выбор графического дисплея

После того, как видеоокно создается, Ваше приложение должно сказать DVD Playback Services который графический дисплей использовать для воспроизведения DVD.

Приложения какао должны установить дисплей воспроизведения с DVDSwitchToDisplay, функция удобства, также проверяющая дисплей, чтобы удостовериться, что это поддерживается для воспроизведения. Убедитесь проверка булево значение, которое это пасовало назад, чтобы проверить, что поддерживается дисплей. Если это не поддерживается, Ваше приложение должно уведомить пользователя и завершить сеанс воспроизведения.

Окно Cocoa может счесть свой дисплей ID и установить дисплей следующим образом:

CGDirectDisplayID display = (CGDirectDisplayID)
    [[[[self screen] deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
Boolean isSupported;
OSStatus err = DVDSwitchToDisplay (display, &isSupported);

Приложения углерода могут найти графическое устройство для своего видеоокна с помощью функции Менеджера окон GetWindowGreatestAreaDevice, и может установить дисплей воспроизведения с помощью функции удобства DVDSwitchToDevice. Как с DVDSwitchToDisplay, обязательно проверьте булево значение, которое это пасовало назад.

GDHandle device;
OSStatus err = GetWindowGreatestAreaDevice (myWindow, kWindowContentRgn, &device, NULL);
Boolean isSupported;
err = DVDSwitchToDevice (device, &isSupported);

Если видеоокно перемещено в различный дисплей во время воспроизведения, необходимо использовать ту же процедуру для установки нового дисплея.

Установка границ видео области

Следующий шаг должен использовать DVDSetVideoBounds функция, чтобы сказать DVD Playback Services, где вывести на экран видеоконтент. Эта функция берет прямоугольник QuickDraw (Rect) это представляет границы видео области в Вашем окне. Прямоугольник выражен в локальных координатах окна. DVD Playback Services автоматически масштабирует видеоконтент для адаптации в этой видео области. Обычно размерности видео области совпадают с предметной областью окна, но это не требование.

Для видеоконтента, чтобы выглядеть правильной, видео область должна всегда иметь одно из двух форматных соотношений: стандарт (4:3) или широкий (16:9). Начальное форматное соотношение видео области обычно 4:3, форматное соотношение первого заголовка или меню на большей части DVDs.

Приложения, использующие прямоугольник Какао (NSRect) или Кварцевый прямоугольник (CGRect представлять границы видео области должно преобразовать его в прямоугольник QuickDraw перед вызовом DVDSetVideoBounds. Перечисление 2-1 показывает, как это сделано для окна Cocoa, в котором видео область и довольное представление имеют те же границы.

Перечисление 2-1  , Устанавливающее видео границы

- (void) setVideoBounds
{
    NSRect content = [[self contentView] bounds]; // 1
    NSRect frame = [self frame]; // 2
 
    Rect qdRect; // 3
    qdRect.left = 0;
    qdRect.right = content.size.width;
    qdRect.bottom = frame.size.height;
    qdRect.top = frame.size.height - content.size.height; // 4
 
    OSStatus err = DVDSetVideoBounds (&qdRect);
}

Вот то, что делает этот код:

  1. Получает высоту и ширину довольного представление.

  2. Получает высоту окна, включая строку заголовка.

  3. Создает прямоугольник QuickDraw для представления видео границ.

  4. Вычисляет позицию главного края в локальных координатах окна.

Каждый раз, когда пользователь изменяет размеры окна или выводит на экран новый заголовок с различным форматное соотношение, Ваше приложение должно вызвать DVDSetVideoBounds снова, чтобы уведомить DVD Playback Services, что изменилась видео область.

Открытие носителей

Теперь Вы готовы открыть носители DVD-Video для воспроизведения. Носители стандартного определения сохранены в папке носителей под названием VIDEO_TS. Видеодиск DVD стандартного определения имеет папку VIDEO_TS в своем корневом каталоге, например. DVD Playback Services позволяет только одной папке носителей быть открытой за один раз; прежде чем Вы попытаетесь открыть папку носителей, необходимо использовать DVDHasMedia функционируйте для проверки, чтобы видеть, открыты ли носители уже.

DVD Playback Services обеспечивает две функции для открытия носителей:

Обе функции берут в качестве ввода объект файла типа FSRef это указывает путь к папке носителей. Прежде, чем вызвать одну из этих функций, необходимо проверить объект файла использование DVDIsValidMediaRef функция.

Перечисление 2-2 показывает, как использовать эти функции:

Перечисление 2-2  Вводные носители

- (BOOL) openMedia:(FSRef *)media isVolume:(BOOL)isVolume
{
    Boolean isValid;
    OSStatus err = DVDIsValidMediaRef (media, &isValid);
    if (isValid)
    {
        if ([self hasMedia] == YES) {
            [self closeMedia];
        }
 
        if (isVolume) {
            err = DVDOpenMediaVolume (media);
        }
        else {
            err = DVDOpenMediaFile (media);
        }
    }
    return isValid;
}

Открытие папки носителей

Исходные приложения DVD, такие как iDVD и Studio DVD Pro могут быть использованы для создания папок носителей VIDEO_TS на жестком диске пользователей. Ваше приложение может быть призвано, чтобы открыть такую папку и играть ее содержание. Перечисление 2-3 показывает, как приложение Какао могло вывести на экран Открытое диалоговое окно, позволяющее пользователю перейти к и открыть папку носителей.

Перечисление 2-3  , Открывающее папку носителей

- (IBAction) onOpenMediaFolder:(id)sender
{
    NSOpenPanel *panel = [NSOpenPanel openPanel]; // 1
    [panel setCanChooseFiles:NO];
    [panel setCanChooseDirectories:YES];
    [panel setAllowsMultipleSelection:NO];
 
    if ([panel runModalForTypes:nil] == NSOKButton) // 2
    {
        NSString *folderPath = [[panel filenames] objectAtIndex:0]; // 3
        const char *cPath = [folderPath cStringUsingEncoding:NSASCIIStringEncoding]; // 4
        FSRef fileRef;
        OSStatus err = FSPathMakeRef ((UInt8*)cPath, &fileRef, NULL); // 5
        [self openMedia:&fileRef isVolume:NO]; // 6
    }
}

Вот то, что делает этот код:

  1. Создает панель Open и конфигурирует его для открытия единственной папки.

  2. Выводит на экран панель и проверки на кнопку OK.

  3. Получает путь POSIX к выбранной папке.

  4. Получает представление струны до пути.

  5. Преобразовывает строку пути в объект файла.

  6. Передает путь к openMedia метод (см. Перечисление 2-2.

Игра носителей

Если носители открыты, и воспроизведение останавливается или приостанавливается, можно начать играть носители путем вызова DVDPlay функция. Эта функция не берет параметров для управления позицией, в которой начинается воспроизведение. Вместо этого DVD Playback Services поддерживает маркер позиции, названный последней закладкой игры. Эта закладка указывает последнюю известную позицию воспроизведения. В новом сеансе воспроизведения последняя закладка игры очищена, чтобы указать, что воспроизведение должно запуститься в начале носителей. Ваше приложение может обновить или сбросить эту закладку с помощью функций DVDSetLastPlayBookmark и DVDClearLastPlayBookmark.

Начальные носители воспроизводят поток, обычно берет средство просмотра к экранному меню. Для игры заголовка функции или ответвления к различному меню пользователь управляет мышью или клавиатурой, чтобы выбрать и нажать кнопку меню. В ответ DVD Playback Services использует инструкции, встроенные в носители сама, чтобы обновить последнюю закладку игры и начать играть указанный выбор. В этой ситуации Ваше приложение не должно использовать DVDPlay.

В то время как носители играют, можно приостановить или остановить воспроизведение в текущем кадре. Если Вы вызываете DVDStop функционируйте дважды по очереди, последняя закладка игры очищена.

Ответ на пользовательские действия в меню DVD

Когда заголовок играет, и пользователь решает вывести на экран меню, необходимо ответить путем вызова DVDGoToMenu функция. С другой стороны необходимо вызвать DVDReturnToTitle функционируйте, когда меню находится на экране, и пользователь решает возвратиться к текущему заголовку.

Если пользователь щелкает по Кнопке меню в CocoaDVDPlayer, например, этот метод действия переключатели между заголовком и его главным меню:

- (IBAction) onToggleMenu:(id)sender
{
    if ((mDVDState == kDVDStatePlaying) ||
        (mDVDState == kDVDStatePlayingStill) ||
        (mDVDState == kDVDStatePaused))
    {
        Boolean onMenu = false;
        DVDMenu whichMenu;
        OSStatus err = DVDIsOnMenu (&onMenu, &whichMenu);
        if (onMenu) {
            err = DVDReturnToTitle();
        } else {
            err = DVDGoToMenu (kDVDMenuRoot);
        }
    }
}

Когда меню DVD сначала выведено на экран, обычно первая (самая верхняя) кнопка нажимается, как показано на рисунке 1-2. Поскольку пользователь управляет мышью или клавиатурой, чтобы выбрать и нажать желаемую кнопку, Ваше приложение отвечает путем уведомления DVD Playback Services. DVD Playback Services отвечает путем выбора или активации корректной кнопки на экране.

Для реализации этого поведения приложение должно выполнить следующие действия:

Перечисление 2-4 показывает, как обработать события от нажатия мыши в видеоокне. В приложении Какао для получения перемещенных в мышь событий необходимо будет также отправить setAcceptsMouseMovedEvents: обменивайтесь сообщениями к окну.

Перечисление 2-4  , Обрабатывающее события от нажатия мыши в видеоокне

- (void) sendEvent:(NSEvent *)theEvent
{
    SInt32 index = kDVDButtonIndexNone; // 1
 
    NSPoint location = [theEvent locationInWindow]; // 2
    Point portPt;
    portPt.h = location.x;
    portPt.v = [self frame].size.height - location.y;
 
    switch ([theEvent type])
    {
        OSStatus err;
        case NSMouseMoved:
            err = DVDDoMenuMouseOver (portPt, &index); // 3
            break;
        case NSLeftMouseDown:
            err = DVDDoMenuClick (portPt, &index); // 4
            break;
        default:
            break;
    }
 
    /* sync the cursor */
    NSCursor *cursor;
    if (index != kDVDButtonIndexNone) { // 5
        cursor = [NSCursor pointingHandCursor];
    } else {
            cursor = [NSCursor arrowCursor];
    }
    [cursor set];
 
    [super sendEvent:theEvent];
}

Вот то, что делает код:

  1. Объявляет, что переменная получает индекс кнопки, если мышь по кнопке.

  2. Получает расположение мыши в Кварцевых координатах (источник в левом нижнем углу окна).

  3. Когда курсор мыши в кнопке DVD, выводит на экран визуальную обратную связь в видеоокне.

  4. Активирует кнопку DVD, если курсор мыши в кнопке, когда щелкают мышью.

  5. Когда мышь по кнопке, устанавливает курсор в указывающую руку.

Перечисление 2-5 показывает, как обработать события клавиатуры, когда меню выведено на экран.

Перечисление 2-5  , Обрабатывающее события клавиатуры

- (BOOL) onKeyDown: (NSEvent *)theEvent
{
    NSString *keyString = [theEvent characters]; // 1
    unichar key = [keyString characterAtIndex:0]; // 2
    BOOL keyIsHandled = YES;
 
    switch (key) { // 3
        case NSUpArrowFunctionKey:
            DVDDoUserNavigation(kDVDUserNavigationMoveUp);
            break;
        case NSDownArrowFunctionKey:
            DVDDoUserNavigation(kDVDUserNavigationMoveDown);
            break;
        case NSLeftArrowFunctionKey:
            DVDDoUserNavigation(kDVDUserNavigationMoveLeft);
            break;
        case NSRightArrowFunctionKey:
            DVDDoUserNavigation(kDVDUserNavigationMoveRight);
            break;
        case NSCarriageReturnCharacter:
        case NSEnterCharacter:
            DVDDoUserNavigation(kDVDUserNavigationEnter);
            break;
        case ' ':
            if (mDVDState == kDVDStatePlaying)
                [self onPause:self];
            else if (mDVDState == kDVDStatePaused)
                [self onPlay:self];
            break;
        default:
            keyIsHandled = NO;
            break;
    }
 
    return keyIsHandled; // 4
}

Вот то, что делает этот код:

  1. Получает строку, содержащую представление Unicode одной или более клавиш, нажатых пользователем.

  2. Получает первый символ Unicode в строке.

  3. Обрабатывает ключ. Клавиши со стрелками используются для навигации между кнопками; введение и клавиши Return используются для нажатия кнопки; клавиша «Пробел» переключается между игрой и паузой.

  4. Возвращает флаг состояния (указание, было ли событие обработано) к окну, получившему событие.

Закрытие носителей

Когда пользователь закончен, играя объем носителей или папку, необходимо закрыть носители. Прежде, чем попытаться закрыть носители, необходимо вызвать DVDHasMedia функция для проверки носителей в настоящее время открыта. В зависимости от того, как носители был ранее открыт, необходимо вызвать одну из двух функций для закрытия его:

Окончание сеанса воспроизведения

Когда пользователь закончен с помощью DVD Playback Services, или когда фатальная ошибка происходит, необходимо закончить сеанс воспроизведения. Существует две основных задачи: (1) нерегистр все обратные вызовы события DVD, и (2) вызывают функцию DVDDispose, который позволяет другому процессу использовать DVD Playback Services.

В этом примере регистрационный номер обратного вызова события используется в качестве флага, чтобы удостовериться, что сеанс воспроизведения активен. Вы изучите, как этот регистрационный номер получен в Регистрации для Событий DVD.

- (void) endSession
{
    if (mEventCallBackID) {
        OSStatus err = DVDUnregisterEventCallBack (mEventCallBackID);
        err = DVDDispose();
        mEventCallBackID = 0;
    }
}