// TODO: this is just a test at the moment... needs to be fixed up and work with memory checking... // void ofQuicktimeSoundPlayer::tryMultiPlay(){ //soundToPlay; //TODO: error checking here! TimeValue srcMovDuration; OSErr err = noErr; Handle audioFileDataRef; OSType audioFileDataRefType; short i, resID = 0; Movie tempMovie; // initialize our movie tempMovie = nil; // create a temporary movie -- we'll add // references to the audio file to this movie tempMovie = NewMovie(newMovieActive); assert(tempMovie != nil); srcMovDuration = GetMovieDuration(soundToPlay); // add multiple references to our audio file // Note: // by not calling BeginMediaEdits/EndMediaEdits // we are telling QuickTime to *not* copy the // actual data - this will give us only references // to the data which are small // add a reference to the source audio file to // our temporary movie err = InsertMovieSegment ( soundToPlay, // our audio file tempMovie, // temporary movie 0, srcMovDuration, GetMovieDuration(tempMovie) ); //printf("here \n"); // for completeness we'll copy the movie settings // from the source file err = CopyMovieSettings(soundToPlay, tempMovie); GoToBeginningOfMovie(tempMovie); StartMovie(tempMovie); soundsForMultiPlay.push_back(tempMovie); //TODO: copy speed, volume, pan settings to this movie? }
//--------------------------------------------------------------------------- void ofVideoPlayer::setPosition(float pct){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- TimeRecord tr; tr.base = GetMovieTimeBase(moviePtr); long total = GetMovieDuration(moviePtr ); long newPos = (long)((float)total * pct); SetMovieTimeValue(moviePtr, newPos); MoviesTask(moviePtr,0); //-------------------------------------- #else //-------------------------------------- gstUtils.setPosition(pct); //-------------------------------------- #endif //-------------------------------------- }
//--------------------------------------------------------------------------- void ofVideoPlayer::setPosition(float pct){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- TimeRecord tr; tr.base = GetMovieTimeBase(moviePtr); long total = GetMovieDuration(moviePtr ); long newPos = (long)((float)total * pct); SetMovieTimeValue(moviePtr, newPos); //-------------------------------------- #else //-------------------------------------- pct = CLAMP(pct, 0,1); positionPct = pct; // check between 0 and 1; //-------------------------------------- #endif //-------------------------------------- }
void ofQtVideoSaver::addAudioTrack(string audioPath) { if(audioPath == "") return; OSErr err; Handle dataRef = NULL; FSSpec fileSpec; short audioMovieRefNum = 0; short audioMovieResId = 0; Movie audioMovie = NULL; Track audioCopyTrack = NULL; Media audioCopyMedia = NULL; Track destTrack = NULL; Media destMedia = NULL; destTrack = NewMovieTrack (movie, 0, 0, kFullVolume); destMedia = NewTrackMedia (destTrack, SoundMediaType, 30. * 100, /* Video Time Scale */ nil, 0); err = BeginMediaEdits (destMedia); char * p = new char[audioPath.length()+1]; strcpy(p, audioPath.c_str()); NativePathNameToFSSpec(p, &fileSpec, 0L); err = OpenMovieFile(&fileSpec, &audioMovieRefNum, fsRdPerm); err = NewMovieFromFile(&audioMovie, audioMovieRefNum, &audioMovieResId, NULL, newMovieActive, NULL); err = CloseMovieFile(audioMovieRefNum); SetMovieTimeScale(audioMovie, 30.*100); audioCopyTrack = GetMovieTrack(audioMovie, 1); audioCopyMedia = GetTrackMedia(audioCopyTrack); long duration = GetMovieDuration(audioMovie); err = AddEmptyTrackToMovie (audioCopyTrack, movie, nil, nil, &destTrack); err = InsertTrackSegment(audioCopyTrack, destTrack, 0, duration, 0); err = EndMediaEdits(destMedia); }
//--------------------------------------------------------------------------- float ofQuickTimePlayer::getDuration(){ if( !isLoaded() ){ ofLog(OF_LOG_ERROR, "ofQuickTimePlayer: movie not loaded!"); return 0.0; } //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- return (float) (GetMovieDuration (moviePtr) / (double) GetMovieTimeScale (moviePtr)); //-------------------------------------- #endif //-------------------------------------- }
//--------------------------------------------------------------------------- float ofQuickTimePlayer::getDuration() const{ if( !isLoaded() ){ ofLogError("ofQuickTimePlayer") << "getDuration(): movie not loaded"; return 0.0; } //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- return (float) (GetMovieDuration (moviePtr) / (double) GetMovieTimeScale (moviePtr)); //-------------------------------------- #endif //-------------------------------------- }
void ofxQtVideoSaver::addAudioTrack(string audioPath) { OSErr err; Handle dataRef = NULL; FSSpec fileSpec; short audioMovieRefNum = 0; short audioMovieResId = 0; Movie audioMovie = NULL; Track audioCopyTrack = NULL; Media audioCopyMedia = NULL; Track destTrack = NULL; Media destMedia = NULL; destTrack = NewMovieTrack (movie, 0, 0, kFullVolume); destMedia = NewTrackMedia (destTrack, SoundMediaType, (TimeScale) (30.f * 100), /* Video Time Scale */ nil, 0); err = BeginMediaEdits (destMedia); char * p = new char[audioPath.length()+1]; strcpy(p, audioPath.c_str()); #ifdef TARGET_WIN32 NativePathNameToFSSpec(p, &fileSpec, 0L); #endif #ifdef TARGET_OSX Boolean isdir; FSPathMakeRef((const UInt8*)p, &fsref, &isdir); FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fileSpec, NULL); #endif err = OpenMovieFile(&fileSpec, &audioMovieRefNum, fsRdPerm); err = NewMovieFromFile(&audioMovie, audioMovieRefNum, &audioMovieResId, NULL, newMovieActive, NULL); err = CloseMovieFile(audioMovieRefNum); SetMovieTimeScale(audioMovie, (TimeScale) (30.*100)); audioCopyTrack = GetMovieTrack(audioMovie, 1); audioCopyMedia = GetTrackMedia(audioCopyTrack); long duration = GetMovieDuration(audioMovie); err = AddEmptyTrackToMovie (audioCopyTrack, movie, 0, 0, &destTrack); err = InsertTrackSegment(audioCopyTrack, destTrack, 0, duration, 0); err = EndMediaEdits(destMedia); }
//--------------------------------------------------------------------------- float ofVideoPlayer::getDuration(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- return GetMovieDuration (moviePtr) / (double) GetMovieTimeScale (moviePtr); //-------------------------------------- #else //-------------------------------------- return fobsDecoder->getDurationSeconds(); //-------------------------------------- #endif //-------------------------------------- }
//--------------------------------------------------------------------------- float ofVideoPlayer::getDuration(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- return (float) (GetMovieDuration (moviePtr) / (double) GetMovieTimeScale (moviePtr)); //-------------------------------------- #else //-------------------------------------- return gstUtils.getDuration(); //-------------------------------------- #endif //-------------------------------------- }
//--------------------------------------------------------------------------- float ofQuickTimePlayer::getPosition(){ if( !isLoaded() ){ ofLog(OF_LOG_ERROR, "ofQuickTimePlayer: movie not loaded!"); return 0.0; } //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- long total = GetMovieDuration(moviePtr); long current = GetMovieTime(moviePtr, nil); float pct = ((float)current/(float)total); return pct; //-------------------------------------- #endif //-------------------------------------- }
/** * Checks whether the player is active. * * This function is called at regular intervals from OpenTTD's main loop, so * we call @c MoviesTask() from here to let QuickTime do its work. */ bool MusicDriver_QtMidi::IsSongPlaying() { if (!_quicktime_started) return true; switch (_quicktime_state) { case QT_STATE_IDLE: case QT_STATE_STOP: /* Do nothing. */ break; case QT_STATE_PLAY: MoviesTask(_quicktime_movie, 0); /* Check wether movie ended. */ if (IsMovieDone(_quicktime_movie) || (GetMovieTime(_quicktime_movie, NULL) >= GetMovieDuration(_quicktime_movie))) { _quicktime_state = QT_STATE_STOP; } } return _quicktime_state == QT_STATE_PLAY; }
//--------------------------------------------------------------------------- float ofxAlphaVideoPlayer::getPosition(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- long total = GetMovieDuration(moviePtr); long current = GetMovieTime(moviePtr, nil); float pct = ((float)current/(float)total); return pct; //-------------------------------------- #else //-------------------------------------- return gstUtils.getPosition(); //-------------------------------------- #endif //-------------------------------------- }
//--------------------------------------------------------------------------- void ofQuickTimePlayer::setPosition(float pct){ if( !isLoaded() ){ ofLog(OF_LOG_ERROR, "ofQuickTimePlayer: movie not loaded!"); return; } //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- TimeRecord tr; tr.base = GetMovieTimeBase(moviePtr); long total = GetMovieDuration(moviePtr ); long newPos = (long)((float)total * pct); SetMovieTimeValue(moviePtr, newPos); MoviesTask(moviePtr,0); //-------------------------------------- #endif //-------------------------------------- }
//--------------------------------------------------------------------------- float ofVideoPlayer::getPosition(){ //-------------------------------------- #ifdef OF_VIDEO_PLAYER_QUICKTIME //-------------------------------------- long total = GetMovieDuration(moviePtr); long current = GetMovieTime(moviePtr, nil); float pct = ((float)current/(float)total); return pct; //-------------------------------------- #else //-------------------------------------- return (float) fobsDecoder->getFrameTime() / getDuration(); //-------------------------------------- #endif //-------------------------------------- }
int QTImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks, int *outNumTracks, Tags *tags) { OSErr err = noErr; MovieAudioExtractionRef maer = NULL; int updateResult = eProgressSuccess; sampleCount totSamples = (sampleCount) GetMovieDuration(mMovie); sampleCount numSamples = 0; Boolean discrete = true; UInt32 quality = kQTAudioRenderQuality_Max; AudioStreamBasicDescription desc; UInt32 maxSampleSize; UInt32 numchan; UInt32 bufsize; bool res = false; CreateProgress(); do { err = MovieAudioExtractionBegin(mMovie, 0, &maer); if (err != noErr) { wxMessageBox(_("Unable to start QuickTime extraction")); break; } err = MovieAudioExtractionSetProperty(maer, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_RenderQuality, sizeof(quality), &quality); if (err != noErr) { wxMessageBox(_("Unable to set QuickTime render quality")); break; } err = MovieAudioExtractionSetProperty(maer, kQTPropertyClass_MovieAudioExtraction_Movie, kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, sizeof(discrete), &discrete); if (err != noErr) { wxMessageBox(_("Unable to set QuickTime discrete channels property")); break; } err = MovieAudioExtractionGetProperty(maer, kQTPropertyClass_MovieAudioExtraction_Audio, kQTAudioPropertyID_MaxAudioSampleSize, sizeof(maxSampleSize), &maxSampleSize, NULL); if (err != noErr) { wxMessageBox(_("Unable to get QuickTime sample size property")); break; } err = MovieAudioExtractionGetProperty(maer, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, sizeof(desc), &desc, NULL); if (err != noErr) { wxMessageBox(_("Unable to retrieve stream description")); break; } numchan = desc.mChannelsPerFrame; bufsize = 5 * desc.mSampleRate; // determine sample format sampleFormat format; switch (maxSampleSize) { case 16: format = int16Sample; break; case 24: format = int24Sample; break; default: format = floatSample; break; } AudioBufferList *abl = (AudioBufferList *) calloc(1, offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * numchan)); abl->mNumberBuffers = numchan; WaveTrack **channels = new WaveTrack *[numchan]; int c; for (c = 0; c < numchan; c++) { abl->mBuffers[c].mNumberChannels = 1; abl->mBuffers[c].mDataByteSize = sizeof(float) * bufsize; abl->mBuffers[c].mData = malloc(abl->mBuffers[c].mDataByteSize); channels[c] = trackFactory->NewWaveTrack(format); channels[c]->SetRate(desc.mSampleRate); if (numchan == 2) { if (c == 0) { channels[c]->SetChannel(Track::LeftChannel); channels[c]->SetLinked(true); } else if (c == 1) { channels[c]->SetChannel(Track::RightChannel); } } } do { UInt32 flags = 0; UInt32 numFrames = bufsize; err = MovieAudioExtractionFillBuffer(maer, &numFrames, abl, &flags); if (err != noErr) { wxMessageBox(_("Unable to get fill buffer")); break; } for (c = 0; c < numchan; c++) { channels[c]->Append((char *) abl->mBuffers[c].mData, floatSample, numFrames); } numSamples += numFrames; updateResult = mProgress->Update((wxULongLong_t)numSamples, (wxULongLong_t)totSamples); if (numFrames == 0 || flags & kQTMovieAudioExtractionComplete) { break; } } while (updateResult == eProgressSuccess); res = (updateResult == eProgressSuccess && err == noErr); if (res) { for (c = 0; c < numchan; c++) { channels[c]->Flush(); } *outTracks = (Track **) channels; *outNumTracks = numchan; } else { for (c = 0; c < numchan; c++) { delete channels[c]; } delete [] channels; } for (c = 0; c < numchan; c++) { free(abl->mBuffers[c].mData); } free(abl); // // Extract any metadata // if (res) { AddMetadata(tags); } } while (false); done: if (maer) { MovieAudioExtractionEnd(maer); } return (res ? eProgressSuccess : eProgressFailed); }
static HRESULT QT_Process_Movie(QTSplitter* filter) { HRESULT hr = S_OK; OSErr err; WineDataRefRecord ptrDataRefRec; Handle dataRef = NULL; Track trk; short id = 0; DWORD tid; LONGLONG time; TRACE("Trying movie connect\n"); ptrDataRefRec.pReader = filter->pInputPin.pReader; ptrDataRefRec.streamSubtype = filter->pInputPin.subType; PtrToHand( &ptrDataRefRec, &dataRef, sizeof(WineDataRefRecord)); err = NewMovieFromDataRef(&filter->pQTMovie, newMovieActive|newMovieDontInteractWithUser|newMovieDontAutoUpdateClock|newMovieDontAskUnresolvedDataRefs|newMovieAsyncOK, &id, dataRef, 'WINE'); DisposeHandle(dataRef); if (err != noErr) { FIXME("QuickTime cannot handle media type(%i)\n",err); return VFW_E_TYPE_NOT_ACCEPTED; } PrePrerollMovie(filter->pQTMovie, 0, fixed1, NULL, NULL); PrerollMovie(filter->pQTMovie, 0, fixed1); GoToBeginningOfMovie(filter->pQTMovie); SetMovieActive(filter->pQTMovie,TRUE); if (GetMovieLoadState(filter->pQTMovie) < kMovieLoadStateLoaded) MoviesTask(filter->pQTMovie,100); trk = GetMovieIndTrackType(filter->pQTMovie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly); TRACE("%p is a video track\n",trk); if (trk) hr = QT_Process_Video_Track(filter, trk); if (FAILED(hr)) return hr; trk = GetMovieIndTrackType(filter->pQTMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly); TRACE("%p is a audio track\n",trk); if (trk) hr = QT_Process_Audio_Track(filter, trk); time = GetMovieDuration(filter->pQTMovie); filter->movie_scale = GetMovieTimeScale(filter->pQTMovie); filter->sourceSeeking.llDuration = ((double)time / filter->movie_scale) * 10000000; filter->sourceSeeking.llStop = filter->sourceSeeking.llDuration; TRACE("Movie duration is %s\n",wine_dbgstr_longlong(filter->sourceSeeking.llDuration)); filter->loaderThread = CreateThread(NULL, 0, QTSplitter_loading_thread, filter, 0, &tid); if (filter->loaderThread) TRACE("Created loading thread 0x%08x\n", tid); filter->splitterThread = CreateThread(NULL, 0, QTSplitter_thread, filter, 0, &tid); if (filter->splitterThread) TRACE("Created processing thread 0x%08x\n", tid); else hr = HRESULT_FROM_WIN32(GetLastError()); return hr; }
void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { if (nlhs < 0) { mexErrMsgTxt("Too few output arguments."); return; } if (nlhs > 1) { mexErrMsgTxt("Too many output arguments."); return; } if (nrhs < 1) { mexErrMsgTxt("Too few input arguments."); return; } if (nrhs > 3) { mexErrMsgTxt("Too many input arguments."); return; } TimeValue duration; TimeRecord myTimeRecord; Rect bounds; OSErr result = 0; short resRefNum = -1; short actualResId = DoTheRightThing; FSSpec theFSSpec; GWorldPtr offWorld; Movie theMovie = nil; MovieController thePlayer = nil; MovieDrawingCompleteUPP myDrawCompleteProc; long frame_end; long myStep = 1; char location[PATH_BUFFER_SIZE]; long frame_count; mwSize cdims[2]; mxGetString(prhs[0], location, PATH_BUFFER_SIZE); if (nrhs > 2) { frame_start = rint(mxGetScalar(prhs[1])); frame_end = rint(mxGetScalar(prhs[2])); } else if (nrhs > 1) { frame_start = 1; frame_end = rint(mxGetScalar(prhs[1])); } else { frame_start = 1; frame_end = 0; } if (frame_start < 1) { mexErrMsgTxt("Error: the starting frame must be positive\n"); return; } if (frame_end < 0) { mexErrMsgTxt("Error: the ending frame must be positive\n"); return; } if (frame_end != 0 && frame_end < frame_start) { mexErrMsgTxt("Error: the ending frame must not be less than the starting frame\n"); return; } myDrawCompleteProc = NewMovieDrawingCompleteUPP(DrawCompleteProc); EnterMovies(); if (NativePathNameToFSSpec(location, &theFSSpec, 0) || OpenMovieFile(&theFSSpec, &resRefNum, 0) || NewMovieFromFile(&theMovie, resRefNum, &actualResId, 0, 0, 0)) { mexErrMsgTxt("Error: failed to open movie\n"); return; } if (resRefNum != -1) CloseMovieFile(resRefNum); GetMovieBox(theMovie, &bounds); QTNewGWorld(&offWorld, k32ARGBPixelFormat, &bounds, NULL, NULL, 0); LockPixels(GetGWorldPixMap(offWorld)); SetGWorld(offWorld, NULL); thePlayer = NewMovieController(theMovie, &bounds, mcTopLeftMovie | mcNotVisible); SetMovieGWorld(theMovie, offWorld, NULL); SetMovieActive(theMovie, true); SetMovieDrawingCompleteProc(theMovie, movieDrawingCallWhenChanged, myDrawCompleteProc, (long) offWorld); GetMovieTime(theMovie, &myTimeRecord); duration = GetMovieDuration(theMovie); // Compute the number of frames for allocation of output structure frame_count = 0; while ((frame_end == 0 || frame_count < frame_end) && GetMovieTime(theMovie, NULL) < duration) { frame_count++; MCDoAction(thePlayer, mcActionStep, (Ptr) myStep); } SetMovieTime(theMovie, &myTimeRecord); // Ignore frames greater than those in the file if (frame_end == 0 || frame_count < frame_end) frame_end = frame_count; cdims[0] = frame_end - frame_start + 1; // Indices are one-based cdims[1] = 1; plhs[0] = mxCreateCellArray(2, cdims); // Step through the movie and save the frame when in the chosen interval // Note: the step size seems to be handled as a short internally. // Using anything greater than 32758 will seek to an incorrect frame frame_num = 1; while (frame_num <= frame_end) { MCDoAction(thePlayer, mcActionStep, (Ptr) myStep); if (frame_num >= frame_start) { MCIdle(thePlayer); mxSetCell(plhs[0], frame_num - frame_start, framedata); } frame_num++; } UnlockPixels(GetGWorldPixMap (offWorld)); DisposeGWorld(offWorld); DisposeMovieController (thePlayer); DisposeMovie(theMovie); DisposeMovieDrawingCompleteUPP(myDrawCompleteProc); ExitMovies(); return; }
ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) { outTracks.clear(); OSErr err = noErr; MovieAudioExtractionRef maer = NULL; auto updateResult = ProgressResult::Success; auto totSamples = (sampleCount) GetMovieDuration(mMovie); // convert from TimeValue decltype(totSamples) numSamples = 0; Boolean discrete = true; UInt32 quality = kQTAudioRenderQuality_Max; AudioStreamBasicDescription desc; UInt32 maxSampleSize; bool res = false; auto cleanup = finally( [&] { if (maer) { MovieAudioExtractionEnd(maer); } } ); CreateProgress(); do { err = MovieAudioExtractionBegin(mMovie, 0, &maer); if (err != noErr) { AudacityMessageBox(_("Unable to start QuickTime extraction")); break; } err = MovieAudioExtractionSetProperty(maer, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_RenderQuality, sizeof(quality), &quality); if (err != noErr) { AudacityMessageBox(_("Unable to set QuickTime render quality")); break; } err = MovieAudioExtractionSetProperty(maer, kQTPropertyClass_MovieAudioExtraction_Movie, kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, sizeof(discrete), &discrete); if (err != noErr) { AudacityMessageBox(_("Unable to set QuickTime discrete channels property")); break; } err = MovieAudioExtractionGetProperty(maer, kQTPropertyClass_MovieAudioExtraction_Audio, kQTAudioPropertyID_MaxAudioSampleSize, sizeof(maxSampleSize), &maxSampleSize, NULL); if (err != noErr) { AudacityMessageBox(_("Unable to get QuickTime sample size property")); break; } err = MovieAudioExtractionGetProperty(maer, kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, sizeof(desc), &desc, NULL); if (err != noErr) { AudacityMessageBox(_("Unable to retrieve stream description")); break; } auto numchan = desc.mChannelsPerFrame; const size_t bufsize = 5 * desc.mSampleRate; // determine sample format sampleFormat format; switch (maxSampleSize) { case 16: format = int16Sample; break; case 24: format = int24Sample; break; default: format = floatSample; break; } // Allocate an array of pointers, whose size is not known statically, // and prefixed with the AudioBufferList structure. MallocPtr< AudioBufferList > abl{ static_cast< AudioBufferList * >( calloc( 1, offsetof( AudioBufferList, mBuffers ) + (sizeof(AudioBuffer) * numchan))) }; abl->mNumberBuffers = numchan; TrackHolders channels{ numchan }; const auto size = sizeof(float) * bufsize; ArraysOf<unsigned char> holders{ numchan, size }; for (size_t c = 0; c < numchan; c++) { auto &buffer = abl->mBuffers[c]; auto &holder = holders[c]; auto &channel = channels[c]; buffer.mNumberChannels = 1; buffer.mDataByteSize = size; buffer.mData = holder.get(); channel = trackFactory->NewWaveTrack( format ); channel->SetRate( desc.mSampleRate ); if (numchan == 2) { if (c == 0) { channel->SetChannel(Track::LeftChannel); channel->SetLinked(true); } else if (c == 1) { channel->SetChannel(Track::RightChannel); } } } do { UInt32 flags = 0; UInt32 numFrames = bufsize; err = MovieAudioExtractionFillBuffer(maer, &numFrames, abl.get(), &flags); if (err != noErr) { AudacityMessageBox(_("Unable to get fill buffer")); break; } for (size_t c = 0; c < numchan; c++) { channels[c]->Append((char *) abl->mBuffers[c].mData, floatSample, numFrames); } numSamples += numFrames; updateResult = mProgress->Update( numSamples.as_long_long(), totSamples.as_long_long() ); if (numFrames == 0 || flags & kQTMovieAudioExtractionComplete) { break; } } while (updateResult == ProgressResult::Success); res = (updateResult == ProgressResult::Success && err == noErr); if (res) { for (const auto &channel: channels) { channel->Flush(); } outTracks.swap(channels); } // // Extract any metadata // if (res) { AddMetadata(tags); } } while (false); // done: return (res ? ProgressResult::Success : ProgressResult::Failed); }
int quicktime_player::get_duration() { return GetMovieDuration(m->movie); }
OSErr QTTarg_AddTextToggleButtonTrack (Movie theMovie) { Track myTrack = NULL; Media myMedia = NULL; MatrixRecord myMatrix; RGBColor myKeyColor; Fixed myWidth, myHeight; TimeValue myDuration = 0L; TimeValue myTimeScale = 0L; OSErr myErr = noErr; ////////// // // get some information about the target movie // ////////// if (theMovie == NULL) { myErr = paramErr; goto bail; } myWidth = Long2Fix(2 * kButtonWidth); myHeight = Long2Fix(2 * kButtonHeight); myDuration = GetMovieDuration(theMovie); myTimeScale = GetMovieTimeScale(theMovie); ////////// // // create a new sprite track in the target movie // ////////// myTrack = NewMovieTrack(theMovie, myWidth, myHeight, kNoVolume); myMedia = NewTrackMedia(myTrack, SpriteMediaType, myTimeScale, NULL, 0); // set the track matrix to compensate for any existing movie matrix GetMovieMatrix(theMovie, &myMatrix); if (InverseMatrix(&myMatrix, &myMatrix)) SetTrackMatrix(myTrack, &myMatrix); myErr = BeginMediaEdits(myMedia); if (myErr != noErr) goto bail; ////////// // // add sprite images and sprites to the sprite track; add actions to the sprites // ////////// QTTarg_AddTextButtonSamplesToMedia(myMedia, 2 * kButtonWidth, 2 * kButtonHeight, myDuration); ////////// // // insert media into track // ////////// myErr = EndMediaEdits(myMedia); if (myErr != noErr) goto bail; // add the media to the track InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1); ////////// // // set the sprite track properties // ////////// QTTarg_SetTrackProperties(myMedia, kNoQTIdleEvents); // no idle events myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff; // white MediaSetGraphicsMode(GetMediaHandler(myMedia), transparent, &myKeyColor); // make sure that the sprite track is in the frontmost layer SetTrackLayer(myTrack, kMaxLayerNumber); SetTrackLayer(myTrack, QTTarg_GetLowestLayerInMovie(theMovie) - 1); bail: return(myErr); }
Boolean QTApp_HandleMenu (UInt16 theMenuItem) { WindowObject myWindowObject = NULL; ApplicationDataHdl myAppData = NULL; MovieController myMC = NULL; Movie myMovie = NULL; Boolean myIsHandled = false; // false => allow caller to process the menu item OSErr myErr = noErr; myWindowObject = QTFrame_GetWindowObjectFromFrontWindow(); if (myWindowObject != NULL) { myMC = (**myWindowObject).fController; myMovie = (**myWindowObject).fMovie; myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject); } switch (theMenuItem) { case IDM_SET_TEXT: // put up a dialog box to get a text string QTText_SetSearchText(); myIsHandled = true; break; case IDM_FIND_TEXT: QTText_FindText(myWindowObject, gSearchText); myIsHandled = true; break; case IDM_EDIT_TEXT: QTText_EditText(myWindowObject); myIsHandled = true; break; case IDM_SEARCH_FORWARD: gSearchForward = true; myIsHandled = true; break; case IDM_SEARCH_BACKWARD: gSearchForward = false; myIsHandled = true; break; case IDM_WRAP_SEARCH: gSearchWrap = !gSearchWrap; myIsHandled = true; break; case IDM_USE_CASE: gSearchWithCase = !gSearchWithCase; myIsHandled = true; break; case IDM_ADD_TEXT_TRACK: { // add a text track to the specified movie; // for purposes of illustration, we'll add 11 (count 'em!) text strings to the movie, // with each string occupying about one-tenth of the movie duration Track myTypeTrack = NULL; Track myTextTrack = NULL; TimeValue myMovieDuration = 0; TimeValue mySampleDuration = 0; char *myStrings[] = {"0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"}; short myFrames[11]; Boolean isChapter = true; myTypeTrack = GetMovieIndTrackType(myMovie, 1, VideoMediaType, movieTrackMediaType); if (myTypeTrack == NULL) break; // get the duration of the movie and the duration of a single frame; // this tells us how many frames fit into one-tenth of the movie myMovieDuration = GetMovieDuration(myMovie); mySampleDuration = QTUtils_GetFrameDuration(myTypeTrack); myFrames[0] = (myMovieDuration / mySampleDuration) / 10; myFrames[1] = (myMovieDuration / mySampleDuration) / 10; myFrames[2] = (myMovieDuration / mySampleDuration) / 10; myFrames[3] = (myMovieDuration / mySampleDuration) / 10; myFrames[4] = (myMovieDuration / mySampleDuration) / 10; myFrames[5] = (myMovieDuration / mySampleDuration) / 10; myFrames[6] = (myMovieDuration / mySampleDuration) / 10; myFrames[7] = (myMovieDuration / mySampleDuration) / 10; myFrames[8] = (myMovieDuration / mySampleDuration) / 10; myFrames[9] = ((myMovieDuration / mySampleDuration) / 10) - 1; myFrames[10] = 1; myTextTrack = QTText_AddTextTrack(myMovie, myStrings, myFrames, 11, VideoMediaType, isChapter); if (myTextTrack != NULL) { MCMovieChanged(myMC, myMovie); // stamp the movie as dirty and update our saved data (**myWindowObject).fIsDirty = true; (**myAppData).fMovieHasText = true; (**myAppData).fTextIsChapter = isChapter; (**myAppData).fTextTrack = myTextTrack; (**myAppData).fTextHandler = GetMediaHandler(GetTrackMedia(myTextTrack)); } } myIsHandled = true; break; case IDM_CUT_TEXT_TRACK: // remove all existing text tracks from the specified movie myErr = QTText_RemoveIndTextTrack(myWindowObject, kAllTextTracks); if (myErr == noErr) { MCMovieChanged(myMC, myMovie); // stamp the movie as dirty and update our saved data (**myWindowObject).fIsDirty = true; (**myAppData).fMovieHasText = false; (**myAppData).fTextIsChapter = false; (**myAppData).fTextIsHREF = false; (**myAppData).fTextTrack = NULL; (**myAppData).fTextHandler = NULL; } myIsHandled = true; break; case IDM_CHAPTER_TRACK: (**myAppData).fTextIsChapter = !(**myAppData).fTextIsChapter; QTText_SetTextTrackAsChapterTrack(myWindowObject, VideoMediaType, (**myAppData).fTextIsChapter); (**myWindowObject).fIsDirty = true; myIsHandled = true; break; case IDM_HREF_TRACK: (**myAppData).fTextIsHREF = !(**myAppData).fTextIsHREF; QTText_SetTextTrackAsHREFTrack((**myAppData).fTextTrack, (**myAppData).fTextIsHREF); (**myWindowObject).fIsDirty = true; myIsHandled = true; break; default: break; } // switch (theMenuItem) return(myIsHandled); }
/* * PsychQTCreateMovie() -- Create a movie object. * * This function tries to open a Quicktime-Moviefile and create an * associated movie object for it. * * win = Pointer to window record of associated onscreen window. * moviename = char* with the name of the moviefile. * preloadSecs = How many seconds of the movie should be preloaded/prefetched into RAM at movie open time? * moviehandle = handle to the new movie. */ void PsychQTCreateMovie(PsychWindowRecordType *win, const char* moviename, double preloadSecs, int* moviehandle) { Movie theMovie = NULL; QTVisualContextRef QTMovieContext = NULL; QTAudioContextRef QTAudioContext = NULL; int i, slotid; OSErr error; CFStringRef movieLocation; CFURLRef movieURLLocation; CFStringRef coreAudioDeviceUID; psych_bool trueValue = TRUE; QTNewMoviePropertyElement newMovieProperties[4] = {0}; int propcount = 0; char msgerr[10000]; char errdesc[1000]; Rect movierect; psych_bool printErrors; // Suppress output of error-messages if moviehandle == 1000. That means we // run in our own Posix-Thread, not in the Matlab-Thread. Printing via Matlabs // printing facilities would likely cause a terrible crash. printErrors = (*moviehandle == -1000) ? FALSE : TRUE; // Set movie handle to "failed" initially: *moviehandle = -1; // We startup the Quicktime subsystem only on first invocation. if (firsttime) { #if PSYCH_SYSTEM == PSYCH_WINDOWS // Initialize Quicktime for Windows compatibility layer: This will fail if // QT isn't installed on the Windows machine... error = InitializeQTML(0); if (error!=noErr) { if (printErrors) { PsychErrorExitMsg(PsychError_internal, "Quicktime Media Layer initialization failed: Quicktime not properly installed?!?"); } else return; } #endif // Initialize Quicktime-Subsystem: error = EnterMovies(); if (error!=noErr) { if (printErrors) PsychErrorExitMsg(PsychError_internal, "Quicktime EnterMovies() failed!!!"); else return; } firsttime = FALSE; } if (!PsychIsOnscreenWindow(win)) { if (printErrors) PsychErrorExitMsg(PsychError_user, "Provided windowPtr is not an onscreen window."); else return; } if (NULL==moviename) { if (printErrors) PsychErrorExitMsg(PsychError_internal, "NULL-Ptr instead of moviename passed!"); else return; } if (numMovieRecords >= PSYCH_MAX_MOVIES) { *moviehandle = -2; if (printErrors) PsychErrorExitMsg(PsychError_user, "Allowed maximum number of simultaneously open movies exceeded!"); else return; } // Search first free slot in movieRecordBANK: for (i=0; (i < PSYCH_MAX_MOVIES) && (movieRecordBANK[i].theMovie); i++); if (i>=PSYCH_MAX_MOVIES) { *moviehandle = -2; if (printErrors) PsychErrorExitMsg(PsychError_user, "Allowed maximum number of simultaneously open movies exceeded!"); else return; } // Slot slotid will contain the movie record for our new movie object: slotid=i; // Create name-string for moviename: movieLocation = CFStringCreateWithCString (kCFAllocatorDefault, moviename, kCFStringEncodingASCII); // Zero-out new record in moviebank: movieRecordBANK[slotid].theMovie=NULL; movieRecordBANK[slotid].QTMovieContext=NULL; movieRecordBANK[slotid].QTAudioContext=NULL; movieRecordBANK[slotid].QTMovieGWorld=NULL; if (!PSYCH_USE_QT_GWORLDS) { // Create QTGLTextureContext: #if PSYCH_SYSTEM != PSYCH_WINDOWS error = QTOpenGLTextureContextCreate (kCFAllocatorDefault, win->targetSpecific.contextObject, win->targetSpecific.pixelFormatObject, NULL, &QTMovieContext); #endif if (error!=noErr) { if (printErrors) PsychErrorExitMsg(PsychError_internal, "OpenGL Quicktime visual context creation failed!!!"); else return; } } // The Movie location newMovieProperties[propcount].propClass = kQTPropertyClass_DataLocation; if (strstr(moviename, "http:") || strstr(moviename, "ftp:")) { // Open movie from URL, e.g., http- or ftp- server: movieURLLocation = CFURLCreateWithString(kCFAllocatorDefault, movieLocation, NULL); newMovieProperties[propcount].propID = kQTDataLocationPropertyID_CFURL; newMovieProperties[propcount].propValueSize = sizeof(movieURLLocation); newMovieProperties[propcount++].propValueAddress = (void*) &movieURLLocation; } else { // Open movie file from filesystem: newMovieProperties[propcount].propID = kQTDataLocationPropertyID_CFStringPosixPath; newMovieProperties[propcount].propValueSize = sizeof(CFStringRef); newMovieProperties[propcount++].propValueAddress = &movieLocation; } if (!PSYCH_USE_QT_GWORLDS) { // The Movie visual context newMovieProperties[propcount].propClass = kQTPropertyClass_Context; newMovieProperties[propcount].propID = kQTContextPropertyID_VisualContext; newMovieProperties[propcount].propValueSize = sizeof(QTVisualContextRef); newMovieProperties[propcount++].propValueAddress = &QTMovieContext; } if (TRUE) { // Create QTAudioContext for default CoreAudio device: coreAudioDeviceUID = NULL; // Use default audio-output device. error =QTAudioContextCreateForAudioDevice (kCFAllocatorDefault, coreAudioDeviceUID, NULL, &QTAudioContext); if (error!=noErr) { if (printErrors) PsychErrorExitMsg(PsychError_internal, "Quicktime audio context creation failed!!!"); else return; } // The Movie audio context newMovieProperties[propcount].propClass = kQTPropertyClass_Context; newMovieProperties[propcount].propID = kQTContextPropertyID_AudioContext; newMovieProperties[propcount].propValueSize = sizeof(QTAudioContextRef); newMovieProperties[propcount++].propValueAddress = &QTAudioContext; } // The Movie active newMovieProperties[propcount].propClass = kQTPropertyClass_NewMovieProperty; newMovieProperties[propcount].propID = kQTNewMoviePropertyID_Active; newMovieProperties[propcount].propValueSize = sizeof(trueValue); newMovieProperties[propcount++].propValueAddress = &trueValue; // Instantiate the Movie error = NewMovieFromProperties(propcount, newMovieProperties, 0, NULL, &theMovie); if (error!=noErr) { QTVisualContextRelease(QTMovieContext); QTAudioContextRelease(QTAudioContext); switch(error) { case -2000: case -50: case -43: sprintf(errdesc, "File not found."); break; case -2048: sprintf(errdesc, "This is not a file that Quicktime understands."); break; case -2003: sprintf(errdesc, "Can't find media handler (codec) for this movie."); break; default: sprintf(errdesc, "Unknown: Check http://developer.apple.com/documentation/QuickTime/APIREF/ErrorCodes.htm#//apple_ref/doc/constant_group/Error_Codes"); } sprintf(msgerr, "Couldn't load movie %s! Quicktime error code %i [%s]", moviename, (int) error, errdesc); *moviehandle = (int) error; if (printErrors) PsychErrorExitMsg(PsychError_user, msgerr); else return; } CFRelease(movieLocation); if (PSYCH_USE_QT_GWORLDS) { // Determine size of images in movie: GetMovieBox(theMovie, &movierect); // Only create a GWorld if movie frames contain at least 1 pixel. This way we skip GWorld // setup on "movies" which only consist of sound tracks. if ((movierect.right - movierect.left != 0) && (movierect.bottom - movierect.top != 0)) { // Create GWorld for this movie object: // error = QTNewGWorld(&movieRecordBANK[slotid].QTMovieGWorld, k32ABGRPixelFormat, &movierect, NULL, NULL, 0); error = QTNewGWorld(&movieRecordBANK[slotid].QTMovieGWorld, 0, &movierect, NULL, NULL, 0); if (error!=noErr) { QTAudioContextRelease(QTAudioContext); DisposeMovie(movieRecordBANK[slotid].theMovie); movieRecordBANK[slotid].theMovie=NULL; if (printErrors) PsychErrorExitMsg(PsychError_internal, "Quicktime GWorld creation failed!!!"); else return; } // Attach this GWorld as rendering target for Quicktime: SetMovieGWorld(theMovie, movieRecordBANK[slotid].QTMovieGWorld, NULL); } } // Preload preloadSecs seconds of movie into system RAM for faster playback: if (preloadSecs > 0) LoadMovieIntoRam(theMovie, 0, ((long) preloadSecs + 0.5) * GetMovieTimeScale(theMovie), keepInRam); // Special setting - 1 means: Load whole movie into RAM: if (preloadSecs == -1) LoadMovieIntoRam(theMovie, 0, GetMovieDuration(theMovie), keepInRam); // We don't preroll: Didn't help for async playback, but leads to failure in // manual playback mode: PrerollMovie(theMovie, 0, FloatToFixed(1)); // MoviesTask() it to make sure start of plaback will be as stutter-free as possible: MoviesTask(theMovie, 10000); // Assign new record in moviebank: movieRecordBANK[slotid].theMovie=theMovie; movieRecordBANK[slotid].QTMovieContext=QTMovieContext; movieRecordBANK[slotid].QTAudioContext=QTAudioContext; movieRecordBANK[slotid].loopflag = 0; *moviehandle = slotid; // Increase counter: numMovieRecords++; // Compute basic movie properties - Duration and fps as well as image size: // Compute duration in seconds: movieRecordBANK[slotid].movieduration = (double) GetMovieDuration(theMovie) / (double) GetMovieTimeScale(theMovie); // Compute expected framerate, assuming a linear spacing between frames: It is derived as // reciprocal of the duration of the first video frame in the movie: movieRecordBANK[slotid].fps = PsychDetermineMovieFramecountAndFps(theMovie, NULL); // Determine size of images in movie: GetMovieBox(theMovie, &movierect); movieRecordBANK[slotid].width = movierect.right - movierect.left; movieRecordBANK[slotid].height = movierect.bottom - movierect.top; // We set nrframes == -1 to indicate that this value is not yet available. // Will do counting on first query for this parameter as it is very time-consuming: movieRecordBANK[slotid].nrframes = -1; return; }
void QTCmpr_CompressSequence (WindowObject theWindowObject) { ComponentInstance myComponent = NULL; GWorldPtr myImageWorld = NULL; // the graphics world we draw the images in PixMapHandle myPixMap = NULL; Movie mySrcMovie = NULL; Track mySrcTrack = NULL; Movie myDstMovie = NULL; Track myDstTrack = NULL; Media myDstMedia = NULL; Rect myRect; PicHandle myPicture = NULL; CGrafPtr mySavedPort = NULL; GDHandle mySavedDevice = NULL; SCTemporalSettings myTimeSettings; SCDataRateSettings myRateSettings; FSSpec myFile; Boolean myIsSelected = false; Boolean myIsReplacing = false; short myRefNum = -1; StringPtr myMoviePrompt = QTUtils_ConvertCToPascalString(kQTCSaveMoviePrompt); StringPtr myMovieFileName = QTUtils_ConvertCToPascalString(kQTCSaveMovieFileName); MatrixRecord myMatrix; ImageDescriptionHandle myImageDesc = NULL; TimeValue myCurMovieTime = 0L; TimeValue myOrigMovieTime = 0L; // current movie time, when compression is begun short myFrameNum; long myFlags = 0L; long myNumFrames = 0L; long mySrcMovieDuration = 0L; // duration of source movie OSErr myErr = noErr; #if USE_ASYNC_COMPRESSION ICMCompletionProcRecord myICMComplProcRec; ICMCompletionProcRecordPtr myICMComplProcPtr = NULL; OSErr myICMComplProcErr = noErr; myICMComplProcRec.completionProc = NULL; myICMComplProcRec.completionRefCon = 0L; #endif if (theWindowObject == NULL) goto bail; ////////// // // get the movie and the first video track in the movie // ////////// mySrcMovie = (**theWindowObject).fMovie; if (mySrcMovie == NULL) goto bail; mySrcTrack = GetMovieIndTrackType(mySrcMovie, 1, VideoMediaType, movieTrackMediaType); if (mySrcTrack == NULL) goto bail; // stop the movie; we don't want it to be playing while we're (re)compressing it SetMovieRate(mySrcMovie, (Fixed)0L); // get the current movie time, when compression is begun; we'll restore this later myOrigMovieTime = GetMovieTime(mySrcMovie, NULL); ////////// // // configure and display the Standard Image Compression dialog box // ////////// // open an instance of the Standard Image Compression dialog component myComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType); if (myComponent == NULL) goto bail; // turn off "best depth" option in the compression dialog, because all of our // buffering is done at 32-bits (regardless of the depth of the source data) // // a more ambitious approach would be to loop through each of the video sample // descriptions in each of the video tracks looking for the deepest depth, and // using that for the best depth; better yet, we could find out which compressors // were used and set one of those as the default in the compression dialog SCGetInfo(myComponent, scPreferenceFlagsType, &myFlags); myFlags &= ~scShowBestDepth; SCSetInfo(myComponent, scPreferenceFlagsType, &myFlags); // because we are recompressing a movie that may have a variable frame rate, // we want to allow the user to leave the frame rate text field blank (in which // case we can preserve the frame durations of the source movie); if the user // enters a number, we will resample the movie at a new frame rate; if we don't // clear this flag, the compression dialog will not allow zero in the frame rate field // // NOTE: we could have set this flag above when we cleared the scShowBestDepth flag; // it is done here for clarity. SCGetInfo(myComponent, scPreferenceFlagsType, &myFlags); myFlags |= scAllowZeroFrameRate; SCSetInfo(myComponent, scPreferenceFlagsType, &myFlags); // get the number of video frames in the movie myNumFrames = QTUtils_GetFrameCount(mySrcTrack); // get the bounding rectangle of the movie, create a 32-bit GWorld with those // dimensions, and draw the movie poster picture into it; this GWorld will be // used for the test image in the compression dialog box and for rendering movie // frames myPicture = GetMoviePosterPict(mySrcMovie); if (myPicture == NULL) goto bail; GetMovieBox(mySrcMovie, &myRect); myErr = NewGWorld(&myImageWorld, 32, &myRect, NULL, NULL, 0L); if (myErr != noErr) goto bail; // get the pixmap of the GWorld; we'll lock the pixmap, just to be safe myPixMap = GetGWorldPixMap(myImageWorld); if (!LockPixels(myPixMap)) goto bail; // draw the movie poster image into the GWorld GetGWorld(&mySavedPort, &mySavedDevice); SetGWorld(myImageWorld, NULL); EraseRect(&myRect); DrawPicture(myPicture, &myRect); KillPicture(myPicture); SetGWorld(mySavedPort, mySavedDevice); // set the picture to be displayed in the dialog box; passing NULL for the rect // means use the entire image; passing 0 for the flags means to use the default // system method of displaying the test image, which is currently a combination // of cropping and scaling; personally, I prefer scaling (your mileage may vary) SCSetTestImagePixMap(myComponent, myPixMap, NULL, scPreferScaling); // install the custom procs, if requested // we can install two kinds of custom procedures for use in connection with // the standard dialog box: (1) a modal-dialog filter function, and (2) a hook // function to handle the custom button in the dialog box if (gUseExtendedProcs) QTCmpr_InstallExtendedProcs(myComponent, (long)myPixMap); // set up some default settings for the compression dialog SCDefaultPixMapSettings(myComponent, myPixMap, true); // clear out the default frame rate chosen by Standard Compression (a frame rate // of 0 means to use the rate of the source movie) myErr = SCGetInfo(myComponent, scTemporalSettingsType, &myTimeSettings); if (myErr != noErr) goto bail; myTimeSettings.frameRate = 0; SCSetInfo(myComponent, scTemporalSettingsType, &myTimeSettings); // request image compression settings from the user; in other words, put up the dialog box myErr = SCRequestSequenceSettings(myComponent); if (myErr == scUserCancelled) goto bail; // get a copy of the temporal settings the user entered; we'll need them for some // of our calculations (in a simpler application, we'd never have to look at them) SCGetInfo(myComponent, scTemporalSettingsType, &myTimeSettings); ////////// // // adjust the data rate [to be supplied][relevant only for movies that have sound tracks] // ////////// ////////// // // adjust the sample count // // if the user wants to resample the frame rate of the movie (as indicated a non-zero // value in the frame rate field) calculate the number of frames and duration for the new movie // ////////// if (myTimeSettings.frameRate != 0) { long myDuration = GetMovieDuration(mySrcMovie); long myTimeScale = GetMovieTimeScale(mySrcMovie); float myFloat = (float)myDuration * myTimeSettings.frameRate; myNumFrames = myFloat / myTimeScale / 65536; if (myNumFrames == 0) myNumFrames = 1; } ////////// // // get the name and location of the new movie file // ////////// // prompt the user for a file to put the compressed image into; in theory, the name // should have a file extension appropriate to the type of compressed data selected by the user; // this is left as an exercise for the reader QTFrame_PutFile(myMoviePrompt, myMovieFileName, &myFile, &myIsSelected, &myIsReplacing); if (!myIsSelected) goto bail; // delete any existing file of that name if (myIsReplacing) { myErr = DeleteMovieFile(&myFile); if (myErr != noErr) goto bail; } ////////// // // create the target movie // ////////// myErr = CreateMovieFile(&myFile, sigMoviePlayer, smSystemScript, createMovieFileDeleteCurFile | createMovieFileDontCreateResFile, &myRefNum, &myDstMovie); if (myErr != noErr) goto bail; // create a new video movie track with the same dimensions as the entire source movie myDstTrack = NewMovieTrack(myDstMovie, (long)(myRect.right - myRect.left) << 16, (long)(myRect.bottom - myRect.top) << 16, kNoVolume); if (myDstTrack == NULL) goto bail; // create a media for the new track with the same time scale as the source movie; // because the time scales are the same, we don't have to do any time scale conversions. myDstMedia = NewTrackMedia(myDstTrack, VIDEO_TYPE, GetMovieTimeScale(mySrcMovie), 0, 0); if (myDstMedia == NULL) goto bail; // copy the user data and settings from the source to the dest movie CopyMovieSettings(mySrcMovie, myDstMovie); // set movie matrix to identity and clear the movie clip region (because the conversion // process transforms and composites all video tracks into one untransformed video track) SetIdentityMatrix(&myMatrix); SetMovieMatrix(myDstMovie, &myMatrix); SetMovieClipRgn(myDstMovie, NULL); // set the movie to highest quality imaging SetMoviePlayHints(mySrcMovie, hintsHighQuality, hintsHighQuality); myImageDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription)); if (myImageDesc == NULL) goto bail; // prepare for adding frames to the movie myErr = BeginMediaEdits(myDstMedia); if (myErr != noErr) goto bail; ////////// // // compress the image sequence // // we are going to step through the source movie, compress each frame, and then add // the compressed frame to the destination movie // ////////// myErr = SCCompressSequenceBegin(myComponent, myPixMap, NULL, &myImageDesc); if (myErr != noErr) goto bail; #if USE_ASYNC_COMPRESSION myFlags = codecFlagUpdatePrevious + codecFlagUpdatePreviousComp + codecFlagLiveGrab; SCSetInfo(myComponent, scCodecFlagsType, &myFlags); #endif // clear out our image GWorld and set movie to draw into it SetGWorld(myImageWorld, NULL); EraseRect(&myRect); SetMovieGWorld(mySrcMovie, myImageWorld, GetGWorldDevice(myImageWorld)); // set current time value to beginning of the source movie myCurMovieTime = 0; // get a value we'll need inside the loop mySrcMovieDuration = GetMovieDuration(mySrcMovie); // loop through all of the interesting times we counted above for (myFrameNum = 0; myFrameNum < myNumFrames; myFrameNum++) { short mySyncFlag; TimeValue myDuration; long myDataSize; Handle myCompressedData; ////////// // // get the next frame of the source movie // ////////// // if we are resampling the movie, step to the next frame if (myTimeSettings.frameRate) { myCurMovieTime = myFrameNum * mySrcMovieDuration / (myNumFrames - 1); myDuration = mySrcMovieDuration / myNumFrames; } else { OSType myMediaType = VIDEO_TYPE; myFlags = nextTimeMediaSample; // if this is the first frame, include the frame we are currently on if (myFrameNum == 0) myFlags |= nextTimeEdgeOK; // if we are maintaining the frame durations of the source movie, // skip to the next interesting time and get the duration for that frame GetMovieNextInterestingTime(mySrcMovie, myFlags, 1, &myMediaType, myCurMovieTime, 0, &myCurMovieTime, &myDuration); } SetMovieTimeValue(mySrcMovie, myCurMovieTime); MoviesTask(mySrcMovie, 0); MoviesTask(mySrcMovie, 0); MoviesTask(mySrcMovie, 0); // if data rate constraining is being done, tell Standard Compression the // duration of the current frame in milliseconds; we only need to do this // if the frames have variable durations if (!SCGetInfo(myComponent, scDataRateSettingsType, &myRateSettings)) { myRateSettings.frameDuration = myDuration * 1000 / GetMovieTimeScale(mySrcMovie); SCSetInfo(myComponent, scDataRateSettingsType, &myRateSettings); } ////////// // // compress the current frame of the source movie and add it to the destination movie // ////////// // if SCCompressSequenceFrame completes successfully, myCompressedData will hold // a handle to the newly-compressed image data and myDataSize will be the size of // the compressed data (which will usually be different from the size of the handle); // also mySyncFlag will be a value that that indicates whether or not the frame is a // key frame (and which we pass directly to AddMediaSample); note that we do not need // to dispose of myCompressedData, since SCCompressSequenceEnd will do that for us #if !USE_ASYNC_COMPRESSION myErr = SCCompressSequenceFrame(myComponent, myPixMap, &myRect, &myCompressedData, &myDataSize, &mySyncFlag); if (myErr != noErr) goto bail; #else if (myICMComplProcPtr == NULL) { myICMComplProcRec.completionProc = NewICMCompletionProc(QTCmpr_CompletionProc); myICMComplProcRec.completionRefCon = (long)&myICMComplProcErr; myICMComplProcPtr = &myICMComplProcRec; } myICMComplProcErr = kAsyncDefaultValue; myErr = SCCompressSequenceFrameAsync(myComponent, myPixMap, &myRect, &myCompressedData, &myDataSize, &mySyncFlag, myICMComplProcPtr); if (myErr != noErr) goto bail; // spin our wheels while we're waiting for the compress call to complete while (myICMComplProcErr == kAsyncDefaultValue) { EventRecord myEvent; WaitNextEvent(0, &myEvent, 60, NULL); SCAsyncIdle(myComponent); } myErr = myICMComplProcErr; #endif myErr = AddMediaSample(myDstMedia, myCompressedData, 0, myDataSize, myDuration, (SampleDescriptionHandle)myImageDesc, 1, mySyncFlag, NULL); if (myErr != noErr) goto bail; } // close the compression sequence; this will dispose of the image description // and compressed data handles allocated by SCCompressSequenceBegin SCCompressSequenceEnd(myComponent); ////////// // // add the media data to the destination movie // ////////// myErr = EndMediaEdits(myDstMedia); if (myErr != noErr) goto bail; InsertMediaIntoTrack(myDstTrack, 0, 0, GetMediaDuration(myDstMedia), fixed1); // add the movie resource to the dst movie file. myErr = AddMovieResource(myDstMovie, myRefNum, NULL, NULL); if (myErr != noErr) goto bail; // flatten the movie data [to be supplied] // close the movie file CloseMovieFile(myRefNum); bail: // close the Standard Compression component if (myComponent != NULL) CloseComponent(myComponent); if (mySrcMovie != NULL) { // restore the source movie's original graphics port and device SetMovieGWorld(mySrcMovie, mySavedPort, mySavedDevice); // restore the source movie's original movie time SetMovieTimeValue(mySrcMovie, myOrigMovieTime); } // restore the original graphics port and device SetGWorld(mySavedPort, mySavedDevice); // delete the GWorld we were drawing frames into if (myImageWorld != NULL) DisposeGWorld(myImageWorld); #if USE_ASYNC_COMPRESSION if (myICMComplProcRec.completionProc != NULL) DisposeICMCompletionUPP(myICMComplProcRec.completionProc); #endif free(myMoviePrompt); free(myMovieFileName); }