QA1481

OSXDEV

Jump to: navigation, 찾기

[편집] MovieAudioExtraction - 추출가능한 모든 오디오 샘플을 추출하기

Q: MovieAudioExtraction은 오디오 파일의 끝부분에서 일부 오디오 샘플을 유실하는 것 처럼 보입니다. 어떻게 하면 추출 중 모든 오디오 샘플들이 리턴될 수 있도록 보장받을 수 있나요?

A: 퀵타임은 무비의 길이를 TimeValue/TimeScaledouble값으로 표현한다. 이는 Movie혹은 Media 구조체의 길이를 타임 유니트로 표현한 것이다. 트랙으로 참조할 수 있는 미디어 덩어리들은 모두 샘플율에 의해 결정되는 그들 각자의 타임 스케일을 가지고 있으며 퀵타임은 Movie의 타임 스케일과 그 안의 다양한 Media의 타임 스케일 사이를 자동적으로 변환해 준다.

오디오 파일로부터 불러들인 Movie의 디폴트 타임 스케일은 보통 600이 된다 (실제로, 이것은 퀵타임에서 새로 만들어진 모든 Movie들의 디폴트 타임 스케일이다). 길이를 계산하는 중에 큰 타임 스케일 값으로 인해 미세한 부분이 깎이는 오차가 발생할 수 있다.

정확히 1024 샘플(모노, 16비트, 8000Hz 샘플율)을 가지고 있는 AIFF 오디오 파일을 예를 들어보자.

이 파일이 퀵타임에 의해 불러들여지면 새 Movie가 600의 TimeScale을 가지는 하나의 트랙과 함께 만들어진다. 이 트랙은 8000 의 TimeScale을 가지는 미디어를 포함하고 있다. 미디어의 길이는 1024/8000(샘플의 개수 / 샘플율)으로 나타내며 타임 스케일 변환 뒤의 Movie 길이는 76/600으로 나타낸다.

8000Hz(미디어 타임 스케일)로 다시 변환하면 1013.3333 샘플이 산출된다. 이는 미디어 길이를 1024 샘플로 만드는데, 이는 원래의 1024보다 명백히 몇 샘플 짧은 것이다.

Movie 길이가 77/ 600이었다면, 1026.6666샘플이 계산되지만, 너무 길다(미디어가 실제로 가지고 있는 데이타보다 더 많은 양이다). 퀵타임은 그렇게 하지 않으므로 타임 스케일 변환중에는 내림의 오차가 발생하여 Movie 길이는 76 타임 유니트가 나온다.

여전히 모든 미디어는 사용가능하고 접근가능하다는 것을 명심하라. 중요한 처리중에 퀵타임이 트랙의 편집을 가하는 문제가 있다. 우리는 이것을 알고 있으므로, 원본 편집을 제거하고, Movie의 타임 스케일을 변경한 뒤 추출 프로세스를 위해 특별히 더 정확한 편집본을 만들 것이다.

// FixSoundTrackEdit 함수는 무비가 다음과 같은 조건에 맞으면 단지 교정만을 할 뿐이다:
//  - 하나의 사운드 트랙을 가진다
//  - 전체 미디어 길이 범위를 가지는 하나의 편집
//  - 편집율이 1이다
//  - 변환된 무비와 미디어 길이가 잘려진다.
// 위 점검사항 중 하나라도 실패한다면, 함수는 교정 작업을 하지 않고 0을 리턴할 것이다.
// 함수가 교정작업을 한다면 1을 리턴할 것이다.
// 다른 에러가 발생한다면, 해당하는 에러코드가 나타날 것이다.
//
// DeleteTrackSegment를 호출하여 부정확한 길이의 편집부분을 삭제하고,
// SetMovieTimeScale를 이용하여 무비 타임 스케일을 미디어의 타임 스케일로 변경하고,
// InsertMediaIntoTrack을 호출하여 새로운 편집을 만든다.

OSStatus FixSoundTrackEdit(Movie inMovie)
{
   Track theTrack;
   Media theMedia;
   SInt32 numberOfTracks;
   TimeValue editTrackStart = 0, editTrackDuration = 0;
   TimeValue editMediaTime;
   TimeValue testMovieDuration, testMediaDuration;
   Fixed editRate;

   OSStatus status = false;

   if (NULL == inMovie) { status = missingRequiredParameterErr; goto bail; }

   /*** 어플리케이션이 의도하지 않은 교정작업이 발생하지 않도록 전체적인 점검을 실행한다 ***/

   // 트랙이 하나 뿐인지 확인한다
   numberOfTracks = GetMovieTrackCount(inMovie);
   if (numberOfTracks != 1) goto bail;

   // 사용가능한 사운드 트랙이어야 한다
   theTrack = GetMovieIndTrackType(inMovie, 1, SoundMediaType, movieTrackMediaType | movieTrackEnabledOnly);
   if (NULL == theTrack) goto bail;

   // 미디어를 얻는다
   theMedia = GetTrackMedia(theTrack);
   if (NULL == theMedia) goto bail;

   // 편집을 확인한다
   GetTrackNextInterestingTime(theTrack, nextTimeTrackEdit | nextTimeEdgeOK, editTrackStart, fixed1,
                               &editTrackStart, &editTrackDuration);
   status = GetMoviesError();
   if (status) goto bail;

   // 비어있는 편집을 확인한다
   editMediaTime = TrackTimeToMediaTime(editTrackStart, theTrack);
   if (editMediaTime == -1) goto bail;

   // 편집률이 1인것을 확인한다
   editRate = GetTrackEditRate(theTrack, editTrackStart);
   if (editRate != fixed1) goto bail;

   // 다른 편집이 있는지 확인한다 - 하나의 편집만을 가져야 한다 
   GetTrackNextInterestingTime(theTrack, nextTimeTrackEdit, editTrackStart + editTrackDuration, fixed1,
                               &editTrackStart, &editTrackDuration);
   status = GetMoviesError();
   if (status) goto bail;

   if (editTrackStart != -1) goto bail;

   // 변환된 길이를 확인한다

   // 미디어 타임 스케일로 바꾼 무비길이가 미디어 길이와 같다면 교정하지 않는다. - 잘림이 생기지 않았다
   testMovieDuration = ceil(((Float64)GetTrackDuration(theTrack) / (Float64)GetMovieTimeScale(inMovie))
                         * GetMediaTimeScale(theMedia));
   testMediaDuration = GetMediaDuration(theMedia);
   if (testMediaDuration == testMovieDuration) goto bail;

   // 무비 타임 스케일로 바꾼 미디어 길이가 무비 길이와 같다면 교정하지 않는다 - 잘림이 생기지 않았다
   testMovieDuration = GetMovieDuration(inMovie);
   testMediaDuration = ((Float64)GetMediaDuration(theMedia) / (Float64)GetMediaTimeScale(theMedia))
                         * GetMovieTimeScale(inMovie);
   if (testMovieDuration != testMediaDuration) goto bail;

   /*** 교정작업을 실행한다 ***/

   // 여기까지 왔다면 잘려졌다는 것을 알 수 있다. 주로 불러오기 과정중 발생한다. 
   // delete the track edit
   // this will not remove the media, but will remove the edits that reference it
   status = DeleteTrackSegment(theTrack, 0, GetTrackDuration(theTrack));
   if (status) goto bail;

   // change the movie time scale to the media time scale
   SetMovieTimeScale(inMovie, GetMediaTimeScale(theMedia));
   status = GetMoviesError();
   if (status) goto bail;

   // create a new edit
   status = InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(theMedia), fixed1);
   if (status) goto bail;

   status = true;

bail:

   return status;
}