Техническое примечание TN2125

Ориентированное на многопотоковое исполнение программирование в QuickTime

QuickTime 1.0 представления технического примечания в 1992 начался с фразы «Мир, не останавливается - это перемещается, быстро». Начиная с его введения QuickTime шел в ногу, развиваясь за эти годы от простых 500 APIs до более чем 2 500, добавляя поддержку нового видео, неподвижного изображения и форматов аудио, потоковой передачи, интерактивность и т.д. Начиная с выпуска Mac OS X 10.3, QuickTime позволил разработчикам перемещать общие интенсивные процессором операции QuickTime в фоновые потоки, разрешающие более мощные и быстро реагирующие приложения.

Это техническое примечание нацелено на разработчиков QuickTime, предназначающихся для Mac OS X 10.3 или позже и хотящих добавить поддержку многопоточности их приложениям или компонентам.

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

Технический список книг

Для использования в своих интересах поддержки многопоточности QuickTime необходимо работать с Mac OS X 10.3 или позже и QuickTime 6.4 или позже. Более ранние версии Mac OS X не предоставляют системную поддержку уровня, требуемую для QuickTime быть вызванной от фоновых потоков, даже с последней версией установленного QuickTime.

QuickTime 7 обеспечивает еще большую гибкость, принятие Core Audio под капотом, например, удалило многие более ранние ограничения при работе с определенными звуковыми форматами носителя.

Разработчики QTKit, которые хотели бы использовать в своих интересах поддержку потокобезопасности, обсужденную в этом документе, должны предназначаться для Mac OS X 10.5 или QuickTime 7.3 или позже. Предыдущие версии не служат основой QTKit APIs, требуемый поддерживать ориентированные на многопотоковое исполнение операции.

Введение
Ориентированные на многопотоковое исполнение операции
Уведомление для разработчиков приложений
Работа с платформой QTKit
QuickTime для Windows
Уведомление для разработчиков компонентов
QuickTime Ориентированные на многопотоковое исполнение компоненты
Ссылки
История версии документа

Введение

Наследие QuickTime на традиционном Mac OS (Система 6.0.8 через Mac OS 9) означало, что многие из исходного APIs были разработаны, чтобы быть вызванными от единственного потока. Mac OS X, однако, представил разработчиков Macintosh питанию истинной многопроцессорной обработки - возможность выполнить многократные одновременные операции в многократных приложениях сразу. Mac OS X также удалил некоторые традиционные ограничения Mac OS относительно типов задач, которые могли быть выполнены с многократными потоками из любого отдельного приложения.

Почему это важно при разработке приложения QuickTime?

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

Как пример, думайте о сжатии фильма - несколько трудоемкие процессы. Возможно Ваше приложение хотело бы сжать два фильма одновременно или проигрывать один фильм при сжатии другого. Эти типы операций QuickTime до Mac OS X 10.3, возможно, представили недопустимые задержки обработки или отсутствие скорости отклика приложения.

Теперь рассмотрите перемещение этой работы от основного потока (поток блокирования пользовательского интерфейса) к фоновому потоку, или даже многим фоновым потокам. Ваше приложение могло тогда сжать и экспортировать многократные фильмы в различных форматах все одновременно при проигрывании другого фильма или выполнении некоторой другой задачи все, не блокируя пользовательский интерфейс.

Цель проекта поддержки многопоточности QuickTime Mac OS X 10.3 и позже в частности решает эту проблему. QuickTime теперь позволит разработчикам перемещать интенсивные процессором задачи в фоновые потоки, освобождая основной поток, таким образом, улучшающий скорость приложения, скорость отклика и общую производительность.

Ориентированные на многопотоковое исполнение операции

Mac OS X 10.3 и позже теперь позволяет Вам выполнять следующие операции QuickTime от фоновых потоков:

Эти типы операций могут привести много в рабочее состояние времени обработки, таким образом вызывающего узкие места CPU и проблемы скорости отклика пользовательского интерфейса. Они идеальны для перемещения в фоновые потоки.

Прежде, чем обсудить, что может быть сделано ориентированным на многопотоковое исполнение, мы должны определить несколько условий; фильм в формате QuickTime, Файл ролика, QuickTime возражает и Непересекающийся.

Что может быть сделано ориентированным на многопотоковое исполнение

Заявки QuickTime, работающие со следующими менеджерами по панели инструментов, могут быть поданы ориентированные на многопотоковое исполнение:

  • Панель инструментов фильма (импорт, рендеринг, создание, экспорт)

  • Программа управления сжатием изображений

  • QuickDraw (внеэкранный GWorlds)

  • Менеджер компонентов

  • Менеджер по псевдониму

  • Диспетчер памяти

Независимые потоки в Вашем приложении могут безопасно использовать логически непересекающиеся наборы объектов QuickTime от многократных потоков. Если один поток имеет свой собственный уникальный набор объектов QuickTime, и другой поток имеет свой собственный уникальный набор, то любую работу, выполняемую на этих объектах, можно считать ориентированной на многопотоковое исполнение. Два потока не должны иметь никаких объектов QuickTime вместе. Посмотрите рисунок 1.

Например, два фоновых потока могли импортировать.DV поток, тогда продвигаются через каждый Фильм с помощью APIs такой как GetNextInterestingTime, SetMovieTime и рендеринг структурирует путем вызова MoviesTask.

Рисунок 1  Непересекающийся доступ ориентирован на многопотоковое исполнение.

С другой стороны, если два потока получают доступ к тому же объекту QuickTime или объектам одновременно, то любую работу, выполняемую на этих объектах, не считают ориентированной на многопотоковое исполнение. Потоки, управляющие этими объектами, требуются, чтобы выполнять свою собственную синхронизацию (или блокирующий) вокруг любого доступа к объекту. Это не отличается от любого незащищенного (или глобальная переменная) структура данных, к которой нужно получить доступ от многократных потоков. Некоторая защита потока должна быть реализована прежде, чем предоставить одновременный доступ. Посмотрите рисунок 2.

  Одновременный доступ рисунка 2 не ориентирован на многопотоковое исполнение.

Взаимные исключения являются простыми блокировками и могут использоваться для управления доступом к некоторому объекту QuickTime как Графическая Последовательность Экземпляра или Распаковки Средства импорта, совместно использованная двумя фоновыми потоками.

Взаимные исключения имеют только два состояния, заблокированные и разблокированные. Условная переменная POSIX может использоваться со взаимными исключениями, чтобы позволить потокам блокировать и ожидать сигнала от другого потока. Когда сигнал поступает, поток, это блокируется, может тогда попытаться захватить блокировку на связанном взаимном исключении.

Multiprocessing Services также обеспечивает синхронизацию потока и сигнальные механизмы.

Посмотрите раздел References в конце этого Технического примечания для получения дополнительной информации относительно pthreads и Multiprocessing Services.

Исключение происходит при работе с файловой системой. Подпрограммы операционной системы, реализующие доступ к файлам, все ориентированы на многопотоковое исполнение. Поскольку файловая система разработана для многопоточного доступа, независимые потоки могут иметь два объекта QuickTime, относящиеся к тому же файлу. Это прекрасно подходит для имения двух долей потоков тот же файл ролика для чтения. Два потока могут иметь свои собственные отдельные фильмы в формате QuickTime, ссылающиеся на тот же файл ролика при выполнении операций чтения. Например, эти потоки могли представить или вывести на экран различные части Фильма одновременно без любых проблем. Однако может не быть безопасно для одного из этих потоков записать в файл ролика, в то время как другой поток читает из него без некоторой блокировки. Посмотрите рисунок 3.

Рисунок 3  , Совместно использующий тот же файл ролика для чтения, ориентирован на многопотоковое исполнение.

Ключевой пункт в этом обсуждении - то, что Ваше приложение не должно позволять больше чем одному потоку работать над тем же объектом QuickTime в то же время, если Вы хотите гарантировать потокобезопасность. Это - Ваша ответственность в качестве вызывающей стороны для обеспечения этого дело обстоит так.

Что не ориентировано на многопотоковое исполнение

Следующие менеджеры по Панели инструментов не ориентированы на многопотоковое исполнение; приложения QuickTime не могут использовать свои службы от фоновых потоков:

  • HIToolbox (пользовательский интерфейс)

  • Менеджер ресурсов

Кроме того, следующая функциональность Панели инструментов Фильма не доступна от фоновых потоков:

  • Воспроизведение фильма

  • Контроллер фильма

Панель инструментов Интерфейса пользователя обеспечивает элементы пользовательского интерфейса для приложений. Работа с HIToolbox, однако, не ориентирована на многопотоковое исполнение. Кроме того, это - основные потоки выполненный цикл, обычно получающий все события, сгенерированные пользователем.

Не имеет значения, если Вы работаете с QuickTime в среде Углерода или Какао, элементы пользовательского интерфейса должны остаться на основном потоке, и это - Ваша ответственность в качестве вызывающей стороны, чтобы избежать создавать или представлять Диалоговое окно или Окно на фоновом потоке. Элементы пользовательского интерфейса QuickTime включают Стандартное Диалоговое окно Сжатия, Диалоговые окна, представленные APIs такой как MovieExportDoUserDialog, Процедура Прогресса Экспорта Фильма по умолчанию и т.д. Посмотрите рисунок 4.

Рисунок 4  Сохраняет весь пользовательский интерфейс на основном потоке.

Работа с Менеджером ресурсов также не ориентирована на многопотоковое исполнение, потому что его APIs включает использование глобального состояния, названного «цепочкой ресурса». Цепочка ресурса является списком открытого файла ресурсов. Это - эффективно неявный параметр, переданный большей части Менеджера ресурсов APIs. Это означает, что Вы не должны вызывать APIs такой как OpenComponentResFile, OpenAComponentResFile, CloseComponentResFile и т.д., от фоновых потоков.

Панель инструментов Фильма обеспечивает функциональность, позволяющую Вам импортировать, играть, представлять, создавать, редактировать и хранить основанные на времени данные. Фильмы не могут проигрываться на фоновых потоках, поэтому вызывание Панели инструментов Фильма воспроизводит APIs такой как StartMovie или SetMovieRate должен только быть сделан от основного потока.

Контроллер Фильма (и Управление Фильмом Углерода, о котором можно думать с точки зрения более знакомого Управления HIToolbox) обеспечивает целый комплект функциональности при работе с фильмами в формате QuickTime; воспроизведение, редактирование, пользовательский интерфейс, интерактивная диспетчеризация события для именования некоторых. Контроллер Фильма, однако, не ориентирован на многопотоковое исполнение и должен только создаваться и использоваться от основного потока.

Уведомление для разработчиков приложений

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

Это означает, что некоторые медиа-файлы не могут быть открыты, и определенные преобразования носителей не могут быть выполнены безопасно от фоновых потоков. Ваше приложение должно будет принять стратегию иметь дело с этой возможностью.

Mac OS X 10.3 включает много ориентированных на многопотоковое исполнение и неориентированных на многопотоковое исполнение компонентов. Например, в то время как наиболее широко используемые форматы неподвижного изображения, такие как JPEG, PNG и TIFF были сделаны, ориентированный на многопотоковое исполнение Макпэйнт не имеет. Можно использовать это в ваших интересах при наличии очень хорошо определенного случая, чтобы протестировать, как хорошо код обрабатывает контакт с компонентами требования носителей, которые не ориентированы на многопотоковое исполнение. В большинстве случаев необходимо будет переместить объект QuickTime в основной поток и продолжать работу. Импорт потоков и выборки экспорта в разделе Scenarios и Sample Code этого документа демонстрируют этот метод.

Вызов EnterMoviesOnThread

Приложения с помощью QuickTime на фоновых потоках должны теперь вызвать EnterMoviesOnThread на каждом фоновом потоке прежде, чем вызвать любой другой QuickTime APIs на тех потоках. Когда QuickTime больше не будет использоваться на фоновом потоке, поток должен вызвать ExitMoviesOnThread. Это указывает к QuickTime, что приложение больше не будет использовать QuickTime от того потока.

EnterMoviesOnThread / ExitMoviesOnThread пара не является тем же как EnterMovies / ExitMovies пара. Приложения должны продолжать вызывать EnterMovies если QuickTime APIs используется на основном потоке.

EnterMoviesOnThread инициализирует частную на поток среду QuickTime, так вызовы к GetMoviesError или MoviesTask(NULL, 0), например, не получит ошибки или фильмы задачи в других потоках.

Вызов EnterMoviesOnThread также сообщает Менеджеру компонентов, что он не должен позволять использование никаких неориентированных на многопотоковое исполнение компонентов на том потоке. Если Менеджер компонентов соберется открыть неориентированный на многопотоковое исполнение компонент для выполнения определенной функции, то он возвратит a componentNotThreadSafeErr (-2098) ошибка, не открывая компонент. Этот код ошибки тогда распространит до вызывающей стороны.

Ваше приложение может получить a componentNotThreadSafeErr от любого QuickTime API, вызванный от фонового потока, и, должен использовать это в качестве уведомления, что работа, выполняемая на фоновом потоке, должна быть смещена к основному потоку.

Перечисление 1  EnterMoviesOnThread.

OSErr EnterMoviesOnThread(UInt32 inFlags)

EnterMoviesOnThread is used to indicate to QuickTime that an application will be using
QuickTime APIs on the current thread.

inFlags - flags indicating how the executing thread will be using QuickTime. Pass 0 for
the default options.

Flags:

kQTEnterMoviesFlagDontSetComponentsThreadMode = 1L << 0

Discussion:

Applications should call EnterMoviesOnThread on threads they create. If
EnterMoviesOnThread is not called on a spawned preemptive thread when calling QuickTime
APIs, the global QuickTime state will be shared with the main thread.

By default, EnterMoviesOnThread will set the current Component Manager thread mode
according to state of QuickTime's thread sharing policy. If the QuickTime state is
private to the thread, the mode will be set to kCSAcceptThreadSafeComponentsOnlyMode. If
the QuickTime state is shared between the thread calling EnterMoviesOnThread and the
main thread, then the mode will be set to kCSAcceptAllComponentsMode.

By including the kQTEnterMoviesFlagDontSetComponentThreadMode flag in the call to
EnterMoviesOnThread, no change to the thread mode will be made leaving it as it was
before the call. Setting the thread mode is a convenience provided by
EnterMoviesOnThread and can be done directly using CSSetComponentsThreadMode. You can
also get the current thread mode by calling CSGetComponentsThreadMode.

The thread mode set by calling EnterMoviesOnThread or CSSetComponentsThreadMode is
thread-local, not process-global.

The first call to EnterMoviesOnThread will change the Component Manager thread mode
unless the kQTEnterMoviesFlagDontSetComponentsThreadMode flag is passed. All subsequent
calls will leave the Component Manger thread mode unaffected.

Multiple calls to EnterMoviesOnThread can be made on a single thread. An example may be
an application that spawns a thread and calls EnterMoviesOnThread. The application then
calls library code that also uses QuickTime. The library code, which is unable to
predict if the caller initialized QuickTime, will call also call EnterMoviesOnThread.
This matches the typical usage of EnterMovies today by libraries.

Перечисление 2  ExitMoviesOnThread.

OSErr ExitMoviesOnThread(void)

ExitMoviesOnThread is used to indicate to QuickTime that the application will no longer
be using QuickTime APIs on the current thread.

Discussion:

ExitMoviesOnThread returns an appropriate operating system or QuickTime error if the
operation couldn't be completed. This might occur because a previous call to
EnterMoviesOnThread was not made.

ExitMoviesOnThread should be called before exiting from a background thread that uses
QuickTime and undoes the setup performed by EnterMoviesOnThread.

Each EnterMoviesOnThread call should be matched with an ExitMoviesOnThread.
ExitMoviesOnThread should not be called on a thread without a previous call to
EnterMoviesOnThread.

Note that after the last ExitMoviesOnThread is called on a background thread, subsequent
calls to QuickTime APIs without calling EnterMoviesOnThread first will result in threads
sharing the main thread's state just as though the application didn't use the
EnterMoviesOnThread / ExitMoviesOnThread pair. This is for compatibility.

Not calling ExitMoviesOnThread while not fatal can potentially result in resource leaks.
Callers should therefore bracket all QuickTime calls on secondary threads between an
initial EnterMoviesOnThread and final ExitMoviesOnThread.

Движущийся QuickTime возражает между потоками

В некоторых случаях Вы, возможно, должны переместить объект QuickTime от одного потока до другого.

При работе с экземплярами Графических Средств импорта и Графических Средств экспорта, необходимо будет управлять этим путем реализации собственного механизма блокировки и гарантируя, что объект QuickTime (Экземпляр Компонента в этом случае) только вызывают от одного потока за один раз.

Фильмы в формате QuickTime, с другой стороны, должны знать, какому потоку они принадлежат в любой момент времени. Существует два APIs, который нужно вызвать, переместив фильм в формате QuickTime от одного потока до другого. Необходимо сначала отсоединиться, Фильм от старого потока тогда присоединяют его к новому потоку.

При передаче фильма в формате QuickTime от одного потока до другого вызвать DetachMovieFromCurrentThread в старом потоке и AttachMovieToCurrentThread в новом потоке. Это позволяет QuickTime знать, какой поток владеет Фильмом и гарантирует, что для Фильма весьма правильно определяют задачу на неправильном потоке. Посмотрите рисунок 5.

Рисунок 5  , Перемещающий фильм в формате QuickTime между потоками.

Получение ссылки Фильма, использующей любой из NewMovie... APIs такой как NewMovie, NewMovieFromDataRef, NewMovieFromFile и т.д. создаст Фильм, который уже присоединен к текущему потоку. Вызовы к AttachMovieToCurrentThread если фильм уже будет присоединен к потоку, перестанет работать. Если Вы открываете или создаете фильм с намерением передать его фоновому потоку, вызвать DetachMovieFromCurrentThread на потоке создания перед вызовом AttachMovieToCurrentThread на фоновом потоке.

Вызов DetachMovieFromCurrentThread отсоединять фильм в формате QuickTime, содержащий небезопасные носители (носители, требующие, чтобы неориентированные на многопотоковое исполнение компоненты представляли) от основного потока, приведет к сбою и возвратит a componentNotThreadSafeErr. Как ранее упомянуто, Ваше приложение должно быть готово справиться с этой особой ситуацией и или выполнить работу на основном потоке, переместить работу в основной поток, если бы ошибка была получена на фоновом потоке, или отмените работу как требуется.

Перечисление 3  AttachMovieToCurrentThread.

OSErr AttachMovieToCurrentThread(Movie m)

m - the Movie for this operation. Your application obtains this
Movie identifier from such functions as NewMovie, NewMovieFromFile, and
NewMovieFromHandle.

Returns noErr if there is no error or componentNotThreadSafeErr if the Movie cannot
be attached to the current thread.

Discussion:

Attaches a movie to the current thread.

Перечисление 4  DetachMovieFromCurrentThread.

OSErr DetachMovieFromCurrentThread(Movie m)

m - the movie for this operation. Your application obtains this
movie identifier from such functions as NewMovie, NewMovieFromFile, and
NewMovieFromHandle.

Returns noErr if there is no error.

Discussion:

Detaches a Movie from the current thread.

Контакт с обратными вызовами

Много QuickTime APIs допускают установку подпрограмм обратного вызова, наиболее распространенное, являющееся асинхронными обратными вызовами завершения и обратными вызовами прогресса.

Асинхронные обратные вызовы завершения всегда вызывали от специальных потоков и должны быть ориентированы на многопотоковое исполнение. Эти потоки не являются тем же потоком, выполняющим работу; это поведение не изменилось в Mac OS X 10.3.

Другие обратные вызовы QuickTime, однако, вызывают от того же потока, выполняющего работу.

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

В случаях, таких как это, Вы, возможно, должны заново продумать свои обратные вызовы прогресса для создания их ориентированными на многопотоковое исполнение. Один очень полезный метод должен использовать пользовательские события углерода, отправленные от обратного вызова прогресса на фоновом потоке к обработчику событий углерода на основном потоке. Это может быть выполнено при помощи ориентированного на многопотоковое исполнение API PostEventToQueue от фонового потока. Посмотрите рисунок 6.

API GetMainEventQueue может использоваться для получения основной очереди событий EventQueueRef - это - очередь, на которую будет отправлено событие Carbon.

На Mac OS X 10.4 и больше, GetMainEventQueue ориентировано на многопотоковое исполнение и может быть вызван от фонового потока, который может отправлять событие.

  Использование рисунка 6 PostEventToQueue для отправки пользовательских Событий Углерода в основной поток.

Когда Вы не хотите использования функции прогресса Фильма NULL:

SetMovieProgressProc(theMovie, NULL, NULL).

Контакт с менеджером ресурсов

Менеджер ресурсов не ориентирован на многопотоковое исполнение. Критически важно помнить, что это включает любые ресурсы управления APIs, цепочку ресурса или карты ресурса.

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

Они включают следующий APIs:

  • GetComponentResource

  • GetComponentIndString

  • GetComponentPublicResource

  • GetComponentPublicIndString

  • GetComponentPublicResourceList

Перечисление 5  Используя GetComponentPublicResource.

ComponentDescription cd;
ResourceHandle resource = NULL;
Component c = 0;

cd.componentType = MovieExportType;
cd.componentSubType = kAComponentSubType;
cd.componentManufacturer = kAManufacturer;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;

c = FindNextComponent(c, &cd)
if (c) {
    err = GetComponentPublicResource(c, 'PICT', 1, &resource);
    if (noErr == err) {
        // do something with the resource

        ...
        DisposeHandle(resource);
    }
}

Для получения дополнительной информации об этом APIs посмотрите Ледяную Отгрузку Плавучей льдины 21

Сценарии и пример кода

(a) Импорт неподвижного изображения - Пример кода ThreadsImporter

ThreadImporter демонстрирует импорт и отображение неподвижных изображений на отдельных потоках.

(b) Экспорт неподвижного изображения - Пример кода ThreadsExporter

ThreadsExporter демонстрирует импорт и экспорт неподвижных изображений в различных форматах на отдельных потоках.

(c) Импорт Фильма в формате QuickTime - Пример кода ThreadsImportMovie

ThreadsImportMovie демонстрирует импорт и отображение фильмов в формате QuickTime на отдельных потоках.

(d) Экспорт Фильма в формате QuickTime - Пример кода ThreadsExportMovie

ThreadsExportMovie демонстрирует Фильмы экспорта с помощью Компонента Экспорта фильма в формате QuickTime на отдельных потоках.

(e) Открытие Movie на основном потоке, тогда перемещающем копию Фильма к фоновому потоку.

Перечисление 8 содержит общий фрагмент кода, демонстрирующий, как Фильм может быть перемещен от основного потока до фонового потока для некоторой последующей обработки. Это используется с функциями работника, показанными в списках 6 и 7. Функция Do_SomeWorkOnSeparateThread вызывается с WindowRef, содержащим фильм в формате QuickTime, и создает фоновый поток, тогда выполняющий функцию DoSomeWorkFunction показанный в тех списках.

(f) Экспортируйте перемещенный фильм от фонового потока.

Перечисление 6 открывает QuickTime Movie Export Component, конфигурирует, это для сжатия видео с помощью Кодека DV тогда вызывает ConvertMovieToDataRef выполнять работу экспорта от фонового потока.

Перечисление 6  , Экспортирующее Фильм на фоновом потоке.

// This worker function performs the Movie Export operation on a background thread
void *DoSomeWorkFunction(void *inWorkerThreadRef)
{
    QTAtomContainer exportSettings;
    Handle theDataRef;
    OSType theDataRefType;
    EventRef theEventRef = NULL;
    OSStatus status;

    WorkerThreadRef worker = (WorkerThreadRef)inWorkerThreadRef;

    EnterMoviesOnThread(0);

    status = AttachMovieToCurrentThread(worker->theMovie);
    require_noerr(status, CantAttachToCurrentThread);

    CFStringRef path = CFSTR("/Users/fasteddie/Desktop/testoutput.mov");

    status = QTNewDataReferenceFromFullPathCFString(path, kQTNativeDefaultPathStyle,
                                                    0, &theDataRef, &theDataRefType);
    require_noerr(status, Done);

    // open the QuickTime Movie Export component and configure it
    ComponentInstance ci = OpenDefaultComponent(MovieExportType, kQTFileTypeMovie);
    if (ci) {

        SCSpatialSettings ss;
        SCTemporalSettings ts;
        UInt8 falseSetting = false;
        QTAtom videAtom = 0;
        QTAtom sptlAtom = 0;
        QTAtom tprlAtom = 0;
        QTAtom ensoAtom = 0;
        QTAtom saveAtom = 0;
        QTAtom fastAtom = 0;

        ss.codecType = kDVCNTSCCodecType;
        ss.codec = NULL;
        ss.depth = 0;
        ss.spatialQuality = codecHighQuality;

        ts.temporalQuality = 0;
        ts.frameRate = FixRatio(30, 1); //30L<<16;
        ts.keyFrameRate = 0;

        // get the defaults and change them - you could keep these around if you want
        status = MovieExportGetSettingsAsAtomContainer(ci, &exportSettings);
        require_noerr(status, Done);

        // video options
        videAtom = QTFindChildByID(exportSettings, kParentAtomIsContainer,
                                   kQTSettingsVideo, 1, NULL);
        if (videAtom) {
            // spatial
            sptlAtom = QTFindChildByID(exportSettings, videAtom, scSpatialSettingsType,
                                       1, NULL);
            if (sptlAtom) {
                status = QTSetAtomData(exportSettings, sptlAtom,
                                       sizeof(SCSpatialSettings), &ss);
            }
            // temporal
            tprlAtom = QTFindChildByID(exportSettings, videAtom, scTemporalSettingsType,
                                       1, NULL);
            if (tprlAtom) {
                status = QTSetAtomData(exportSettings, tprlAtom,
                                       sizeof(SCTemporalSettings), &ts);
            }
        }

        // we only care about video
        // disable export sound
        ensoAtom = QTFindChildByID(exportSettings, kParentAtomIsContainer,
                                   kQTSettingsMovieExportEnableSound, 1, NULL);
        if (ensoAtom) {
            status = QTSetAtomData(exportSettings, ensoAtom, sizeof(falseSetting),
                                   &falseSetting);
        }

        // turn off save for internet options aka fastStart
        saveAtom = QTFindChildByID(exportSettings, kParentAtomIsContainer,
                                   kQTSettingsMovieExportSaveOptions, 1, NULL);
        if (saveAtom) {
            fastAtom = QTFindChildByID(exportSettings, saveAtom,
                                       kQTSettingsMovieExportSaveForInternet, 1, NULL);
            if (fastAtom) {
                status = QTSetAtomData(exportSettings, fastAtom, sizeof(falseSetting),
                                       &falseSetting);
            }
        }

        // set 'em
        status = MovieExportSetSettingsFromAtomContainer(ci, exportSettings);
        require_noerr(status, Done);

        // no progress proc - if you do use a custom progress proc remember that it will
        // be called on this thread and that you cannot use any user interface
        // one approach you could use if you required user interface would be to create
        // custom carbon events and post them to a handler installed on the main thread
        SetMovieProgressProc(worker->theMovie, NULL, NULL);

        // export the movie
        ConvertMovieToDataRef(worker->theMovie, 0, theDataRef, theDataRefType,
                              kQTFileTypeMovie, FOUR_CHAR_CODE('TVOD'),
                              createMovieFileDeleteCurFile |
                              createMovieFileDontCreateResFile,
                              ci);
    }

Done:

    DetachMovieFromCurrentThread(worker->theMovie);

    if (ci) CloseComponent(ci);
    if (exportSettings) QTDisposeAtomContainer(exportSettings);
    if (theDataRef) DisposeHandle(theDataRef);

CantAttachToCurrentThread:

    ExitMoviesOnThread();

    worker->threadStatus = status;

    CreateEvent(NULL, kEventClassQTThreading, kEventAppCleanUpThreadDroppings, 0,
                kEventAttributeNone, &theEventRef);
    SetEventParameter(theEventRef, kEventParamThreadData, typeWorkerThreadRef,
                      sizeof(worker), &worker);

    if (theEventRef) {
        PostEventToQueue(worker->mainEventQueue, theEventRef,
                         kEventPriorityStandard);
        ReleaseEvent(theEventRef);
    }

    pthread_exit(NULL);
}

(g) Рендеринг видео к GWorld и созданию Фильма с нуля на фоновом потоке.

Перечисление 7 создает новый Фильм и файл ролика, представляет перемещенный фильм к a '2vuy' GWorld, управляет luma и сохраняет демонстрационные данные в недавно создаваемый файл ролика все на фоновом потоке.

  Рендеринг перечисления 7 и Создание Фильма на фоновом потоке.

// This worker function renders and creates a new Movie on a background thread
void *DoSomeWorkFunction(void *inWorkerThreadRef)
{
    Handle theDataRef;
    OSType theDataRefType;
    DataHandler theDataHandler = 0;
    Movie theNewMovie = NULL;
    ImageDescriptionHandle id;
    GWorldPtr theGWorld;
    PixMapHandle theGWorldPixMap;
    unsigned long theGWorldRowBytes;
    Ptr baseAddr;
    long theDataSize;
    Track theTrack;
    Media theMedia;
    Rect srcRect;
    short flags;
    OSType whichMediaType = VideoMediaType;
    TimeValue movieTime = 0;
    TimeValue duration;
    EventRef theEventRef = NULL;
    OSStatus status;

    WorkerThreadRef worker = (WorkerThreadRef)inWorkerThreadRef;

    EnterMoviesOnThread(0);

    status = AttachMovieToCurrentThread(worker->theMovie);
    require_noerr(status, CantAttachToCurrentThread);

    CFStringRef path = CFSTR("/Users/fasteddie/Desktop/testoutput.mov");

    status = QTNewDataReferenceFromFullPathCFString(path, 
                                                    kQTNativeDefaultPathStyle,
                                                    0,
                                                    &theDataRef,
                                                    &theDataRefType);
    require_noerr(status, Done);

    status = CreateMovieStorage(theDataRef, theDataRefType,
                                FOUR_CHAR_CODE('TVOD'), smSystemScript,
                                createMovieFileDeleteCurFile |
                                createMovieFileDontCreateResFile,
                                &theDataHandler, &theNewMovie);
    require_noerr(status, Done);

    // get the size of the movie
    GetMovieBox(worker->theMovie, &srcRect);
    MacOffsetRect(&srcRect, -srcRect.left, -srcRect.top);

    // create a GWorld to render the frame into
    status = QTNewGWorld(&theGWorld,
                         k2vuyPixelFormat,
                         &srcRect,
                         NULL,
                         NULL,
                         0);
    require_noerr(status, Done);

    // set the GWorld
    SetGWorld(theGWorld, NULL);
    SetMovieGWorld(worker->theMovie, theGWorld, NULL);

    theGWorldPixMap = GetGWorldPixMap(theGWorld);
    LockPixels(theGWorldPixMap);
    baseAddr = GetPixBaseAddr(theGWorldPixMap);
    theGWorldRowBytes = QTGetPixMapHandleRowBytes(theGWorldPixMap);

    // get the image description and data size
    // NOTE: according to Ice Floe Dispatch 19 Image Descriptions
    // for QuickTime Movie files containing uncompressed Y'CbCr video data
    // should be version 2 and include a number of required Image Description
    // extensions. We don't add any of these for the sake of simplicity
    // http://developer.apple.com/quicktime/icefloe/dispatch019.html
    status = MakeImageDescriptionForPixMap(theGWorldPixMap, &id);
    theDataSize = (**id).dataSize;

    // create a new movie track and media
    theTrack = NewMovieTrack(theNewMovie,
                             FixRatio((srcRect.right - srcRect.left), 1),
                             FixRatio((srcRect.bottom - srcRect.top), 1),
                             kNoVolume);
    status = GetMoviesError();
    require_noerr(status, Done);

    theMedia = NewTrackMedia(theTrack, VideoMediaType,
                             kQTSDefaultMediaTimeScale, NULL, 0);
    status = GetMoviesError();
    require_noerr(status, Done);

    // begin the editing session so sample data will be written out to the file
    status = BeginMediaEdits(theMedia);
    require_noerr(status, Done);

    // for the first frame, include the frame we are currently on
    flags = nextTimeMediaSample | nextTimeEdgeOK;
    whichMediaType = VideoMediaType;

    Handle theSampleData = NewHandle(theDataSize);
    if (MemError() || NULL == theSampleData) goto Done;

    while (1) {
        // get the next frame of the source movie
        // skip to the next interesting time
        GetMovieNextInterestingTime(worker->theMovie,
                                    flags,
                                    1,
                                    &whichMediaType,
                                    movieTime,
                                    0,
                                    &movieTime,
                                    &duration);
        status = GetMoviesError();
        require_noerr(status, Done);

        if (-1 == movieTime) break;

        // set the time for the frame
        SetMovieTimeValue(worker->theMovie, movieTime);

        // draw the frame into the GWorld
        MoviesTask(worker->theMovie, 0);
        status = GetMoviesError();
        require_noerr(status, Done);

        // mess with the luma subtracting 25 from each Y
        // value clamping at 16 the min value (25+16 = 41)
        // CbYCrY 8-bits each component pixels 0-1
        // see Ice Floe Dispatch 19
        // http://developer.apple.com/quicktime/icefloe/dispatch019.html
        UInt32 height = srcRect.bottom;
        Ptr nextScanLine = baseAddr;
        while (height--) {
            UInt32 width = theGWorldRowBytes >> 1; 
            UInt8 *thePixPtr = (UInt8 *)nextScanLine;
            while (width--) {
                UInt8 Y = thePixPtr[1];
                Y = ((Y <= 41) * 16) | ((Y - 25) * !(Y <= 41));
                thePixPtr[1] = Y;
                thePixPtr += 2;
            }
            nextScanLine += theGWorldRowBytes;
        }

        // add the media sample to the movie
        PtrToXHand(baseAddr, theSampleData, theDataSize);
        status = AddMediaSample(theMedia, 
                                theSampleData, // the video sample
                                0,             // no offset into data
                                theDataSize, 
                                60,            // frame duration
                                (SampleDescriptionHandle)id, 
                                1,             // one sample
                                0,             // self-contained samples
                                NULL);
        require_noerr(status, Done);

        flags = nextTimeMediaSample;
    }

    // end the media editing session
    status = EndMediaEdits(theMedia);
    require_noerr(status, Done);

    // add the media to the track
    status = InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(theMedia),
                                  fixed1);
    require_noerr(status, Done);

    status = AddMovieToStorage(theNewMovie, theDataHandler);

Done:

    SetMovieGWorld(worker->theMovie, NULL, NULL);

    // something messed up so delete the file
    if (status && theDataHandler) {
        DataHDeleteFile(theDataHandler);
    }

    if (theDataRef) DisposeHandle(theDataRef);
    if (theDataHandler) CloseMovieStorage(theDataHandler);
    if (id) DisposeHandle((Handle)id);
    if (theSampleData) DisposeHandle(theSampleData);
    if (theNewMovie) DisposeMovie(theNewMovie);
    if (theGWorld) DisposeGWorld(theGWorld);

    DetachMovieFromCurrentThread(worker->theMovie);

CantAttachToCurrentThread:

    worker->threadStatus = status;

    ExitMoviesOnThread();

    CreateEvent(NULL, kEventClassQTThreading, kEventAppCleanUpThreadDroppings,
                0, kEventAttributeNone, &theEventRef);
    SetEventParameter(theEventRef, kEventParamThreadData, typeWorkerThreadRef,
                      sizeof(worker), &worker);

    if (theEventRef) {
        PostEventToQueue(worker->mainEventQueue, theEventRef,
                         kEventPriorityStandard);
        ReleaseEvent(theEventRef);
    }

    pthread_exit(NULL);
}

Перечисление 8  Do_SomeWorkOnSeparateThread.

// Worker thread struct we pass around
typedef struct {
    SInt32        refCount;
    pthread_t     workerThread;
    Movie         theMovie;
    EventQueueRef mainEventQueue;
    OSStatus      threadStatus;
} WorkerThread, *WorkerThreadRef;

// Our custom Carbon event type 
enum {
  kEventClassQTThreading = 'QTTH',
  kEventAppCleanUpThreadDroppings = 'clup',
  kEventParamThreadData = 'thrd',
  typeWorkerThreadRef = 'thrd'      // WorkerThreadRef
};


// code required to install our Carbon event handler
// this snippet could reside in a larger initialize function that installs
// a minimum set of Carbon event handlers, possibly before calling
// RunApplicationEventLoop()

...

EventTypeSpec eventType[] = {{kEventClassQTThreading,
                              kEventAppCleanUpThreadDroppings}};
status = InstallApplicationEventHandler(Handle_CleanUpThreadData,
                                        GetEventTypeCount(eventType),
                                        eventType, NULL, NULL);

...

// The Carbon event handler for our custom clean up event
static pascal OSStatus Handle_CleanUpThreadData(EventHandlerCallRef inHandlerCallRef,
                                                EventRef inEvent, void *inUserData)
{
    WorkerThreadRef worker = NULL;

    GetEventParameter(inEvent, kEventParamThreadData, typeWorkerThreadRef,
                      NULL, sizeof(worker), NULL, &worker);
    if (NULL == worker) return eventNotHandledErr;

    if (1 == DecrementAtomic(&worker->refCount)) {
        DisposeMovie(worker->theMovie);
        free(worker);
    }

    return noErr;
}

// check the component for the magic cmpThreadSafe flag
Boolean IsComponentThreadSafe(OSType inComponentType, OSType inComponentSubType)
{
    Component comp = 0;
    ComponentDescription cd = { inComponentType, inComponentSubType,
                                kAnyComponentManufacturer, 0, cmpIsMissing };
    ComponentDescription compDesc;

    comp = FindNextComponent(0, &cd);
    while (comp != NULL) {
        GetComponentInfo(comp, &compDesc, NULL, NULL, NULL);
        if (compDesc.componentFlags & cmpThreadSafe) return true;

        comp = FindNextComponent(comp, &cd);
    } 

    return false;
}

/*****************************************************
*
* Do_SomeWorkOnSeparateThread(WindowRef aWindow) 
*
* Purpose: Takes the Movie being played in the passed in Window
* and clones it, moves it to a background thread and then calls
* one of the DoSomeWorkFunctions above to perform the work
*
* Inputs: A Window Reference
*
* Returns: none
*/
static OSStatus Do_SomeWorkOnSeparateThread(WindowRef aWindow)
{
    WorkerThreadRef worker;
    pthread_attr_t attr;
    OSStatus status = paramErr;

    if (NULL == aWindow) return status;

    WindowDataPtr wdr = (WindowDataPtr)GetWRefCon(aWindowRef);
    if (NULL == wdr) return status;

    Handle cloneHandle = NewHandle(0);
    if (NULL == cloneHandle || status = MemError()) return status;

    // allocate memory for the worker thread data
    worker = calloc(1, sizeof(WorkerThread));
    if (NULL == worker) { status = memFullErr; goto Failure; }

    // we need the main event queue so we can call
    // PostEventToQueue from the background thread
    // but GetMainEventQueue isn't thread safe on
    // 10.3.x -- that's why we set this up here
    // However, GetMainEventQueue IS thread safe on
    // 10.4 and greater, so we have the flexibility
    // to do this later if we wished on newer systems
    worker->mainEventQueue = GetMainEventQueue();

    // clone the original movie this new movie will be attached
    // to the main thread so make sure to detach it
    status = PutMovieIntoHandle(wdr->fMovie, cloneHandle);
    require_noerr(status, Failure);

    status = NewMovieFromHandle(&worker->theMovie, cloneHandle, newMovieActive, NULL);
    require_noerr(status, Failure);

    status = DetachMovieFromCurrentThread(worker->theMovie);
    if (componentNotThreadSafeErr == status) {

        // darn! - we can't export this movie as is on a separate thread
        // we first need to remove tracks containing unsafe media

        long trackCount = GetMovieTrackCount(worker->theMovie);
        long count;

        // delete all tracks of unsafe types
        for (count = 1; count <= trackCount; count++) {
            Track track = GetMovieIndTrack(worker->theMovie, count);
            Media media = GetTrackMedia(track);
            SampleDescriptionHandle desc;
            OSType theMediaType;
            OSType theCodecType;

            if (track) {
                GetMediaHandlerDescription(media, &theMediaType, NULL, NULL);
                desc = (SampleDescriptionHandle)NewHandle(0);
                GetMediaSampleDescription(media, 1, desc);
                theCodecType = (**desc).dataFormat;
                DisposeHandle((Handle)desc);
                if (!IsComponentThreadSafe(MediaHandlerType, theMediaType) ||
                    !IsComponentThreadSafe(decompressorComponentType, theCodecType)) {
                    DisposeMovieTrack(track);
                    count--;
                }
            }
        }

        // get the track count again - if there's no tracks left
        // there's not much point in attempting an export is there
        trackCount = GetMovieTrackCount(worker->theMovie);
        if (trackCount == 0) goto Failure;

        // try again
        status = DetachMovieFromCurrentThread(worker->theMovie);
        require_noerr(status, Failure);
    }

    // create thread detached
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    IncrementAtomic(&worker->refCount);

    // create the worker thread and do some work
    status = pthread_create(&worker->workerThread, &attr, DoSomeWorkFunction, worker);
    pthread_attr_destroy(&attr);
    require_noerr(status, Failure);

    DisposeHandle(cloneHandle);

    return noErr;

Failure:
    if (cloneHandle) DisposeHandle(cloneHandle);
    if (worker->theMovie) DisposeMovie(worker->theMovie);
    if (worker) free(worker);
    return status;
}

Работа с платформой QTKit

Обзор

С Mac OS X 10.5 и QuickTime 7.3 или позже установленный, QTKit предоставляет поддержку уровня платформы, требующуюся, чтобы использовать объекты QTKit на фоновых потоках. Когда Вы хотите использовать a, это включает возможность сказать QTKit QTMovie экземпляр на фоновом потоке, когда Вы присоединяете или отсоединяете a QTMovie экземпляр от текущего потока и также позволяет управление QTMovie состояние ожидания.

Три QTMovie методы класса и четыре QTMovie методы экземпляра обеспечивают эту расширенную функциональность.

Методы класса QTMovie

QTMovie Класс имеет три метода класса поддерживать потокобезопасность. Эти методы позволяют клиенту уведомлять платформу, когда она будет использовать или сделана с помощью объектов QTKit на фоновых потоках. Обратите внимание на то, что эти методы функционально эквивалентны EnterMoviesOnThread и ExitMoviesOnThread C пара API.

Перечисление 9  enterQTKitOnThread.

+ (void)enterQTKitOnThread

Discussion:

Indicates that the client will be using QTKit on the current (non-main) thread and performs the required
QuickTime-specific initialization. Must be paired with a call to +(void)exitQTKitOnThread.

Перечисление 10  exitQTKitOnThread.

+ (void)exitQTKitOnThread

Discussion:

Indicates that the client will no longer be using QTKit on the current
(non-main) thread and performs any required QuickTime-specific cleanup. Must be paired with a call to
+(void)enterQTKitOnThread or +(void)enterQTKitOnThreadDisablingThreadSafetyProtection.

Перечисление 11  enterQTKitOnThreadDisablingThreadSafetyProtection.

+ (void)enterQTKitOnThreadDisablingThreadSafetyProtection

Discussion:

Indicates that the client will be using the QTKit on the current (non-main) thread allowing the use of
non-threadsafe components. This is equivalent to using the kCSAcceptAllComponentsMode flag with the
EnterMoviesOnThread C API. Must be paired with a call to +(void)exitQTKitOnThread.

Developers should not under normal circumstances need to disable thread-safe component protection.

Методы экземпляра QTMovie

QTMovie Класс имеет четыре метода экземпляра поддерживать потокобезопасность. Эти методы позволяют Вам присоединять и отсоединяться QTMovie экземпляры от в настоящее время рабочего потока, а также получают и устанавливают состояние ожидания a QTMovie экземпляр.

Перечисление 12  attachToCurrentThread.

- (void)attachToCurrentThread

Discussion:

Attaches a QTMovie instance to the current thread and if it is the main thread, adds the QTMovie instance to
the global idle list. Call -setIdling:NO to override this default.

Перечисление 13  detachFromCurrentThread.

- (void)detachFromCurrentThread

Discussion:

Detaches a QTMovie instance from the current thread and removes it from the global idle list. QTMovie
instances must never be idled when they are attached to background threads.

  Бездействие перечисления 14.

- (BOOL)idling  Returns YES if the QTMovie is being idled, NO if it is not being idled.  Discussion:  Returns the current idling state of a QTMovie instance.

Перечисление 15  setIdling.

- (void)setIdling:(BOOL)state

state - idle state, YES or NO.

Discussion:

This method allows you to manage the idle state of a QTMovie instance, that is, whether it's being tasked or
not.

A QTMovie instance migrated to background threads using the detachFromCurrentThread /
attachToCurrentThread methods will automatically be removed from the global idle list by the QTKit framework.

QTMovie instances attached back to the main thread will be added to the global idle list automatically
by the QTKit framework. Call -setIdling:NO to override this default behavior.

Note that QTMovies attached to background threads must never be idled.

Остерегайтесь базового фильма в формате QuickTime Контроллер

A QTMovie экземпляр всегда имеет - фильм в формате QuickTime Экземпляр контроллера, связанный с ним; это - фундаментальная часть QTMovie проект объектов. Платформа QTKit полагается на этот фильм в формате QuickTime Экземпляр контроллера для многих важных операций, таких как обработка mcActions, коммуникация межфильма, и т.д.

Однако Компоненты Контроллера фильма в формате QuickTime не ориентированы на многопотоковое исполнение и не могут быть созданы на фоновых потоках. Поэтому существует потаенная проблема потокобезопасности здесь, которая будет влиять QTMovie создание объекта, даже когда сопровождаются все рекомендуемые инструкции потокобезопасности для работы с QuickTime и QTKit.

Правильно создавая QTMovie для фоновой обработки

A QTMovie экземпляр объекта должен всегда создаваться и инициализироваться на основном потоке, тогда перемещенном на фоновый поток для последующей обработки при желании.

Другими словами, a QTMovie объект никогда не должен создаваться на фоновом потоке; базовый фильм в формате QuickTime Контроллер не инициализирует и много последующих операций на QTMovie экземпляр просто перестанет работать. В то время как возможно обойти это ограничение при помощи enterQTKitOnThreadDisablingThreadSafetyProtection, это не рекомендуемый или поддерживаемый подход.

Пример кода

Перечисление 16  , Настраивающее a QTMovie объект на основном потоке.

- (void)doExportWithFile:(NSString *)inFile
{
    QTMovie *movie = nil;

    NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:(id)inFile, QTMovieFileNameAttribute,
                                            [NSNumber numberWithBool:NO], QTMovieOpenAsyncOKAttribute, nil];

    movie = [QTMovie movieWithAttributes:attrs error:nil];

    [movie detachFromCurrentThread];

    [NSThread detachNewThreadSelector:@selector(doExportOnThread:)
                             toTarget:self 
                           withObject:movie];
}

Перечисление 17  , Работающее над фоновым потоком.

- (void)doExportOnThread:(QTMovie *)movie
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
                                            [NSNumber numberWithBool:YES], QTMovieExport,
                                            [NSNumber numberWithLong:'M4V '], QTMovieExportType, nil];

    [QTMovie enterQTKitOnThread];
    [movie attachToCurrentThread];

    // do export
    [movie writeToFile:@"/Users/Shared/iPodMovie.m4v" withAttributes:attrs];

    [movie detachFromCurrentThread];
    [QTMovie exitQTKitOnThread];

    [pool release];
}

Получение QTKit

Классы получения QTKit, представленные в Mac OS X 10.5 и QuickTime 7.3 обычно, имеют хорошие характеристики потокобезопасности. Чтобы быть более определенными, эти классы могут использоваться от фоновых потоков, за исключением QTCaptureView который наследовался от NSView и поэтому имеет несколько известных ограничений, как обсуждено в документации Потокобезопасности Какао.

Несмотря на то, что Кэптьюр Сешнз, представленный QTCaptureSession возразите и их Вводы (например, QTCaptureDeviceInput) и Выводы (например, QTCaptureMovieFileOutput) может создаваться, выполняться и контролироваться от фоновых потоков, любые вызовы метода, видоизменяющие эти объекты или доступ, непостоянная информация должна быть сериализирована. Поэтому эти методы требуются, чтобы выполнять свою собственную синхронизацию (или блокирующий) вокруг любого доступа к объекту. Это поведение не отличается от любой незащищенной структуры данных, к которой нужно получить доступ от многократных потоков.

Посмотрите, что Потокобезопасность Какао и Используя Привязывает Какао для большего количества подробных данных относительно того, что и что не считают ориентированным на многопотоковое исполнение при использовании Какао.

QuickTime для Windows

В то время как можно вызвать QuickTime от единственного фонового потока на Windows с относительной безопасностью, системная поддержка уровня, требуемая вызывать Уровень Носителей QuickTime (QTML) от многократных фоновых потоков, не существует на платформе. Например, критические платформы, такие как QuickDraw не ориентированы на многопотоковое исполнение на Windows.

Кроме того, QuickTime полностью сериализируется на Windows, только позволяющем единственный поток вызывать в Библиотеку QuickTime в любой момент. Если один поток выполнит операцию QuickTime, то любой другой поток, делающий вызов API QuickTime, блокирует ожидание предыдущей работы для возврата.

Поэтому вызов QuickTime от многократных фоновых потоков на Windows не рекомендуется.

Уведомление для разработчиков компонентов

Очень модульная конструкция QuickTime позволяет ему быть расширенным посредством добавления новых компонентов, предоставляющих новые и или определенные услуги.

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

Используйте ресурс Менеджера компонентов с одним выстрелом APIs

Как обсуждено, Менеджер ресурсов не ориентирован на многопотоковое исполнение, но вызовы Менеджера компонентов с одним выстрелом, возвращающие ресурсы компонента. Когда реализация вызовов компонента, возвращая информацию сохранила в ресурсах компонента такой как ImageCodecGetCodecInfo, GraphicsImportGetMIMETypeList и т.д. удостоверьтесь, что использовали ресурс Менеджера компонентов с одним выстрелом APIs как требуется. См. списки 9 - 11.

Перечисление 18  WRONG - не использует Менеджер ресурсов.

// The example below shows some typical old code a component could use to
// obtain one of its resources. This is a relatively large amount of code and is
// not thread-safe. If your component contains older code such as this
// it's time to update the code.

OSErr  err;
Handle resource = NULL;
short  saveResFile;
short  resRef;

saveResFile = CurResFile();
err = OpenAComponentResFile((Component)store->self, &resRef);
if (err == noErr) {
  resource = Get1Resource('PICT', 128);
  if (resource) {
    LoadResource(resource);
    DetachResource(resource);
  } else {
    err = ResError();
    if (err == noErr)
      err = resNotFound;
  }

  CloseComponentResFile(resRef);
  UseResFile(saveResFile);
}

Перечисление 19  CORRECT - использование GetComponentResource.

// Use the Component Manager one-shot thread-safe functions.
// This one line performs the exact same function as the above listing.

OSErr  err;
Handle resource = NULL;

err = GetComponentResource((Component)store->self, 'PICT', 128, &resource);

Перечисление 20  CORRECT - реализация ImageCodecGetInfo.

// Example 'cdci' resource
#define kMyCodecFormatName  "My Cool Codec"

// These flags specify information about the capabilities of the component
#define kMyDecoFlags (codecInfoDoes32 | codecInfoDoes8)

// These flags specify the possible format of compressed data produced by the component
// and the format of compressed files that the component can handle during decompression
#define kMyFormatFlags (codecInfoDepth32 | codecInfoDepth40)

// Component Description
resource 'cdci' (129) {
    kMyCodecFormatName, // Type
    1,                  // Version
    0,                  // Revision level
    'MINE',             // Manufacturer
    kMyDecoFlags,       // Decompression Flags
    0,                  // Compression Flags
    kMyFormatFlags,     // Format Flags
    0,                  // Compression Accuracy
    128,                // Decomression Accuracy
    0,                  // Compression Speed
    200,                // Decompression Speed
    0,                  // Compression Level
    0,                  // Reserved
    8,                  // Minimum Height
    8,                  // Minimum Width
    0,                  // Decompression Pipeline Latency
    0,                  // Compression Pipeline Latency
    0                   // Private Data
};

// ImageCodecGetCodecInfo
// Your codec receives the ImageCodecGetCodecInfo request whenever an application
// calls the Image Compression Manager's GetCodecInfo function.
// Your component should return a formatted CodecInfo structure defining its
// capabilities. 
// Both compressors and decompressors may receive this call.
pascal ComponentResult MyDeco_GetCodecInfo(MyCodecGlobals glob, CodecInfo *info)
{
    CodecInfo **tempCodecInfo;
    OSErr err = noErr;

    if (NULL == info) return paramErr;

    err = GetComponentResource((Component)glob->self, codecInfoResourceType,
                               129, (Handle *)&tempCodecInfo);
    if (noErr == err) {
        *info = **tempCodecInfo;
        DisposeHandle((Handle)tempCodecInfo);
    }

    return err;
}

Для получения дополнительной информации об этом APIs посмотрите Ледяную Отгрузку Плавучей льдины 21

Не используйте Компонент RefCon для хранения глобального состояния

Некоторые компоненты поддерживают глобальное состояние, совместно использующееся Экземплярами Компонента, и используйте Компонент RefCon с этой целью. Это больше не собирается работать, когда Ваш компонент вызывают от многократных потоков, и поэтому Вы не должны использовать этот RefCon для хранения глобального состояния.

Если Ваш компонент использует SetComponentRefCon и GetComponentRefCon, необходимо будет рассмотреть это использование. Должны быть защищены совместно используемые глобальные переменные, используемые для коммуникации между компонентами, и совместно используемые глобальные переменные для постоянных таблиц должны быть заменены глобальными данными константы, как описано в следующем разделе этого документа.

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

Перечисление 21  функция pthread_once.

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))

pthread_once performs one time initialization by ensuring that any initialization code
is only ever executed once.

once_control - a pointer to a static variable initialized to PTHREAD_ONCE_INIT.

init_routine - a C initialization function corresponding to the following prototype,
void initRoutine(void);

Discussion:

When pthread_once is first called with a given once_control argument, the function calls
the init_routine then sets the value of once_control to record that initialization has
been successful. Calls to pthread_once with the same once_control argument after
successful initialization do nothing.

Перечисление 22  Используя pthread_once для инициализации.

#include <pthread.h>

typedef struct  {
    Handle  someGlobalHandle;
    long    someDynamicValues[ARRAY_SIZE];
    ...
} SharedGlobals;

typedef struct {
    ComponentInstance   self;
    ComponentInstance   delegateComponent;
    ComponentInstance   target;
    OSType              codecType;
    SharedGlobals       *sharedGlob;
    ...
} ComponentGlobals, *ComponentGlobalsPtr;

static SharedGlobals componentSharedGlobals;

static void InitSharedGlobals(void)
{
    componentSharedGlobals.someGlobalHandle = NewHandle(HANDLE_SIZE * sizeof(long));

    InitSomeGlobalHandle(&componentSharedGlobals);
    InitSomeDynamicValues(&componentSharedGlobals);
}

static SharedGlobals* GetSharedGlobals(void)
{
    static pthread_once_t control = PTHREAD_ONCE_INIT;

    /* pthread_once executes a routine exactly once in a process.
       The first call to this subroutine by any thread in the process
       executes the given routine, without parameters. Any subsequent
       call will have no effect.

       This is useful when a unique initialization has to be done by
       one thread and reduces synchronization requirements. */

    pthread_once(&control, InitSharedGlobals);

    return &componentSharedGlobals;
}

pascal ComponentResult MyCodec_ImageCodecOpen(ComponentGlobalsPtr glob,
                                              ComponentInstance self)
{
    ComponentResult result = noErr;
    ComponentDescription cd;

    result = GetComponentInfo((Component)self, &cd, NULL, NULL, NULL);
    if (result) return result;

    if ((glob = (ComponentGlobalsPtr)calloc(1, sizeof(ComponentGlobals))) == NULL)
        return memFullErr;

    SetComponentInstanceStorage(self,(Handle)glob);

    glob->self = self;
    glob->codecType = cd.componentSubType;
    ...
    glob->sharedGlob = GetSharedGlobals();

    ...

    return result;
}

Рассмотрите помещающие постоянные таблицы в свою исполнимую программу

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

Это - ценный метод и имеет много преимуществ:

  • Если Ваши табличные данные будут постоянными, и компилятор знает, что это, то данные будут помещены в раздел только для чтения исполнимой программы компонента. Это также известно как Сегмент текста.

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

  • Табличные данные будут отмечены только для чтения. Если память должна быть разбита на страницы, система знает, что это может возвратить табличные данные в любое время путем отображения его назад от файла только для чтения. Это плюс то, потому что табличные данные никогда не будут расточительно записываться обратно к диску.

Перечисление 23  Предварительно созданные таблицы, отмеченные как константа.

static const gReverseBitsTable[256] = {
    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
    [..]
};

Running nm -m from the teminal displays how these tables have indeed
been placed in the TEXT segment.

% nm -m whatever.o
00000000 (__TEXT,__const) non-external _gReverseBitsTable

If you didn't have the "const" keyword in the table definition, the 
table would be placed in the the DATA segment and each process will get
its own writable copy.

00000000 (__DATA,__data) non-external _gNonConstReverseBitsTable

Установите ориентированный на многопотоковое исполнение флаг Компонента

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

  Компонент перечисления 24 ориентированный на многопотоковое исполнение флаг.

cmpThreadSafe  = 1L << 28 // Component is thread-safe

Перечисление 25  , Добавляющее флаг cmpThreadSafe.

// extended 'thng' template
#define thng_RezTemplateVersion 1

#include <Carbon/Carbon.r>
#include <QuickTime/QuickTime.r>

resource 'thng' (256) {
    kSomeQTComponentType, // Type
    'DEMO',               // SubType
    'DEMO',               // Manufacturer
    0, 
    0,
    0,           // - use componentHasMultiplePlatforms
    0,
    'STR ',      // Name Type
    128,         // Name ID
    'STR ',      // Info Type
    129,         // Info ID
    0,           // Icon Type
    0,           // Icon ID
    kMyComponentVersion,  // Version
    // Registration Flags
    componentHasMultiplePlatforms |
    kOtherComponentRegistrationFlags,
    0,           // Resource ID of Icon Family
    {
      // Add Thread Safe Flag
      kMyComponentFlags | cmpThreadSafe,
      'dlle',    // Code Resource type - Entry point found by
                 // symbol name 'dlle' resource
      512,       // ID of 'dlle' resource
      platformPowerPCNativeEntryPoint,
                 // Platform Type (response from
                 // gestaltComponentPlatform or failing
                 // that, gestaltSysArchitecture)
    };
};

Для получения дополнительной информации относительно 'thng' ресурс и создание Мужественных Компонентов видят Техническое примечание TN2012, 'Создавая Universal Компоненты QuickTime для Mac OS X'.

QuickTime Ориентированные на многопотоковое исполнение компоненты

Следующие компоненты QuickTime ориентированы на многопотоковое исполнение на Mac OS X 10.3 и позже с QuickTime 6.5.2 установленных. Более новые версии QuickTime могут предложить более ориентированные на многопотоковое исполнение компоненты. Fiendishthngs (см. раздел Tools этого документа) может использоваться для определения определенных возможностей компонента с более новыми версиями QuickTime.

Более новые версии QuickTime используют платформу Core Audio, снимающую многие из этих ограничений.

Таблица 1  

Кодеки 'imco' compressorComponentType 'imdc' decompressorComponentType изображения

Декомпрессоры 'imdc' decompressorComponentType изображения

Apple Кодек DV/DVCPRO - NTSC

Основа Apple (иначе основной кодек)

Apple DV - Кодек PAL

Apple CMYK

Apple DVCPRO - Кодек PAL

Apple GIF

Apple DVCPRO50 - кодек NTSC

Apple AVR JPEG

Apple DVCPRO50 - кодек PAL

Apple OpenDML JPEG

Apple планарный кодек RGB

Apple r408

Apple кодек PNG

Масштабирование Apple

Apple ни один кодек (иначе необработанный кодек)

Apple Соренсон YUV9

Кодек анимации Apple

Apple YUV420

Видеокодек Apple

Apple YUV422

Apple кодек TIFF

Apple 16-разрядный серый

Кодек Apple Cinepak

Apple 32-разрядный серый с альфой

Фотография Apple - кодек JPEG

Apple 48-разрядный RGB

Движение Apple JPEG кодек

Apple 64-разрядный ARGB

Движение Apple JPEG B кодек

Кодек JPEG 2000 года

Кодек Apple MPEG4

Таблица 2  

Средства импорта неподвижного изображения и средства экспорта 'захватывают' GraphicsImporterComponentType 'grex' GraphicsExporterComponentType

Средства импорта неподвижного изображения 'захватывают' GraphicsImporterComponentType

JPEG

GIF

Photoshop

JPEG 2000

PNG

Изображение QuickTime

РАЗМОЛВКА

Основной Импорт / экспорт

Таблица 3  

Средства импорта фильма и средства экспорта 'едят', MovieImportType 'плюются' MovieExportType

Средства импорта фильма 'едят' MovieImportType

Средства экспорта фильма 'плюются' MovieExportType

DV

Анимированный GIF

3G

Фильм в формате QuickTime

Медленный фильм

BMP

MPEG 4

Аудио компакт-диска (AIFF)

Последовательность изображений

AVI

Фильм, которому подсказывают,

AIFF

WAVE

muLaw

Таблица 4  

Обработчики 'dhlr' DataHandlerType данных

Обработчики 'mhlr' MediaHandlerType носителей

Обработчик данных дескриптора Apple

Apple универсальный обработчик носителей

Apple обработчик данных NULL

Обработчик Apple MPEG4 ODSM носителей

Обработчик данных указателя Apple

Обработчик Apple MPEG 4 носителей сцены

Обработчик данных ресурсов Apple

Обработчик носителей стандарта Apple

Обработчик данных псевдонима Apple

Обработчик носителей временного кода

Обработчик носителей видео Apple

Таблица 5  

Стандартное сжатие 'scdi' StandardCompressionType

Кодеки 'dcom' DataCompressorComponentType 'ddec' DataDecompressorComponentType данных

Стандартное изображение

Данные Apple

Стандартный звук

Выкачайте / Расширяются (zlib)

Таблица 6  

Звуковые декомпрессоры 'sdec' kSoundDecompressor

Компоненты 'clok' clockComponentType часов

Декомпрессор звука DV

Часы микросекунды Apple

Ссылки

Технический список книг

Mac OS X

PThreads и многопоточность

Инструменты

Fiendishthngs (Mac OS X Универсальный Двоичный файл) - Эта утилита перечислит все компоненты, которые это находит в системе, и позвольте Вам, запрашивают большинство типов компонента QuickTime для получения дальнейшей информации.



История версии документа


ДатаПримечания
14.09.2007

Обновленный для QuickTime 7.3 и Mac OS X 10.5. Добавленная информация QTKit. Редакционные исправления.

08.05.2007

Передовая статья

14.02.2006

добавленный QuickTime для раздела Windows

05.05.2005

передовая статья

08.10.2004

Новый документ, обсуждающий, как использовать QuickTime от фоновых потоков для приложений и разработчиков компонентов.