Apple Developer Connection
高度な検索
Member Login ログイン | ご入会 ADC連絡先

Technical Note TN2138
QTKitについてよく尋ねられる質問

このテクニカルノートでは、QTKitについてよく尋ねられる次の一連の質問を取り上げます。





サウンドファイルを再生しようとしても何も起こらない

Q: QTKitを使用してサウンドファイルを開き、再生しようとしましたが、何も起こりません。 まったく何の音も聞こえません。 しかし、setMovie:メソッドを使用してQTMovieQTMovieViewに設定すると、問題なく機能します。自作のコードを以下に示します。何が間違っているのでしょうか。

    QTMovie *aMovie;
    QTMovieView *aMovieView;

    aMovie = [QTMovie movieWithFile:s error:nil];
    [aMovie setVolume: volumeVal];
 // [aMovieView setMovie:aMovie];   // これをすると正常に機能する!
    [aMovie play];

A: 次のコード行で、自動解放(QTMovie)オブジェクトを作成します。

    aMovie = [QTMovie movieWithFile:s error:nil];

実行ループに再び入ると、オブジェクトが破棄されます。これがオブジェクトを再び使用しようとしても何も起こらない理由です。

QTMovieViewQTMovieを割り当てるとコードが正常に機能する理由は、QTMovieViewによってQTMovieオブジェクトが保持され、保持されたそのQTMovieQTMovieに組み込まれた通常アイドリングメカニズムによってアイドル状態になります。そのため、ムービーが再生されます。

自動解放プールに置かれないように、代わりにalloc:メソッドを使用してQTMovieオブジェクトを作成します。 その後は、オブジェクトが用済みになったときに解放するだけです。たとえば、次のようにします。

    QTMovie* aMovie;
    aMovie = [[QTMovie alloc] initWithFile:s error:nil];
    [aMovie setVolume: volumeVal];
    [aMovie play];

 ...    /* ここ何かする */

    [aMovie release];   // 用済みになったらオブジェクトを解放する

あるいは、元のコードのようにmovieWithFile:メソッドを使用することもできますが、直後にオブジェクトを保持するようにします。

    QTMovie* aMovie;
    aMovie = [QTMovie movieWithFile:s error:nil];
    [aMovie retain];    // オブジェクトを保持する
    [aMovie setVolume: volumeVal];
    [aMovie play];

...     /* ここ何かする */

    [aMovie release];   // 用済みになったらオブジェクトを解放する

先頭に戻る

QTMovieにQTTrackを追加するには

Q: QTMovieQTTrackを追加するにはどのようにするのですか? QTKitの参考資料をちょっと調べてみましたが、これを行う簡単な方法が分かりません。

A: 現在のところ、QTMovieオブジェクトにトラックを追加するQTKitメソッドはありません。 しかし、今後のバージョンのQTKitではそのサポートを追加する予定です。

それまでは、標準のQuickTime API(すなわち、NewMovieTrackNewTrackMediaなど)を使用して、QTMovieに関連付けられているムービーにトラックを追加する必要があります。 これでうまく機能するはずです。

そのために実行する必要があることの概略は以下のとおりです。

  • QTMovieを作成する

  • QTMovieに関連付けられているQuickTimeムービーを取得する

  • NewMovieTrackを使用して新しいトラックを作成する

  • そのトラックを使用してQTTrackを初期化する

以下のコードにその方法を示します。

リスト1: QTMovieへのQTTrackの追加


@implementation QTMovie (QTMovieExtensions)

-(BOOL)isEditable
{
    NSDictionary *movieDict = [self movieAttributes];
    NSNumber *isEditable = [movieDict objectForKey:QTMovieEditableAttribute];

    return [isEditable boolValue];
}

- (QTTrack *) addVideoTrackWithSize:(NSSize) aSize
{
    QTTrack *newTrack = nil;

    require( [self isEditable] == YES, NOT_EDITABLE);

    Track videoTrack = NewMovieTrack ([self quickTimeMovie],
                                    FixRatio (aSize.width, 1),
                                    FixRatio(aSize.height, 1),
                                    kFullVolume);
    require (GetMoviesError() == noErr, NEWTRACK_ERROR);

    Handle dataRef = NULL;
    Handle hMovieData = NewHandle (0);
    require (hMovieData != nil, NEWHANDLE_ERR);
    OSErr osErr = PtrToHand (&hMovieData, &dataRef, sizeof(Handle));
    require (osErr == noErr, PTRTOHAND_ERROR);

    Media videoMedia = NewTrackMedia (videoTrack,
                                    VideoMediaType,
                                    [[self timeScale] longValue],
                                    dataRef,
                                    HandleDataHandlerSubType);
    require (GetMoviesError() == noErr, TRACKMEDIA_ERROR);

    QTTime movieDuration = [self duration];
    InsertMediaIntoTrack (videoTrack, 0, 0, movieDuration.timeValue, fixed1);

    require (GetMoviesError() == noErr, INSERTMEDIA_ERROR);

    newTrack = [QTTrack trackWithQuickTimeTrack: videoTrack error:nil];

    return newTrack;

INSERTMEDIA_ERROR:
    DisposeTrackMedia(videoMedia);
TRACKMEDIA_ERROR:
PTRTOHAND_ERROR:
    DisposeHandle(hMovieData);
NEWHANDLE_ERR:
    DisposeMovieTrack (videoTrack);
NEWTRACK_ERROR:
NOT_EDITABLE:

    return nil;
}


@end

先頭に戻る

QTMovieTimeDidChangeNotification通知はいつ発行されるか

Q: QTMovieTimeDidChangeNotification通知はいつ発行されるのでしょうか。 新しいフレームが表示されるときなど、ムービーのタイムコードが変わるときに発行されると思っていたのですが、実際はそうではないようです。 再生しているムービーの現在時間を表示したいと思っています。

A: QTMovieTimeDidChangeNotificationが発行されるのは、ムービー時間が***通常再生時以外の***時間に変化したときです。 そのため、毎フレーム発行されるわけではありません。

たとえば、ユーザがムービーコントローラバーをクリックしてムービー時間を変えたときや、ワイヤードアクションによってムービー時間が変わったときに発行されます。

代わりに、NSTimer、さらにはMovie Controllerアクションフィルタ関数(Movie Toolbox関数のMCSetActionFilterWithRefConを参照)の使用を検討するとよいでしょう。

先頭に戻る

空のムービーを作成して画像を追加するには

Q: 1つのNSImageから10秒間のQTMovieを作成し、QuickTimeムービー(.mov)ファイルとして保存したいと思っています。 しかし、QTKitには「空の」ムービーを作成する手段がないように思われます。 本当にそうなのでしょうか。

A: はい、そのとおりです。

今後のバージョンのQTKitには、画像を追加できる「空の」ムービー(書き込み可能なデータ参照付きのムービー)を作成する簡単なメソッドが含まれる見込みです。

それまでの間は、2つの対処法があります。

まず、QuickTimeのネイティブAPIであるCreateMovieStorageを使用して、書き込み可能なデータ参照付きのQuickTimeムービーを作成できます。作成後、QTKitのmovieWithQuickTimeMovie:メソッドを使用して、そのQuickTimeムービーからQTMovieをインスタンス化します。サンプルコードプロジェクト「QTKitCreateMovie」では、その方法を示しています。

第二に、NSImageからQTMovieを直接初期化して、現在行っているようにエクスポートすることもできます。 以下のコードにこの方法を示します。

リスト2: NSImageからQTMovieを初期化する

        // 画像のためのNSImageオブジェクトをインスタンス化する
        NSImage *image = [NSImage imageNamed:@"some_image"];
        if (image)
        {
            // すべての表現についてTIFFを含んだデータオブジェクトを返す
            NSData *data = [image TIFFRepresentation];
            QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToData:data
                                                              name:@"some_image.tiff" MIMEType:nil];
            // NSImageからQTMovieを作成する
            QTMovie *movie = [QTMovie movieWithDataReference:dataRef error:nil];

            // ムービーを編集可能にする
            [movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];

            // 再生時間を10秒に設定する
            QTTimeRange range = QTMakeTimeRange(QTZeroTime, [movie duration]);
            [movie scaleSegment:range newDuration:QTMakeTime(10, 1)];

            // 3GPPファイルとしてエクスポートするか、既存のエクスポートコードを使用する....

            // ディクショナリに適切なエクスポート属性をセットアップする
            NSDictionary *dict= [NSDictionary dictionaryWithObjectsAndKeys:
                [NSNumber numberWithBool:YES], QTMovieExport,
                [NSNumber numberWithLong:kQTFileType3GPP], QTMovieExportType, nil];

            // QTMovieを3GPPムービーファイルとしてディスクに書き出す
            [movie writeToFile:@"/tmp/my3GP.mov" withAttributes:dict];
        }

先頭に戻る

QTMovieをディスク上の新規ファイルにエクスポートするには

Q: ハードディスクに書き出したいQTMovieがあります。 そのために、writeToFile:メソッドを使用しました。ファイルはディスク上に作成されるのですが、再生するとムービーにはブランク(白)画面しか表示されません。

自作のコードを以下に示します。

// マイムービー
QTMovie *movie = [qtPlayer movie];

// コーデックディクショナリ
NSDictionary *codecDictionary =
    [NSDictionary dictionaryWithObjectsAndKeys: @"jpeg",
    QTAddImageCodecType, [NSNumber numberWithInt: codecNormalQuality],
    QTAddImageCodecQuality, nil];

// moviePathも正しい
[movie writeToFile: moviePath withAttributes: codecDictionary];

A: QTAddImageCodecTypeおよびQTAddImageCodecQualityキーは、-[QTMovie addImage:forDuration:withAttributes:]メソッドに渡されるディクショナリで使用するためのものです。

writeToFile:withAttributes:メソッドに対しては、代わりに以下のキー(これらはQTMovie.hインターフェイスファイルから抜粋)を使用する必要があります。

    // writeToFile: 属性ディクショナリキー
QTKIT_EXTERN NSString *QTMovieExport
        AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;  // NSNumber (BOOL)
QTKIT_EXTERN NSString *QTMovieExportType
        AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;  // NSNumber (long)
QTKIT_EXTERN NSString *QTMovieFlatten
        AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;  // NSNumber (BOOL)
QTKIT_EXTERN NSString *QTMovieExportSettings
        AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;  // NSData (QTAtomContainer)
QTKIT_EXTERN NSString *QTMovieExportManufacturer
        AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;  // NSNumber (long)

次のサンプルは、エクスポート操作の実行方法を示します。 サンプルコードプロジェクト「QTKitCreateMovie」サンプルコードプロジェクト「QTKitProgressTester」、およびサンプルコードプロジェクト「QTKitCommandLine」

先頭に戻る

QuickTimeでサポートしているファイルタイプまたは拡張のリストを取得するには

Q: QuickTimeで扱えるファイルタイプまたは拡張のリストを取得するには、どうすればよいでしょうか。

A: QTMovie movieFileTypes:クラスメソッドを参照してください。 このメソッドが、必要としていることをそのとおり実行してくれるはずです。 このメソッドの出力をNSSavePanel setAllowedFileTypes:メソッドに渡すだけです。 サンプルコードプロジェクト「QTKitAdvancedDocument」で、これがどのように実行されているか確認してください。

これは問題なく機能します。また、movieFileTypes:メソッドに渡すフラグのセットを調整することで、静止画像タイプ、変換可能タイプ、および「積極的な」インポータを必要とするタイプ(テキストファイルやHTMLファイルなど)を選択的に含めることができます。

ただし、もっと簡単な方法もあります。それはcanInitWithFile:メソッドを使用する方法です。以下は、この方法を示したコードの例です。

- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
{
    BOOL isDir = NO;

    [[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir];

    return isDir ? YES : [QTMovie canInitWithFile:filename];
}

この方法は、movieFileTypes:メソッドによって返されるファイルタイプの配列をQTIncludeAllTypesフラグとともに渡すのと基本的に同じです。

先頭に戻る

QTMakeTimeFromString/QTTimeFromString関数がどのように機能するか

Q: QTMakeTimeFromString/QTTimeFromString関数がどのように機能するか

現在のドキュメントでは、この関数がQTMakeTimeFromStringという名前だとしていますが、ヘッダファイルのQTTime.hを見ても、この関数は見つかりません。 この関数は実際には、QTTimeFromStringという名前です。 使用しているコードを以下に示します。

QTTime oldTime = [qtMovie currentTime];
QTTime incTime = QTTimeFromString( @"00:02:00.00" );
QTTime newTime = QTTimeIncrement( oldTime, incTime );

NSLog( QTStringFromTime( oldTime ) );
NSLog( QTStringFromTime( incTime ) );
NSLog( QTStringFromTime( newtime ) );

次のような結果になります。

    0:00:00:00.00/48000
    0:00:00:00.00/1000000
    0:00:00:00.00/1000000

また、時間文字列を@"0:00:02:00.00", @"0:0:2:0.0",などのバリエーションに設定してみました。 しかしやはり、うまくいきません。

何が間違っているのでしょうか。

A: QTTime.hにある次のコメントに注目してください。

// ,,,dd:hh:mm:ss.ff/ts

これは次のような意味になります。

日:時間:分:秒:フレーム/タイムスケール

したがって、次のような文字列を使うようにします。

QTTime incTime = QTTimeFromString( @"00:00:02:00.00/600" );
NSLog( QTStringFromTime( incTime ) );

これでうまく機能するはずです。 現在のドキュメントは正しくありません。

先頭に戻る

QTMovieオブジェクトの形成は完全か

Q:

自作のアプリケーションでmovieWithFile:メソッドを使用するとQTMovieオブジェクトを正常にインスタンス化できますが、その直後にオブジェクトを照会してみると、トラックが含まれておらず、属性もほとんどないことが分かります。 また、QTMovieNaturalSizeAttribute属性を取得しようとすると、「0,0」というサイズが報告されます。

QTMovieオブジェクトを適切にインスタンス化するには、何か他にしなければならないことがあるのでしょうか。

A:

movieWithFile:メソッドで初期化した直後にQTMovieオブジェクトを照会したとき、属性を要求したり、writeToFile:メソッドなどの呼び出しのような一定の操作を実行したりはできるという点で、QTMovieオブジェクトの形成が完全ではないことがあります。これは、デフォルトではすべてのQTMovie初期化メソッドが非同期で実行されるためです。

これを防ぐには、初期化呼び出しを同期的に行います。 そのためには、initWithAttributes:メソッドを使用して、QTMovieOpenAsyncOKAttributeをNOに設定する必要があります。 コードの一例を以下に示します。

NSSize movieSize;
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
        (id)url, QTMovieURLAttribute,
        [NSNumber numberWithBool:NO], QTMovieOpenAsyncOKAttribute,
        nil];
QTMovie *movie = [QTMovie movieWithAttributes:attrs error:nil];
movieSize = [[movie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];

[movie play];
...

もう1つの選択肢は、QTMovieLoadStateDidChangeNotification通知の通知ハンドラをインストールして、ロード状態が少なくとも100000(kMovieLoadStateCompleteで定義)になるまではQTMovieオブジェクトに属性を照会しないことです。 コードの一例を以下に示します。

// QTMovieLoadStateDidChangeNotificationの通知ハンドラをインストールする
QTMovie *movie = [QTMovie movieWithURL:url error:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(loadStateChanged:) name:QTMovieLoadStateDidChangeNotification object:movie];
.
.
.

// ロード状態の変化をチェックする
-(void)loadStateChanged:(QTMovie *)movie
{
    long loadState = [[movie attributeForKey:QTMovieLoadStateAttribute] longValue];
    if (loadState >= 20000)
    {
        // 20000はkMovieLoadStatePlaythroughOK
        [movie play];
    }
    else if (loadState >= 10000)
    {
        // 10000はkMovieLoadStatePlayable
        NSSize movieSize;
        movieSize = [[movie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
        /* ... */
    }
    else if (loadState == -1)
    {
        /* エラーが発生、エラーを処理 */
    }
}

先頭に戻る

最終ムービーフレームを見つけるには

Q:

ムービーの最終フレームに達したかどうかを判断するには、どうしたらよいのでしょうか。

stepForward:メソッドを使ってみたのですが、実際に成功したかどうか報告されません。QTMovieDidEndNotificationはこの情報を与えてくれるのでしょうか。

A:

ムービーの最後に達しても、QTMovieDidEndNotificationは発行されません。

1つの選択肢は、これらの操作に対して以下に示すような新しいメソッドを持つ独自のQTMovieカテゴリを定義することです(各メソッドには、現在または将来のQTKitメソッドと競合しないように、先頭に「rt_」という接頭辞を付加しています)。

@implementation QTMovie (MyQTMovieExtension)

- (BOOL)rt_stepForward
{
  QTTime curTime, newTime;

  curTime = [self currentTime];
  [self stepForward];
  newTime = [self currentTime];

  return (QTTimeCompare(curTime, newTime) != NSOrderedSame);
}

- (BOOL)rt_stepBackward
{
  QTTime curTime, newTime;

  curTime = [self currentTime];
  [self stepBackward];
  newTime = [self currentTime];

  return (QTTimeCompare(curTime, newTime) != NSOrderedSame);
}

@end

先頭に戻る

バックグラウンドスレッドでQTMovieオブジェクトを使用するには

Q:

テクニカルノートTN2125「QuickTimeにおけるスレッドセーフなプログラミング」を読んだばかりなのですが、QTKitについて何も述べられていません。バックグラウンドスレッドでQTMovieオブジェクトを安全に使用するには、どうすればよいでしょうか。

A:

バックグラウンドスレッドでQTMovieオブジェクトを扱うときには、以下のガイドラインを順守してください。

(1) QTMovieオブジェクトはすべてメインスレッド上に割り当てます。

この理由はまったく単純なものです。 QTMovieはムービーコントローラコンポーネントのインスタンスに必ず関連付けられていますが、現在のところムービーコントローラコンポーネントはスレッドセーフではないからです。 確かにバックグラウンドスレッドにnilでないQTMovieを作成できますが、関連付けられているムービーコントローラは必ずNULLになります。 このこと自体は致命的ではありません。バックグラウンドスレッドで初期化したQTMovieオブジェクトでも、多くの操作を実行できます。 しかし、ほとんどすべての通知や非同期ムービーロードなど、一部の操作は正しく機能しません。 そういうわけで、バックグラウンドスレッドにQTMovieを割り当てるのはお勧めしません。

(2) 異なるスレッドでQTMovieを操作するには、(quickTimeMovie:メソッドで取得した当該QTMovieに関連付けられているQuickTimeムービー上で)Movie Toolbox の C API DetachMovieFromCurrentThreadを使用して現在のスレッドからそのQTMovieをデタッチし後に、AttachMovieToCurrentThreadを使用して別のスレッドにアタッチします。また、QTKitのバグを回避するために、ここでprivateメソッドのsetIdling:を呼び出して、ムービーがバックグラウンドスレッドで操作されないようにすることをお勧めします。

(3) 二次スレッドで他のQuickTime呼び出しまたはQTKit呼び出しを行う前に、EnterMoviesOnThreadを呼び出します。

(4) QuickTime呼び出しを行ったスレッドを終了する前に、ExitMoviesOnThreadを呼び出します。

(5) これらの呼び出しのいずれかによるエラーを処理する用意をします。二次スレッドに移動できないムービーもあります(これはコーデックによります)。

QTKitは上記のどれも自動的に実行してくれませんが、アップルでは、このプロセスを簡単にする(少なくともCocoaに似たプロセス)にするいくつかの新しいAPIに取り組んでいます。

以下の短いコードは、バックグラウンドスレッドでムービーのエクスポートを実行する方法を示します。

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

    EnterMoviesOnThread(0);
    AttachMovieToCurrentThread([movie quickTimeMovie]);

    // QTKitのバグを回避するために、privateメソッドのsetIdling:を呼び出して、
    // ムービーがバックグラウンドスレッドで操作されないようにする
    // 必要がある
    if ([movie respondsToSelector:@selector(setIdling:)])
        [movie setIdling:NO];

    // エクスポートを実行する
    [movie writeToFile:....];

    DetachMovieFromCurrentThread([movie quickTimeMovie]);
    ExitMoviesOnThread();

    [pool release];
}

先頭に戻る

QTMovieViewをサブクラス化するには

Q:

QTMovieViewクラスのmouseDown:メソッドをオーバーライドしようとしたのですが、成功しませんでした。QTMovieViewを適切にオーバーライドする方法を教えてください。

A:

これはQTKit 1.0のQTMovieViewに関する既知の問題であり、その後QuickTime 7.0.4で修正されています。 QuickTime 7.0.4がインストールされていることを確認してください。

先頭に戻る

QTMovieがなぜか画面の左上部に描画される

Q:

Movie ToolboxのC API CreateMovieStorageを使ってムービーを作成し、フレームを追加して、このQuickTimeムービーからQTMovieをインスタンス化すると、QTMovieはなぜか画面の左上部に表示されます。どうなっているのでしょうか。

A:

この問題は、QuickTime Carbon APIとQTKitを併用しているときに発生する可能性のあるものです。 CarbonのネイティブQuickTime APIを使ってムービーを作成するときには、ムービーが表示する位置を認識するように、あらかじめ有効なポートを設定する必要があります。 これについては、次のQ&Aを参照してください。

QA1345「The QuickTime Movie Toolbox requires a valid graphics port for all movies」

この事例で何が起きているかというと、CreateMovieStorageを呼び出したときに、画面がアクティブポートとして設定されたのです。そのため、特に指定しないかぎり(たとえば、SetMovieGWorldを呼び出してムービーを別のポートに指定するなど)、ムービーは画面に表示されます。

これを回避するには、CreateMovieStorageを使って新しいムービーを作成する直前に、「ダミー」のgworldを作成してアクティブに設定します。この方法を以下に示します。

GWorldPtr gworld = NULL;
Rect rect = {0, 0, 1, 1};

NewGWorld(&gworld, 32, &rect, NULL, NULL, 0);
SetMovieGWorld(qtMovie, gworld, NULL);

以降、ムービーは「ダミー」のgworldに描画されるようになります。

先頭に戻る

ドキュメント改訂履歴

日付メモ
2006-09-25スレッド処理、同期ムービーインスタンス化などを題材とする新しいQ&Aを追加。
2005-12-19NewTrackMediaに正しいタイムスケール値を渡すための修正。
2005-07-14QTKitについてよく尋ねられる多数の質問に対する回答を用意。

掲載日: 2006-09-25




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.