diff --git a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj
index 5c023739e..730247409 100644
--- a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj
+++ b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj
@@ -1538,6 +1538,14 @@
+
+
+
+
timecodes = IndexFile();
+ if (timecodes.size() == 0)
+ throw wxString(_T("QuickTime video provider: failed to index file"));
+
+ // ask about vfr override etc
+ vfr_fps.SetVFR(timecodes);
+ int override_tc = wxYES;
+ if (VFR_Output.IsLoaded()) {
+ override_tc = wxMessageBox(_("You already have timecodes loaded. Would you like to replace them with timecodes from the video file?"), _("Replace timecodes?"), wxYES_NO | wxICON_QUESTION);
+ if (override_tc == wxYES) {
+ VFR_Input.SetVFR(timecodes);
+ VFR_Output.SetVFR(timecodes);
+ }
+ } else { // no timecodes loaded, go ahead and apply
+ VFR_Input.SetVFR(timecodes);
+ VFR_Output.SetVFR(timecodes);
+ }
+
+ // set assumed "cfr" fps (dunno if this is actually used anywhere)
+ double len_s = (double)GetMovieDuration(movie) / (double)GetMovieTimeScale(movie);
+ assumed_fps = (double)num_frames / len_s;
+
+ cur_fn = 0;
+}
+
+
+std::vector QuickTimeVideoProvider::IndexFile() {
+ TimeScale scale = GetMovieTimeScale(movie);
+ OSType v_type[1];
+ v_type[0] = VisualMediaCharacteristic;
+ std::vector timecodes;
+ std::map timestamp_map; // TODO: just do a binary search instead
+
+ int framecount = 1;
+ TimeValue cur_timeval = 0;
+
+ // get the first frame
+ GetMovieNextInterestingTime(movie, nextTimeMediaSample + nextTimeEdgeOK, 1, v_type, cur_timeval, 0, &cur_timeval, NULL);
+ keyframes.push_back(0); // interesting assumption?
+
+ // first, find timestamps and count frames
+ while (cur_timeval >= 0) {
+ qt_timestamps.push_back(cur_timeval);
+ timestamp_map.insert(std::pair(cur_timeval, framecount));
+ timecodes.push_back((cur_timeval * 1000) / scale);
+ framecount++;
+ GetMovieNextInterestingTime(movie, nextTimeMediaSample, 1, v_type, cur_timeval, 0, &cur_timeval, NULL);
+ }
+ // GetMovieNextInterestingTime() returns -1 when there are no more interesting times,
+ // so we incremented framecount once too much
+ num_frames = --framecount;
+
+
+ // next, find keyframes
+ cur_timeval = 0;
+ while (cur_timeval >= 0) {
+ GetMovieNextInterestingTime(movie, nextTimeSyncSample, 1, v_type, cur_timeval, 0, &cur_timeval, NULL);
+ keyframes.push_back(timestamp_map[cur_timeval]);
+ }
+
+ return timecodes;
+}
+
+
+const AegiVideoFrame QuickTimeVideoProvider::GetFrame(int n) {
+ if (n < 0)
+ n = 0;
+ if (n >= num_frames)
+ n = num_frames-1;
+ cur_fn = n;
+
+ // seek
+ SetMovieTimeValue(movie, qt_timestamps[n]);
+ qt_err = GetMoviesError();
+ QTCheckError(qt_err, wxString::Format(_T("QuickTime video provider: failed to seek to TimeValue %d"), qt_timestamps[n]));
+
+ // render to offscreen buffer
+ qt_err = UpdateMovie(movie);
+ QTCheckError(qt_err, wxString(_T("QuickTime video provider: failed to render frame")));
+ MoviesTask(movie, 0L);
+ qt_err = GetMoviesError();
+ QTCheckError(qt_err, wxString(_T("QuickTime video provider: failed to render frame")));
+
+ // set up destination
+ AegiVideoFrame dst_frame;
+ dst_frame.format = FORMAT_RGB32;
+ dst_frame.w = w;
+ dst_frame.h = h;
+ dst_frame.invertChannels = true;
+ dst_frame.flipped = false;
+ dst_frame.pitch[0] = w * 4; // 4 bytes per sample
+ dst_frame.Allocate();
+
+ // copy data from offscreen buffer
+ Ptr src_ptr8 = GetPixBaseAddr(GetGWorldPixMap(gw));
+ uint32_t *src_ptr = reinterpret_cast(src_ptr8);
+ uint32_t *dst_ptr = reinterpret_cast(dst_frame.data[0]);
+ for (int i=0; i<(w*h); i++)
+ // swap endian if needed; quickdraw always renders big-endian
+ *dst_ptr++ = Endian::BigToMachine(*src_ptr++); // fun with pointers!
+
+ return dst_frame;
+}
+
+
+
+
+///////////////
+// Utility functions
+
+void QuickTimeVideoProvider::QTCheckError(OSErr err, wxString errmsg) {
+ if (err != noErr)
+ throw errmsg;
+ /* CheckError(err, errmsg.c_str()); // I wonder if this actually works on Mac, and if so, what it does */
+}
+
+int QuickTimeVideoProvider::GetWidth() {
+ return w;
+}
+
+int QuickTimeVideoProvider::GetHeight() {
+ return h;
+}
+
+int QuickTimeVideoProvider::GetFrameCount() {
+ return num_frames;
+}
+
+int QuickTimeVideoProvider::GetPosition() {
+ return cur_fn;
+}
+
+double QuickTimeVideoProvider::GetFPS() {
+ return assumed_fps;
+}
+
+bool QuickTimeVideoProvider::AreKeyFramesLoaded() {
+ if (keyframes.GetCount() > 0)
+ return true;
+ else
+ return false;
+}
+
+wxArrayInt QuickTimeVideoProvider::GetKeyFrames() {
+ return keyframes;
+}
+
+FrameRate QuickTimeVideoProvider::GetTrueFrameRate() {
+ return vfr_fps;
+}
+
+
+#endif /* WITH_QUICKTIME */
diff --git a/aegisub/src/video_provider_quicktime.h b/aegisub/src/video_provider_quicktime.h
new file mode 100644
index 000000000..87736ea31
--- /dev/null
+++ b/aegisub/src/video_provider_quicktime.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2009, Karl Blomster
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of the Aegisub Group nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// AEGISUB
+//
+// Website: http://aegisub.cellosoft.com
+// Contact: mailto:zeratul@cellosoft.com
+//
+
+
+#pragma once
+
+#include "config.h"
+#include
+
+#ifdef WITH_QUICKTIME
+
+#ifdef _MSC_VER
+// avoid conflicts between MSVC's stdint.h and QT's stdint.h
+#define _STDINT_H
+// get MSVC to shut up about a macro redefinition in QT's ConditionalMacros.h
+#pragma warning(disable: 4004)
+#endif
+
+#ifndef WIN32
+#define MacOffsetRect OffsetRect
+#endif
+
+
+#include "include/aegisub/video_provider.h"
+extern "C" {
+#ifdef WIN32
+#include
+#include
+#include
+#include
+#else
+#include // not sure about this path, someone on mac needs to test it
+#endif
+}
+#include
+#include
+#include
+#include