English 中文(简体)
Display MPEG-4 Export Component Dialog
原标题:

Below is my code for a doc based QTKit app that exports to the fixed Apple device formats by way of a popUp that displays the three device options. The [NSNumberWithLong:mpg4 ] option works fine as well, but it lacks custom output settings. I want to integrate the familiar QT MPEG-4 export component dialog so that the user can customize the mpeg-4 output options.

Can someone explain how to do this?

thanks.

-paul.

- (void)exportPanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
int which = [mExportTypePopUpButton indexOfSelectedItem];
int                 selectedItem;
NSMutableDictionary *settings = nil;
static NSArray      *exportTypes = nil;

// init
if (exportTypes == nil)
{
    exportTypes = [[NSArray arrayWithObjects:

                    //[NSNumber numberWithLong: mpg4 ],   ///MPEG-4

                    [NSNumber numberWithLong: M4VH ],   ///Apple Tv
                    [NSNumber numberWithLong: M4VP ],   ///iPhone
                    [NSNumber numberWithLong: M4V  ],   ///iPod

                    nil] retain];
}





if (returnCode == NSOKButton)
{
    // init
    selectedItem = [mExportTypePopUpButton indexOfSelectedItem];
    settings = [NSMutableDictionary dictionaryWithCapacity:2];
    [settings setObject:[NSNumber numberWithBool:YES] forKey:QTMovieExport];



    if ((selectedItem >= 0) && (selectedItem < [exportTypes count]))
        [settings setObject:[exportTypes objectAtIndex:selectedItem] forKey:QTMovieExportType];

    if (which==0)

        [mReceiver setStringValue:@"Apple Tv"];


    if (which==1)

        [mReceiver setStringValue:@"iPhone"];


    if (which==2)

        [mReceiver setStringValue:@"iPod"];





    /
    if (![mMovie writeToFile:[sheet filename] withAttributes:settings])
        NSRunAlertPanel(@"Cancel", @"Export terminated.", nil, nil, nil);



}

}

问题回答

Here is the standard Quicktime Component export settings dialog we are going to use:

enter image description here

The dialog is only available in Quicktime framework (not to be confused with QTKit), hence the limitations:

  1. 32-bit build only
  2. No iOS integration whatsoever.

First thing you need to do is to link your application with Quiktime framework, make sure you build it in 32-bit Intel architecture and Build Active Architecture Only setting is turned on.

The goal is to open the dialog, set the necessary export settings and use them to export a movie.

I will provide the code pieces in reverse order starting from the actual dialog call going back to the usage of required structures. We have got to use a lot of code, so some patience is necessary.

The dialog requires a Quicktime component. On our example we will use MPEG4 component. We provide the component, the previously stored settings and open the dialog:

#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);   
}

When user finished with editing and clicks OK button we store the settings for later usage. The OpenComponent function requires Component data type. I have built a convenience class wrapper for it, see the listing below.

Now we need to get the MPEG4 component:

- (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
", err, i);
                    }
                    currChild = nextChild;
                }
            }
            QTDisposeAtomContainer(resources);
        }
    }
    return _components;
}

Basically we look for a component with the description we need in the list. The list was build to get components of a certain type, because Quicktime has tons of different once.

We are interested only in those once created for movie export.

ComponentDescription _componentDescription;

_componentDescription.componentType = MovieExportType;
_componentDescription.componentSubType = kAnyComponentSubType;
_componentDescription.componentManufacturer = kAnyComponentManufacturer;
_componentDescription.componentFlags = canMovieExportFiles;
_componentDescription.componentFlagsMask = canMovieExportFiles;

The list is cached for further usage, do not forget to release it in your dealloc.

We are going pretty good so far. We have got our MPEG4 component object, got Component structure from it. Now we need to apply the settings we stored last time to the 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);
    }
}

If you use the component first time without any settings, the dialog will display default once.

After user has finished with editing and pressed OK we need to store the settings:

- (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 is a wrapper, see the listing below. You can easily make it serialised to a file to store the settings.

The last step is to use the settings we have just got for movie conversion. The good new is that we do not have to use Quicktime any more, we can use QTKit methods.

QTMovie* movie = [QTMovie movieWithFile:sourceFilePath error:outError];

NSDictionary* settings = [movieExportSetting movieAttributes];

[movie writeToFile:outputFilePath withAttributes:settings error:outError]

Set movie delegate and implement movie: shouldContinueOperation: withPhase: atPercent: withAttributes: to see the progress if needed.

Below you can find listings of the classes used. I hope this helps.

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

@implementation JSQTComponent

@synthesize component = _component; @synthesize componentDescription = _componentDescription; @synthesize name = _name; @synthesize info = _info; @synthesize icon = _icon;

  • (id)initWithComponent:(Component)component { self = [super init]; if (self != nil) { _component = component; [self getDescription]; } return self; }

  • (void)dealloc { [_name release]; [_info release]; [_icon release]; [super dealloc]; }

  • (void)getDescription { OSErr err; Handle aName = NewHandleClear(255); Handle anInfo = NewHandleClear(255); Handle anIcon = NewHandleClear(255); Handle iconSuite = NULL;

    err = GetComponentInfo(_component, &_componentDescription, aName, anInfo, anIcon); if (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); }

@end

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




相关问题
Display MPEG-4 Export Component Dialog

Below is my code for a doc based QTKit app that exports to the fixed Apple device formats by way of a popUp that displays the three device options. The [NSNumberWithLong:mpg4 ] option works fine as ...

Quicktime Framework and opening Transport Streams

I have noticed that Quicktime 10 is now able to open Transport Stream Video files and also search reliably within that video file(Which is something that VLC can not seem to handle). Quicktime 7, on ...

export to MP3 from quicktime API

A question for Apple,QT programmers. Would like to know if it s possible to export a Movie object to MP3 using the QuickTime API. Preferably the ConvertMovieToFile function. I ve looked at ...

热门标签