Using the Audio Extraction API in QuickTime 7
OSXDEV
당신의 애플리케이션에 아이튠즈 비쥬얼라이저 같은 퀵타임 무비 오디오를 위해 커스텀 비쥬얼 디스플레이가 필요한가? 또는 홈무비에서 오디오를 뽑아내서 당신이 원하는 오디오 포맷으로 바꾸고 싶은가?
아마 당신은 아이무비나 퀵타임플레이어를 사용해봤을 것이고 이 애플리케이션들이 쉽게 비디오 클립에서 오디오를 뽑아내는 방식에 만족할 것이다. 예를 들어, 홈비디오에 관심이 있어서 이 클립의 오디오를 다른 비디오나 이미지에 사용하고 싶은 경우가 있다. 그렇다면 QuickTime 7에서 소개된 새 오디오 추출 API를 사용하면 된다. 이 새 API는 퀵타임 무비에 있는 다중 사운드 트랙을 쉽게 추출, mix할 수 있게 해주며 raw PCM데이타로 변환 할 수 있다. 그 raw 오디오 데이타를 재생하거나 익스포트시키거나 애플리케이션 안에서 직접 조작 할 수 있다. 또는 무비 오디오를 추출한 후 코어오디오를 이용해서 시그널 프로세싱을 수행하고 싶을 지도 모른다. Audio Extraction API를 이용해서 이런 것은 물론 그 이상의 일을 할 수 있다.
이전에는 편리하고 사용하기 쉬운 API가 없어서 오디오를 추출하기가 쉽지 않았다. 개발자들은
- (a) 귀찮고 제약이 많은 GetMediaSample함수를 이용해서 수동으로 모든 무비의 오디오 샘플을 일일이 뽑아내거나
- (b) 일부분씩 추출하지 않고 한번에 모든 오디오를 추출해주는 PutMovieIntoTypedHandle함수 같은 오디오 익스포트 컴포넌트를 이용하거나
- (c) 만들기 어려운 사운드 아웃풋 컴포넌트를 만드는 방법을 사용해야만 했다
현재는 오디오 추출 API는 많은 앞서 설명한 기존의 방법에 대해서 다음과 같은 많은 장점을 제공한다.
- 보다 효과적인 실행방법
- 무비내의 모든 활성 사운드 트랙을 믹싱
- 추출하고자 하는 구간을 정확히 뽑아내는 기능
- 믹스 결과의 채널 레이아웃을 지정할 수 있는 기능
- AudioContext에 새로운 트랙 타입을 자동으로 믹스해넣는 기능(???)
- 플래이백할때와 같은 실행 경로를 사용한다. 그러므로 듣는 그대로의 결과물을 얻을 수 있다.
- 사용하기 쉬운 API
오디오 추출 API는 QuickTime 7문서나 여기에 설명되어있다.
주의 : 오디오 추출 API는 현재 사운드 트랙의 오디오만 처리한다. 프로그램 스트림 내에 있는 muxed MPEG-1오디오는 현재 지원되지 않는다.
목차 |
[편집] 시작하기
오디오 추출의 기본적인 순서는 다음과 같다.
- 추출 시작
- 추출 속성 설정
- 오디오 샘플로 버퍼 채우기
- 추출 종료
오디오를 추출하기 시작할때 무비는 반드시 액티브상태여야 한다. (SetMovieActive 함수 사용). 듣고자 하는 모든 사운드 트랙 역시 enable되어 있어야 한다. (SetTrackEnabled함수 사용) 추출하고자 하는 세션을 설정했다면, 시작하자. 추출작업은 MovieAudioExtractionFillBuffer를 처음으로 호출할때 시작되는데 MovieAudioExtractionFillBuffer가 호출되고 나면 현재 추출세션에 대한 설정이 고정되어 수정할 수 없다. 만일 바꾸고 싶다면 세션을 닫고 새 세션을 열어야 한다.
오디오 추출작업은 GetMovieDuration으로 인식되는 무비의 끝까지 유효한 데이터를 리턴할 것이다. 만일 모든 오디오트랙이 그 전에 끝나면 무비 duration까지 조용하다. GetTrackDuration함수를 이용해서 관심있는 특정 오디오 트랙의 실제 길이를 알아내고 추출을 한정할 수 있다.
지금부터 단계별로 새로운 API를 이용해 오디오를 추출하는 법을 자세히 살펴보자.
[편집] 1단계 : 추출시작
MovieAudioExtractionBegin함수를 호출함으로써 시작된다. 이 API는 오디오 추출 작업을 하기전에 반드시 불러야 한다. Listing 1에 보이는 것 같이 이 함수는 이어지는 추출 루틴으로 넘겨질 오패크(불분명한) 세션 오브젝트를 넘겨준다.
Listing 1: 오디오 추출 세션 오패크 오브젝트 가져오는 방법
#if TARGET_OS_MAC #include <QuickTime/QuickTime.h> #elif TARGET_OS_WIN32 #include <Movies.h> #endif OSStatus err = noErr; MovieAudioExtractionRef extractionSessionRef = nil; err = MovieAudioExtractionBegin(movie, 0, &extractionSessionRef);
추출된 오디오 포맷은 무비의 결합된 채널 레이아웃, (예를 들면, 모든 오른쪽의 믹스된 채널, 모든 왼쪽의 믹스된 서라운드 채널등등), 32비트 float , de-interleaved, 무비의 모든 트랙들로부터 찾을 수 있는 가장 높은 샘플율에 의거 설정된다.
오디오 추출 API는 모든 다양한 형태의 PCM데이터를 지원한다. 만일 엄청나게 압축된 데이타가 필요하다면 리턴 데이터에 고유의 압축을 해야한다. 추출된 오디오의 포맷을 설정하기 위해서 아래와 같이 "Get/Set Audio Extraction Session Properties"를 사용하면 된다. 이 설정 작업은 MovieAudioExtractionFillBuffer함수가 호출되기 전에 완료해야 한다.
[편집] 2단계 : 오디오 추출 세션 프라퍼티를 얻고 설정하기
오디오 추출작업은 주어진 하나의 오디오 추출 세션에 대해 얻고 설정할 수 있는 수많은 프라퍼티들을 정의한다. 예를 들어, 오디오 스트림 기본 디스크립션(ASBD) 프라퍼티나 오디오 채널 레이아웃을 얻을 수 있다.
채널 레이아웃과 같은 몇몇 프라퍼티들은 유동적인 크기를 가지므로, 정보를 얻어내는 두단계의 프로세스에 걸쳐있다는 것을 주의하라.
- 먼저, MovieAudioExtractionGetPropertyInfo를 호출하여 프라퍼티를 위해 얼마나 많은 공간을 할당해야 하는지를 찾는다.
- 다음은, MovieAudioExtractionGetProperty를 호출해 프라퍼티의 실제 값을 얻는다.
예를들어, Listing 2의 코드는 채널 레이아웃을 얻기 위해 이 기술을 사용하는 방법을 보여준다.
Listing 2: 추출 세션을 위한 채널 레이아웃을 얻기
#if TARGET_OS_MAC
#include <CoreAudio/CoreAudio.h>
#include <QuickTime/QuickTime.h>
#elif TARGET_OS_WIN32
#include <CoreAudioTypes.h>
#include <Movies.h>
#endif
OSStatus err = noErr;
AudioChannelLayout *layout = NULL;
UInt32 size = 0;
// First get the size of the extraction output layout
err = MovieAudioExtractionGetPropertyInfo(extractionSessionRef,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
NULL, &size, NULL);
if (err == noErr)
{
// Allocate memory for the channel layout
layout = (AudioChannelLayout *) calloc(1, size);
if (layout == nil)
{
err = memFullErr;
goto bail;
}
// Get the layout for the current extraction configuration.
// This will have already been expanded into channel descriptions.
err = MovieAudioExtractionGetProperty(extractionSessionRef,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
size, layout, nil);
}
ASBD를 이용해 Float32가 아닌 다른 비압축 포맷을 지정할 수 있다. 이것은 추출API를 자동으로 저장된 오디오 포맷에서 당신이 지정한 포맷으로 변환한다. 추출세션을 위해 ASBD를 설정하려면, MovieAudioExtractionSetProperty를 사용한다. 예를들어, Listing 3은 디폴트인 non-interleaved Float32대신 interleaved된 16비트 PCM을 리턴받기 위해 ASBD를 설정하는 법을 보여준다.
Listing 3: 추출세션으로 Interleaved 16비트 PCM을 리턴받기 위해 MovieAudioExtractionSetProperty를 사용하기
#if TARGET_OS_MAC
#include <CoreAudio/CoreAudio.h>
#include <QuickTime/QuickTime.h>
#elif TARGET_OS_WIN32
#include <CoreAudioTypes.h>
#include <Movies.h>
#endif
OSStatus err;
AudioStreamBasicDescription asbd;
// Get the default audio extraction ASBD
err = MovieAudioExtractionGetProperty(extractionSessionRef,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
sizeof (asbd), &asbd, nil);
// Convert the ASBD to return interleaved 16-bit PCM instead of non-interleaved Float32.
asbd.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked
| kAudioFormatFlagsNativeEndian;
asbd.mBitsPerChannel = sizeof (SInt16) * 8;
asbd.mBytesPerFrame = sizeof(SInt16) * asbd.mChannelsPerFrame;
asbd.mBytesPerPacket = asbd.mBytesPerFrame;
// Set the new audio extraction ASBD
err = MovieAudioExtractionSetProperty(extractionSessionRef,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
sizeof (asbd), &asbd);
ASBD는 AudioStreamBasicDescription구조체로 표현된다. 이것은 Core Audio(맥 OS X에 있는 현대적인 오디오아키텍쳐)에 있는 기본적인 설명 구조체이다. 오디오 데이타의 스트림을 기술하는 데 필요한 모든 정보를 담고있다. ASBD와 채널 레이아웃 구조에 대한 정의는 CoreAudio.framework/Headers/CoreAudioTypes.h 헤더파일에서 찾아볼 수 있다.
Listing4 는 이 구조체가 어떤 것인지를 각 필드의 설명과 함께 보여준다.
Listing 4: AudioStreamBasicDescription (ASBD)구조체
typedef struct AudioStreamBasicDescription {
Float64 mSampleRate;
UInt32 mFormatID;
UInt32 mFormatFlags;
UInt32 mBytesPerPacket;
UInt32 mFramesPerPacket;
UInt32 mBytesPerFrame;
UInt32 mChannelsPerFrame;
UInt32 mBitsPerChannel;
UInt32 mReserved;
} AudioStreamBasicDescription;
mSampleRate
The number of sample frames per second of the data in the stream.
mFormatID
A four char code indicating the general kind of data in the stream.
mFormatFlags
Flags specific to each format.
mBytesPerPacket
The number of bytes in a packet of data.
mFramesPerPacket
The number of sample frames in each packet of data.
mBytesPerFrame
The number of bytes in a single sample frame of data.
mChannelsPerFrame
The number of channels in each frame of data.
mBitsPerChannel
The number of bits of sample data for each channel in a frame of data.
mReserved
Pads the structure out to force an even 8 byte alignment.
이 구조체는 오디오 데이타의 스트림의 프라퍼티를 설명하는 모든 정보를 포함하고 있다.
오디오 데이타에서, 하나의 프레임은 모든 채널에 걸친 하나의 샘플이다. ASBD가 non-interleaved 오디오를 설명한다면, 바이트와 프레임 카운트 필드는 하나의 채널을 나타낼 것이다 (non-interleaved 스테레오 16-비트 PCM에 있어서 mBytesPerPacket은 2가 되어야 한다.) Interleaved 오디오에 있어서, 그 필드는 n 채널들의 집합을 설명한다 ( interleaved 스테레오 16-비트 PCM에 있어서 mBytesPerPacket은 4가 되어야 한다 ). 무압축 오디오에 있어서, 하나의 패킷은 하나의 프레임이다. (mFramesPerPacket == 1).
무비 오디오 추출과정중 언제든지 ASBD를 얻을 수 있지만, 값을 설정하려면 MovieAudioExractionFillBuffer가 처음으로 호출되기 전에 값을 설정해야 한다. 추출 세션을 막 시작한 뒤에 이 프라퍼티를 얻었다면, 무비의 디폴트 추출 포맷을 넘겨줄 것이다. 그것은 디폴트 무비 믹스의 채널 개수를 포함할 것이다.
ASBD의 아웃풋을 설정하면, 아웃풋 채널 레이아웃까지 설정하는 것을 권장한다. 아웃풋 ASBD가 디폴트 추출 믹스와 다른 개수의 채널을 가진다면, 아웃풋 채널 레이아웃을 설정해야만 한다.
PCM 아웃풋 포맷만을 설정할 수 있다. 압축된 아웃풋 포맷을 설정하려하면 실패할 것이다.
MovieAudioExtractionSetProperty함수를 사용하여 채널 믹싱- 다른 레이아웃- 샘플률 변환과 원하는 샘플 사이즈를 설정한다.
interleaved 샘플을 설정하는 데에도 이 함수를 사용할 수 있다(디폴트는 non-interleaved).성공적으로 하기 위해 설정해야 할 두가지 명심할 사항이 있다; 위에서 언급한 것처럼 audio stream basic description (ASBD)와 채널 레이아웃이다. (ASBD는 포맷, 샘플, 채널 개수, interleaving등을 설정한다)
또한, 오디오 채널 레이아웃을 설정하는 대신 모든 오디오 채널의 믹싱을 불가능하게 하고 kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete 프라퍼티를 사용하여 Listing 5처럼 각각을 추출해 내는 것도 가능하다.
Listing 5: kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete을 이용해 오디오 채널의 믹싱을 불가능하게 하기
#if TARGET_OS_MAC #include <QuickTime/QuickTime.h> #elif TARGET_OS_WIN32 #include <Movies.h> #endif OSStatus err; Boolean allChannelsDiscrete = true; // disable mixing of audio channels err = MovieAudioExtractionSetProperty(extractionSessionRef, kQTPropertyClass_MovieAudioExtraction_Movie, kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, sizeof (Boolean), &allChannelsDiscrete);
오디오 추출 세션을 위한 다른 프라퍼티들은 무비의 현재 타임을 포함한다. 현재 타임은 무비내의 어떤 임의의 포인트로 설정될 수 있다. 추출중에 이미 오디오 데이타를 조금 추출해 냈더라도 언제든 현재 타임을 설정할 수 있다. Listing 6이 현재 타임 프라퍼티를 설정하는 법을 보여준다.
Listing 6: 무비 오디오 추출 현재 타임 설정하기
#if TARGET_OS_MAC
#include <QuickTime/QuickTime.h>
#elif TARGET_OS_WIN32
#include <Movies.h>
#endif
OSStatus err;
TimeRecord timeRec;
Movie movie;
movie = MyGetMovie();
timeRec.scale = GetMovieTimeScale(movie);
timeRec.base = NULL;
timeRec.value.hi = 0;
timeRec.value.lo = 60 * timeRec.scale; // for instance, to start at time 1:00.00
// Set the extraction current time. The duration will
// be determined by how much is pulled.
err = MovieAudioExtractionSetProperty(extractionSessionRef,
kQTPropertyClass_MovieAudioExtraction_Movie,
kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
sizeof(TimeRecord), &timeRec);
[편집] 3단계 : 오디오 샘플로 버퍼 채우기
준비는 끝났다. 연속적인 MovieAudioExtractionFillBuffer호출로 당신이 선택한 포맷으로 무압축 PCM 오디오를 받을 수 있다.
디폴트는 무비의 시작점에서 오디오 추출을 시작하기 위한 첫번째 호출과 마지막 호출이 끝난 지점에서 연속적인 호출을 하는 것이다. 그러나 이전 섹션에서 보여준 것처럼 현재 타임 프라퍼티를 설정함으로서 무비의 타임라인 중 어디라도 추출점을 설정할 수 있다(Listing 6 참조).
MovieAudioExtractionFillBuffer는 제공된 버퍼가 허락하고 입력무비의 한계가 허락하는 한 요청된 PCM프레임을 최대한 많이 추출할 것이라는 걸 명심하라. ioNumFrames는 리턴되는 유효 프레임의 정확한 개수로 업데이트 될 것이다. 더이상 무비로부터 추출할 오디오가 없으면 MovieAudioExtractionFillBuffer는 noErr를 계속해서 리턴하고 오디오 데이타는 리턴되지 않을 것이다. 이 경우 outFlags는 Listing 7과 같이 kQTMovieAudioExtractionComplete 비트를 가질 것이다.
Listing 7:추출 완료를 검사
err = MovieAudioExtractionFillBuffer(extractionSessionRef, &numFrames, slice->mBufferList, &flags);
if (flags & kQTMovieAudioExtractionComplete)
{
// extraction complete!
}
kQTMovieAudioExtractionComplete 비트가 마지막 유효 데이타와 합쳐지는 것도 가능하다.
[편집] 4단계 : 추출 끝내기
Listing 8과 같이 끝났으면 MovieAudioExtractionComplete를 호출해야 한다. 이것은 메모리 와 리소스의 낭비를 막기위해 내부 버퍼와 데이타 구조체를 회수한다.
Listing 8:Ending a Movie Extraction Session
OSStatus err; err = MovieAudioExtractionEnd(extractionSessionRef);
주의사항: 이상적으로, 무압축 샘플은 무비의 시작점에서 시작해서 되풀이 해나가면서 샘플을 얻었건 임의로 무비 타임을 오디오 샘플을 추출했건 간에
상관없이 비트마다 구별이 된다.
이것은 전형적인 케이스(특히 퀵타임 7.04 이후) 이지만, MP3 오디오 데이타를 압축해제하는 동안에는 피할수 없는 숨은 것이 있어서,
최대 2048개의 제로값이 유효 데이타 시작 전에 선행한다(정확한 제로의 개수는 인코더에 따라 다르다).
[편집] 백그라운드 쓰레드로부터 오디오 추출
맥 OS X v10.3 이후는 특정 퀵타임 동작을 백그라운드 쓰레드에서 실행하도록 허락한다.(퀵타임과 함께하는 쓰레드 프로그래밍에 대한 완전한 논의는 기술노트 TN2125:쓰레드로부터 안전한 퀵타임 프로그래밍 에서 한다). 이것은 이제 당신이 백그라운드 쓰레드에서 무비를 열고 이 쓰레드에서 오디오 추출을 수행할 수 있다는 것을 의미한다(혹은 반대로 메인쓰레드에서 무비를 열고, 이것을 메인 쓰레드로부터 떼어내서, 백그라운드 쓰레드로 붙이고그 백그라운드 쓰레드에서 추출을 수행하는 것도 가능)
그 과정은 아래에 자세히 나와있다. EnterMoviesOnThread 호출하기 백그라운드 쓰레드에서 퀵타임을 사용하는 어플리케이션들은 그 쓰레드에서 다른 퀵타임 API를 호출하기 전에 먼저 반드시 EnterMoviesOnThread를 각 쓰레드에서 호출해 줘야 한다. EnterMoviesOnThread는 퀵타임에게 현재 쓰레드에서 퀵타임API를 사용할 것이라고 알리는 데 사용된다.
EnterMoviesOnThread를 호출하는 것은 또한 컴포넌트 매니저에게 그 스레드에서 쓰레드에 안전하지 않은 컴포넌트의 사용을 허가하지 않도록 알린다. 컴포넌트 매니저가 어떤 함수를 실행하기 위해 쓰레드에 안전하지 않은 컴포넌트를 열려고 한다면, componentNotThreadSafeErr (-2098) 에러를 리턴하면서 컴포넌트는 열리지 않는다. 이 에러코드는 호출자에게로 퍼져서 당신의 프로그램이 이러한 타입의 에러를 다룰 수 있도록 해 준다.
주의사항:당신의 어플리케이션은 백그라운드 쓰레드에서 호출한 모든 퀵타임 API로부터 componentNotThreadSafeErr를 받을 수 있으며,
이것을 통지로 활용하여 백그라운드에서 수행되는 작업이 메인 쓰레드로 옮겨오도록 할 수 있다.
이런 에러를 다룰 수 있도록 준비하라
퀵타임이 더이상 백그라운드 쓰레드에서 사용되지 않으면, 쓰레드는 ExitMoviesOnThread를 호출해야 한다. 이것은 퀵타임에게 어플리케이션이 더이상 그 쓰레드에서 퀵타임을 사용하지 않을 것이라고 알린다.
오디오 추출을 위해 쓰레드에서 퀵타임 무비 사용하기 퀵타임 무비들은 언제든 자신이 어느 쓰레드에 속해 있는지를 알아야 한다.NewMovie, NewMovieFromDataRef, NewMovieFromFile처럼 NewMovie...API중 어떤 함수를 이용해 무비 레퍼런스를 획득하면 현재 쓰레드에 붙은 무비를 만든다.
이것은 당신이 백그라운드 쓰레드에서 오디오 추출을 하고 싶다면 다음과 같이 해야 한다는 것이다
- 백그라운드 쓰레드에서 무비를 열거나
- 메인 쓰레드에서 무비를 열고, 백그라운드로 옮긴다.
한 쓰레드에서 다른 쓰레드로 퀵타임 무비를 옮길때 호출되어야 하는 두개의 API가 있다; 퀵타임 무비를 한 쓰레드에서 다른 쓰레드로 넘길 때 원래의 쓰레드에서 DetachMovieFromCurrentThread를 호출하고 새 쓰레드에서 AttachMovieToCurrentThread를 호출한다. 이것은 퀵타임으로 하여금 어느 쓰레드가 무비를 가지는 지 알게 하며 무비가 잘못된 쓰레드에서 잘못 처리되는 것을 방지한다. 무비가 이미 그 스레드에 붙어있다면 AttachMovieToCurrentThread는 실패할 것이다.
[편집] 종합
여기에 백그라운드 쓰레드에서 오디오 추출을 하는 데 필수적인 단계와 호출의 간략한 정리를 했다.
- EnterMoviesOnThread
- 무비를 연다 (NewMovie, NewMovieFromDataRef, 등)
- 오디오 추출을 수행한다 (MovieAudioExtractionBegin, 등)
- ExitMoviesOnThread
아니면, 메인 쓰레드에서 먼저 무비를 오픈 한 뒤:
- 메인 쓰레드에서 무비를 오픈한다 (NewMovie, NewMovieFromDataRef, 등)
- 메인 쓰레드로부터 무비를 떼어낸다 (DetachMovieFromCurrentThread)
그 후 백그라운드 쓰레드로부터:
- EnterMoviesOnThread
- 무비를 백그라운드 쓰레드로 붙인다 (AttachMovieToCurrentThread)
- 오디오 추출을 수행한다(MovieAudioExtractionBegin, 등)
- ExitMoviesOnThread
[편집] 결론
새로운 퀵타임 7 오디오 추출 API는 어떤 퀵타임 무비에서든지 손쉽게 믹스된, 무압축의 오디오를 얻어낼 수 있다. 이 API는 또한 무비에서 사용가능한 모든 사운드 트랙을 믹싱한다던지, 믹스된 결과물에서 특정 채널 레이어를 지정하는 등의 오디오 추출작업에 있어서 전통적인 오디오 내보내기 컴포넌트 보다 많은 장점을 제공한다. 당신의 다음 오디오 프로그래밍 프로젝트에서는 이 강력하고, 사용하기 쉬운 API를 잘 활용해 보길 바란다.
[편집] 부가정보
샘플코드 다음의 샘플코드 프로젝트들은 이 글에서 설명한 기법을 이용해 오디오 추출 API의 사용예를 보여준다.
다른 참조
- 코어 오디오 페이지
- 퀵타임 7의 새 기능 가이드*
- 기술노트 TN2125: 퀵타임에서 쓰레드에 안전한 프로그램하기*





