Ориентированное на многопотоковое исполнение программирование в 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, требуемый поддерживать ориентированные на многопотоковое исполнение операции.
Введение
Наследие 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 - непрозрачная структура данных QuickTime в памяти, ссылающейся на основанные на времени носители.
Файл ролика - файл, содержащий некоторые демонстрационные данные носителей, находящиеся на диске. Файл ролика может или может не быть файлом фильма в формате QuickTime (.mov). Например, файл ролика мог быть импортированным файлом (.mp4) MPEG4 и просматриваемый как фильм в формате QuickTime.
Объект QuickTime - в контексте этого технического примечания этот срок используется в качестве общей ссылки к любому из типов данных QuickTime. Объект QuickTime мог быть некоторой непрозрачной структурой данных как фильм в формате QuickTime, Экземпляр Компонента, последовательность Распаковки и т.д.
Непересекающийся - Имеющий элементы вместе.
Что может быть сделано ориентированным на многопотоковое исполнение
Заявки QuickTime, работающие со следующими менеджерами по панели инструментов, могут быть поданы ориентированные на многопотоковое исполнение:
Панель инструментов фильма (импорт, рендеринг, создание, экспорт)
Программа управления сжатием изображений
QuickDraw (внеэкранный GWorlds)
Менеджер компонентов
Менеджер по псевдониму
Диспетчер памяти
Независимые потоки в Вашем приложении могут безопасно использовать логически непересекающиеся наборы объектов QuickTime от многократных потоков. Если один поток имеет свой собственный уникальный набор объектов QuickTime, и другой поток имеет свой собственный уникальный набор, то любую работу, выполняемую на этих объектах, можно считать ориентированной на многопотоковое исполнение. Два потока не должны иметь никаких объектов QuickTime вместе. Посмотрите рисунок 1.
Например, два фоновых потока могли импортировать.DV поток, тогда продвигаются через каждый Фильм с помощью APIs такой как GetNextInterestingTime, SetMovieTime и рендеринг структурирует путем вызова MoviesTask.

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

Взаимные исключения являются простыми блокировками и могут использоваться для управления доступом к некоторому объекту QuickTime как Графическая Последовательность Экземпляра или Распаковки Средства импорта, совместно использованная двумя фоновыми потоками.
Взаимные исключения имеют только два состояния, заблокированные и разблокированные. Условная переменная POSIX может использоваться со взаимными исключениями, чтобы позволить потокам блокировать и ожидать сигнала от другого потока. Когда сигнал поступает, поток, это блокируется, может тогда попытаться захватить блокировку на связанном взаимном исключении.
Multiprocessing Services также обеспечивает синхронизацию потока и сигнальные механизмы.
Посмотрите раздел References в конце этого Технического примечания для получения дополнительной информации относительно pthreads и Multiprocessing Services.
Исключение происходит при работе с файловой системой. Подпрограммы операционной системы, реализующие доступ к файлам, все ориентированы на многопотоковое исполнение. Поскольку файловая система разработана для многопоточного доступа, независимые потоки могут иметь два объекта QuickTime, относящиеся к тому же файлу. Это прекрасно подходит для имения двух долей потоков тот же файл ролика для чтения. Два потока могут иметь свои собственные отдельные фильмы в формате QuickTime, ссылающиеся на тот же файл ролика при выполнении операций чтения. Например, эти потоки могли представить или вывести на экран различные части Фильма одновременно без любых проблем. Однако может не быть безопасно для одного из этих потоков записать в файл ролика, в то время как другой поток читает из него без некоторой блокировки. Посмотрите рисунок 3.

Ключевой пункт в этом обсуждении - то, что Ваше приложение не должно позволять больше чем одному потоку работать над тем же объектом QuickTime в то же время, если Вы хотите гарантировать потокобезопасность. Это - Ваша ответственность в качестве вызывающей стороны для обеспечения этого дело обстоит так.
Что не ориентировано на многопотоковое исполнение
Следующие менеджеры по Панели инструментов не ориентированы на многопотоковое исполнение; приложения QuickTime не могут использовать свои службы от фоновых потоков:
HIToolbox (пользовательский интерфейс)
Менеджер ресурсов
Кроме того, следующая функциональность Панели инструментов Фильма не доступна от фоновых потоков:
Воспроизведение фильма
Контроллер фильма
Панель инструментов Интерфейса пользователя обеспечивает элементы пользовательского интерфейса для приложений. Работа с HIToolbox, однако, не ориентирована на многопотоковое исполнение. Кроме того, это - основные потоки выполненный цикл, обычно получающий все события, сгенерированные пользователем.
Не имеет значения, если Вы работаете с QuickTime в среде Углерода или Какао, элементы пользовательского интерфейса должны остаться на основном потоке, и это - Ваша ответственность в качестве вызывающей стороны, чтобы избежать создавать или представлять Диалоговое окно или Окно на фоновом потоке. Элементы пользовательского интерфейса QuickTime включают Стандартное Диалоговое окно Сжатия, Диалоговые окна, представленные APIs такой как MovieExportDoUserDialog, Процедура Прогресса Экспорта Фильма по умолчанию и т.д. Посмотрите рисунок 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.

Получение ссылки Фильма, использующей любой из 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 ориентировано на многопотоковое исполнение и может быть вызван от фонового потока, который может отправлять событие.

Когда Вы не хотите использования функции прогресса Фильма NULL:
SetMovieProgressProc(theMovie, NULL, NULL).
Контакт с менеджером ресурсов
Менеджер ресурсов не ориентирован на многопотоковое исполнение. Критически важно помнить, что это включает любые ресурсы управления APIs, цепочку ресурса или карты ресурса.
Однако вызовы Менеджера компонентов с одним выстрелом, возвращающие ресурсы компонента, ориентированы на многопотоковое исполнение и должны использоваться, если Ваше приложение должно получить общедоступный ресурс из Компонента.
Они включают следующий APIs:
GetComponentResourceGetComponentIndStringGetComponentPublicResourceGetComponentPublicIndStringGetComponentPublicResourceList
Перечисление 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, снимающую многие из этих ограничений.
Кодеки '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 |
|
Средства импорта неподвижного изображения и средства экспорта 'захватывают' GraphicsImporterComponentType 'grex' GraphicsExporterComponentType | Средства импорта неподвижного изображения 'захватывают' GraphicsImporterComponentType |
|---|---|
JPEG | GIF |
Photoshop | JPEG 2000 |
PNG |
|
Изображение QuickTime |
|
РАЗМОЛВКА |
|
Основной Импорт / экспорт |
|
Средства импорта фильма и средства экспорта 'едят', MovieImportType 'плюются' MovieExportType | Средства импорта фильма 'едят' MovieImportType | Средства экспорта фильма 'плюются' MovieExportType |
|---|---|---|
DV | Анимированный GIF | 3G |
Фильм в формате QuickTime | Медленный фильм | BMP |
MPEG 4 | Аудио компакт-диска (AIFF) | Последовательность изображений |
AVI |
| Фильм, которому подсказывают, |
AIFF |
|
|
WAVE |
|
|
muLaw |
|
|
Обработчики 'dhlr' DataHandlerType данных | Обработчики 'mhlr' MediaHandlerType носителей |
|---|---|
Обработчик данных дескриптора Apple | Apple универсальный обработчик носителей |
Apple обработчик данных NULL | Обработчик Apple MPEG4 ODSM носителей |
Обработчик данных указателя Apple | Обработчик Apple MPEG 4 носителей сцены |
Обработчик данных ресурсов Apple | Обработчик носителей стандарта Apple |
Обработчик данных псевдонима Apple | Обработчик носителей временного кода |
| Обработчик носителей видео Apple |
Стандартное сжатие 'scdi' StandardCompressionType | Кодеки 'dcom' DataCompressorComponentType 'ddec' DataDecompressorComponentType данных |
|---|---|
Стандартное изображение | Данные Apple |
Стандартный звук | Выкачайте / Расширяются (zlib) |
Звуковые декомпрессоры '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 от фоновых потоков для приложений и разработчиков компонентов. |