Вот стандартный диалог настроек экспорта компонента Quicktime, который мы собираемся использовать:
Диалог доступен только в среде Quicktime (не путать с QTKit), отсюда и ограничения:
- Только 32-битная сборка
- Нет интеграции с iOS.
Первое, что вам нужно сделать, это связать ваше приложение с платформой Quiktime, убедитесь, что вы собрали его в 32-битной архитектуре Intel, и включена опция Build Active Architecture Only.
Цель - открыть диалоговое окно, установить необходимые параметры экспорта и использовать их для экспорта фильма.
Я предоставлю фрагменты кода в обратном порядке, начиная с фактического вызова диалога, возвращаясь к использованию требуемых структур. Мы должны использовать много кода, поэтому нужно немного терпения.
Для диалога требуется компонент Quicktime. На нашем примере мы будем использовать компонент MPEG4. Предоставляем компонент, ранее сохраненные настройки и открываем диалоговое окно:
#import <QuickTime/QuickTimeComponents.h>
- (void)editExportSetting:(JSMovieExportSetting*)setting
{
MovieExportComponent exporter = OpenComponent(_MPEG4Component.component);
Boolean canceled;
[self updateMovieExportComponent:exporter withExportSetting:setting];
ComponentResult err = MovieExportDoUserDialog(exporter, NULL, NULL, 0, 0, &canceled);
if (!canceled)
{
if (err == 0)
{
[self updateExportSetting:setting withMovieExportComponent:exporter];
}
}
CloseComponent(exporter);
}
Когда пользователь закончит редактирование и нажмет кнопку ОК, мы сохраняем настройки для последующего использования.
Для функции OpenComponent
требуется тип данных Component
. Для этого я создал оболочку вспомогательного класса, см. Листинг ниже.
Теперь нам нужно получить компонент MPEG4:
- (JSQTComponent*)MPEG4Component
{
/*
MPEG-4
*/
ComponentDescription description;
description.componentType = 1936746868;
description.componentSubType = 1836082996;
description.componentManufacturer = 1634758764;
description.componentFlags = 269058096;
description.componentFlagsMask = 66207;
return [self componentWithDescription:description];
}
- (JSQTComponent*)componentWithDescription:(ComponentDescription)description
{
JSQTComponent* result = nil;
for (JSQTComponent* component in [self components])
{
if ([component isEqualToComponentDescription:description])
{
result = component;
break;
}
}
return result;
}
- (NSArray*)components
{
if (_components == nil)
{
_components = [NSMutableArray new];
QTAtomContainer resources = NULL;
OSErr err = GetComponentPublicResourceList(kQTPresetsListResourceType, 1, 0, &_componentDescription, nil, nil, &resources);
if (err != noErr)
{
NSLog(@"JSQTComponentDataModel error: %d", err);
}
else if (resources != NULL)
{
QTAtom currChild = 0;
QTAtom nextChild = 0;
OSErr err;
unsigned atomsCount = QTCountChildrenOfType(resources, kParentAtomIsContainer, 0);
for (UInt16 i=0; i < atomsCount; i++)
{
QTAtom compAtomId = 0;
if (QTFindChildByIndex(resources, kParentAtomIsContainer, kQTMetaDataCommonKeyComposer, i+1, &compAtomId))
{
Component component = (Component)compAtomId;
err = QTNextChildAnyType(resources, kParentAtomIsContainer, currChild, &nextChild);
if (err == noErr && nextChild)
{
[_components addObject:[[[JSQTComponent alloc] initWithComponent:component] autorelease]];
}
else
{
NSLog(@"Error %d getting item %d\n", err, i);
}
currChild = nextChild;
}
}
QTDisposeAtomContainer(resources);
}
}
return _components;
}
В основном мы ищем компонент с описанием, которое нам нужно в списке. Список был составлен так, чтобы получать компоненты определенного типа, потому что у Quicktime было множество разных когда-то.
Нас интересуют только те, которые когда-то были созданы для экспорта фильмов.
ComponentDescription _componentDescription;
_componentDescription.componentType = MovieExportType;
_componentDescription.componentSubType = kAnyComponentSubType;
_componentDescription.componentManufacturer = kAnyComponentManufacturer;
_componentDescription.componentFlags = canMovieExportFiles;
_componentDescription.componentFlagsMask = canMovieExportFiles;
Список кешируется для дальнейшего использования, не забудьте опубликовать его в вашем dealloc.
Пока все идет хорошо. Мы получили компонентный объект MPEG4, получили структуру Component
. Теперь нам нужно применить настройки, которые мы сохранили в прошлый раз, к компоненту.
- (void)updateMovieExportComponent:(MovieExportComponent)component withExportSetting:(JSMovieExportSetting*)movieExportSetting
{
NSData* presetSettings = movieExportSetting.exportSettings;
Handle settings = NewHandleClear([presetSettings length]);
if (settings)
{
memcpy(*settings, [presetSettings bytes], GetHandleSize(settings));
// Set movie export settings from the settings QTAtomContainer
MovieExportSetSettingsFromAtomContainer(component, (QTAtomContainer)settings);
DisposeHandle(settings);
}
}
Если вы используете компонент в первый раз без каких-либо настроек, диалоговое окно покажет значение по умолчанию один раз.
После того, как пользователь закончил редактирование и нажал OK, нам нужно сохранить настройки:
- (void)updateExportSetting:(JSMovieExportSetting*)movieExportSetting withMovieExportComponent:(MovieExportComponent)component
{
QTAtomContainer settings;
ComponentResult err = MovieExportGetSettingsAsAtomContainer(component, &settings);
if (err == 0)
{
NSData* exportSettings = [NSData dataWithBytes:*settings length:GetHandleSize(settings)];
movieExportSetting.exportSettings = exportSettings;
}
}
JSMovieExportSetting
- это оболочка, см. Список ниже. Вы можете легко преобразовать его в файл для сохранения настроек.
Последний шаг - использовать настройки, которые мы только что получили, для конвертации фильмов.
Хорошая новость заключается в том, что нам больше не нужно использовать Quicktime, мы можем использовать методы QTKit.
QTMovie* movie = [QTMovie movieWithFile:sourceFilePath error:outError];
NSDictionary* settings = [movieExportSetting movieAttributes];
[movie writeToFile:outputFilePath withAttributes:settings error:outError]
Установите делегат фильма и внедрите movie: shouldContinueOperation: withPhase: atPercent: withAttributes:
, чтобы увидеть прогресс при необходимости.
Ниже вы можете найти списки используемых классов. Надеюсь, это поможет.
JSQTComponent
@interface JSQTComponent : NSObject
{
Component _component;
ComponentDescription _componentDescription;
NSString* _name;
NSString* _info;
NSString* _icon;
}
- (id)initWithComponent:(Component)component;
@property (nonatomic, readonly) Component component;
@property (nonatomic, readonly) ComponentDescription componentDescription;
@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSString* info;
@property (nonatomic, retain) NSString* icon;
- (BOOL)isEqualToComponentDescription:(ComponentDescription)anotherComponentDescription;
@end
@ реализация JSQTComponent
@ synthesize component = _component;
@synthesize componentDescription = _componentDescription;
@synthesize name = _name;
@synthesize info = _info;
@synthesize icon = _icon;
(идентификатор) initWithComponent: (Компонент) Компонент
{
self = [super init];
if (self! = nil)
{
_component = компонент;
[self getDescription];
}
вернуть себя;
}
(аннулируются) dealloc
{
[_name release];
[_информационный выпуск];
[_icon release];
[супер сделка];
}
(аннулируются) getDescription
{
OSErr err;
Handle aName = NewHandleClear (255);
Handle anInfo = NewHandleClear (255);
Handle anIcon = NewHandleClear (255);
Обрабатывать iconSuite = NULL;
err = GetComponentInfo (_component, & _componentDescription, aName, anInfo, anIcon);
если (err == 0)
{
self.name = [NSString stringWithPStringHandle: aName];
self.info = [NSString stringWithPStringHandle: anInfo];
self.icon = [NSString stringWithPStringHandle: anIcon];
// err = GetComponentIconSuite (aComponent, & iconSuite);
}
DisposeHandle (aName);
DisposeHandle (anInfo);
DisposeHandle (anIcon);
DisposeHandle (iconSuite);
}
(BOOL) isEqualToComponentDescription: (ComponentDescription) anotherComponentDescription
{
return (_componentDescription.componentType == anotherComponentDescription.componentType) &&(_componentDescription.componentSubType == anotherComponentDescription.componentSubType) &&
(_componentDescription.componentManufacturer == anotherComponentDescription.componentManufacturer);
}
@ конец
JSMovieExportSetting
@interface JSMovieExportSetting : NSObject <NSCoding>
{
NSString* _name;
NSNumber* _componentSubType;
NSNumber* _componentManufacturer;
NSData* _exportSettings;
NSDictionary* _movieAttributes;
}
- (id)initWithName:(NSString*)name attributes:(NSDictionary*)attributes;
@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSNumber* componentSubType;
@property (nonatomic, retain) NSNumber* componentManufacturer;
@property (nonatomic, retain) NSData* exportSettings;
@property (nonatomic, readonly) NSDictionary* movieAttributes;
@end
@implementation JSMovieExportSetting
...
- (NSDictionary*)movieAttributes
{
if (_movieAttributes == nil)
_movieAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], QTMovieExport,
_componentSubType, QTMovieExportType,
_componentManufacturer, QTMovieExportManufacturer,
_exportSettings, QTMovieExportSettings, nil] retain];
return _movieAttributes;
}
- (void)setExportSettings:(NSData*)exportSettings
{
if (_exportSettings != exportSettings)
{
[_exportSettings release];
_exportSettings = [exportSettings retain];
[_movieAttributes release];
_movieAttributes = nil;
}
}
...
@end