diff --git a/aegisub/video_provider.cpp b/aegisub/video_provider.cpp
index 7975454b4c6886ab6fadca0d2f345c8336db3079..e0bcd12000e0a6c4e1705439b8a1c392adb0c0de 100644
--- a/aegisub/video_provider.cpp
+++ b/aegisub/video_provider.cpp
@@ -50,105 +50,7 @@
 #include "video_provider_lavc.h"
 #endif
 #include "video_provider_dummy.h"
-
-
-///////////////
-// Constructor
-VideoProvider::VideoProvider() {
-	cacheMax = 0;
-}
-
-
-//////////////
-// Destructor
-VideoProvider::~VideoProvider() {
-	ClearCache();
-	tempRGBFrame.Clear();
-}
-
-
-/////////////
-// Get frame
-const AegiVideoFrame VideoProvider::GetFrame(int n,int format) {
-	// See if frame is cached
-	CachedFrame cached;
-	for (std::list<CachedFrame>::iterator cur=cache.begin();cur!=cache.end();cur++) {
-		cached = *cur;
-		if (cached.n == n) {
-			cache.erase(cur);
-			cache.push_back(cached);
-			return cached.frame;
-		}
-	}
-
-	// Not cached, retrieve it
-	const AegiVideoFrame frame = DoGetFrame(n);
-	const AegiVideoFrame *srcFrame = &frame;
-
-	// Convert to compatible format
-	if (!(frame.format & format)) {
-		if (format & FORMAT_RGB32) tempRGBFrame.format = FORMAT_RGB32;
-		else throw _T("Unable to negotiate video frame format.");
-		tempRGBFrame.w = frame.w;
-		tempRGBFrame.h = frame.h;
-		tempRGBFrame.pitch[0] = frame.w * 4;
-		tempRGBFrame.ConvertFrom(frame);
-		srcFrame = &tempRGBFrame;
-	}
-
-	// Cache frame
-	Cache(n,*srcFrame);
-	return *srcFrame;
-}
-
-
-////////////////
-// Get as float
-void VideoProvider::GetFloatFrame(float* buffer, int n) {
-	const AegiVideoFrame frame = GetFrame(n);
-	frame.GetFloat(buffer);
-}
-
-
-//////////////////////////
-// Set maximum cache size
-void VideoProvider::SetCacheMax(int n) {
-	if (n < 0) n = 0;
-	cacheMax = n;
-}
-
-
-////////////////
-// Add to cache
-void VideoProvider::Cache(int n,const AegiVideoFrame frame) {
-	// Cache enabled?
-	if (cacheMax == 0) return;
-
-	// Cache full, use frame at front
-	if (cache.size() >= cacheMax) {
-		cache.push_back(cache.front());
-		cache.pop_front();
-	}
-
-	// Cache not full, insert new one
-	else {
-		cache.push_back(CachedFrame());
-	}
-
-	// Cache
-	cache.front().n = n;
-	cache.front().frame.CopyFrom(frame);
-}
-
-
-///////////////
-// Clear cache
-void VideoProvider::ClearCache() {
-	while (cache.size()) {
-		cache.front().frame.Clear();
-		cache.pop_front();
-	}
-}
+#include "video_provider_cache.h"
 
 
 ////////////////
@@ -175,8 +77,15 @@ VideoProvider *VideoProviderFactory::GetProvider(wxString video,double fps) {
 	wxString error;
 	for (unsigned int i=0;i<list.Count();i++) {
 		try {
+			// Create provider
 			VideoProvider *provider = GetFactory(list[i])->CreateProvider(video,fps);
-			if (provider) return provider;
+			if (provider) {
+				// Cache if necessary
+				if (provider->GetDesiredCacheSize()) {
+					provider = new VideoProviderCache(provider);
+				}
+				return provider;
+			}
 		}
 		catch (wxString err) { error += list[i] + _T(" factory: ") + err + _T("\n"); }
 		catch (const wxChar *err) { error += list[i] + _T(" factory: ") + wxString(err) + _T("\n"); }
diff --git a/aegisub/video_provider.h b/aegisub/video_provider.h
index 41f65475edbab240dee09d3de589dc759cc27202..49a4bdded400d4e27b2656d06737936ba5ca5bb9 100644
--- a/aegisub/video_provider.h
+++ b/aegisub/video_provider.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2007, Rodrigo Braz Monteiro, Fredrik Mellbin
+// Copyright (c) 2006-2008, Rodrigo Braz Monteiro, Fredrik Mellbin
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,6 @@
 
 //////////
 // Headers
-#include <list>
 #include <wx/intl.h>
 #include "video_frame.h"
 #include "factory.h"
@@ -50,54 +49,38 @@
 class SubtitlesProvider;
 
 
-////////////////
-// Cached frame
-class CachedFrame {
-public:
-	AegiVideoFrame frame;
-	int n;
-};
-
-
 ////////////////////////////
 // Video Provider interface
 class VideoProvider {
-private:
-	unsigned int cacheMax;
-	std::list<CachedFrame> cache;
-	AegiVideoFrame tempRGBFrame;
-
-	void Cache(int n,const AegiVideoFrame frame);
-	AegiVideoFrame GetCachedFrame(int n);
-
-protected:
-	// Override this method to actually get frames
-	virtual const AegiVideoFrame DoGetFrame(int n)=0;	// Get frame as AegiVideoFrame
-
-	// Cache functions
-	void SetCacheMax(int n_frames);
-	void ClearCache();
-
 public:
-	// Base methods
-	void GetFloatFrame(float* Buffer, int n);	// Get frame as float
-	const AegiVideoFrame GetFrame(int n, int formatMask=FORMAT_RGB32);
-	VideoProvider();
-	virtual ~VideoProvider();
+	// Virtual destructor
+	virtual ~VideoProvider() {}
 
-	// Subtitles
-	virtual SubtitlesProvider *GetAsSubtitlesProvider() { return NULL; }	// Get subtitles provider
+	// Override this method to actually get frames
+	virtual const AegiVideoFrame GetFrame(int n, int formatMask)=0;
 
-	// Override the following methods:
+	// Override the following methods to get video information:
 	virtual int GetPosition()=0;				// Get the number of the last frame loaded
 	virtual int GetFrameCount()=0;				// Get total number of frames
 	virtual int GetWidth()=0;					// Returns the video width in pixels
 	virtual int GetHeight()=0;					// Returns the video height in pixels
 	virtual double GetFPS()=0;					// Get framerate in frames per second
-	virtual void OverrideFrameTimeList(wxArrayInt list) {}	// Override the list with the provided one, for VFR handling
-	virtual bool IsNativelyByFrames() { return false; }
+
+	// Use this to set any post-loading warnings, such as "being loaded with unreliable seeking"
 	virtual wxString GetWarning() { return _T(""); }
+
+	// Name of decoder, e.g. "Avisynth/FFMPegSource"
 	virtual wxString GetDecoderName() { return _("Unknown"); }
+
+	// How many frames does this provider wants that Aegisub caches? Set to 0 if it doesn't require caching.
+	virtual int GetDesiredCacheSize() { return 0; }
+
+	// For providers that are natively time-based (e.g. DirectShow)
+	virtual bool IsNativelyByFrames() { return true; }
+	virtual void OverrideFrameTimeList(wxArrayInt list) {}	// Override the list with the provided one, for VFR handling
+
+	// If this video provider has a built-in subtitles provider, return that
+	virtual SubtitlesProvider *GetAsSubtitlesProvider() { return NULL; }
 };
 
 
diff --git a/aegisub/video_provider_avs.cpp b/aegisub/video_provider_avs.cpp
index 536b6212e3c1341f5f5978935d9cad580890bdbe..c1a07017e2646c2df5a41a05774ec659263b1633 100644
--- a/aegisub/video_provider_avs.cpp
+++ b/aegisub/video_provider_avs.cpp
@@ -302,7 +302,7 @@ PClip AvisynthVideoProvider::OpenVideo(wxString _filename, bool mpeg2dec3_priori
 
 ////////////////////////
 // Actually get a frame
-const AegiVideoFrame AvisynthVideoProvider::DoGetFrame(int _n) {
+const AegiVideoFrame AvisynthVideoProvider::GetFrame(int _n,int formatMask) {
 	// Transform n if overriden
 	int n = _n;
 	if (frameTime.Count()) {
diff --git a/aegisub/video_provider_avs.h b/aegisub/video_provider_avs.h
index 2b6b3d20ea4c65361e9c36d3824647435d297fb2..963afb445c95ea19804f66e882aeed8a56246b94 100644
--- a/aegisub/video_provider_avs.h
+++ b/aegisub/video_provider_avs.h
@@ -79,7 +79,7 @@ public:
 	void LoadSubtitles(AssFile *subs);
 	bool LockedToVideo() { return true; }
 
-	const AegiVideoFrame DoGetFrame(int n);
+	const AegiVideoFrame GetFrame(int n,int formatMask);
 	void GetFloatFrame(float* Buffer, int n);
 
 	// properties
diff --git a/aegisub/video_provider_cache.cpp b/aegisub/video_provider_cache.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..00c13929b0ae90bd866ce0e9a5c8c30c6bebc017
--- /dev/null
+++ b/aegisub/video_provider_cache.cpp
@@ -0,0 +1,176 @@
+// Copyright (c) 2008, Rodrigo Braz Monteiro
+// 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
+//
+
+
+///////////
+// Headers
+#include "video_provider_cache.h"
+
+
+///////////////
+// Constructor
+VideoProviderCache::VideoProviderCache(VideoProvider *parent) {
+	master = parent;
+	cacheMax = 0;
+	SetCacheMax(parent->GetDesiredCacheSize());
+}
+
+
+//////////////
+// Destructor
+VideoProviderCache::~VideoProviderCache() {
+	delete master;
+	ClearCache();
+	tempRGBFrame.Clear();
+}
+
+
+/////////////
+// Get frame
+const AegiVideoFrame VideoProviderCache::GetFrame(int n,int format) {
+	// See if frame is cached
+	CachedFrame cached;
+	for (std::list<CachedFrame>::iterator cur=cache.begin();cur!=cache.end();cur++) {
+		cached = *cur;
+		if (cached.n == n) {
+			cache.erase(cur);
+			cache.push_back(cached);
+			return cached.frame;
+		}
+	}
+
+	// Not cached, retrieve it
+	const AegiVideoFrame frame = master->GetFrame(n,FORMAT_RGB32);
+	const AegiVideoFrame *srcFrame = &frame;
+
+	// Convert to compatible format
+	if (!(frame.format & format)) {
+		if (format & FORMAT_RGB32) tempRGBFrame.format = FORMAT_RGB32;
+		else throw _T("Unable to negotiate video frame format.");
+		tempRGBFrame.w = frame.w;
+		tempRGBFrame.h = frame.h;
+		tempRGBFrame.pitch[0] = frame.w * 4;
+		tempRGBFrame.ConvertFrom(frame);
+		srcFrame = &tempRGBFrame;
+	}
+
+	// Cache frame
+	pos = n;
+	Cache(n,*srcFrame);
+	return *srcFrame;
+}
+
+
+////////////////
+// Get as float
+void VideoProviderCache::GetFloatFrame(float* buffer, int n) {
+	const AegiVideoFrame frame = GetFrame(n,FORMAT_RGB32);
+	frame.GetFloat(buffer);
+}
+
+
+//////////////////////////
+// Set maximum cache size
+void VideoProviderCache::SetCacheMax(int n) {
+	if (n < 0) n = 0;
+	cacheMax = n;
+}
+
+
+////////////////
+// Add to cache
+void VideoProviderCache::Cache(int n,const AegiVideoFrame frame) {
+	// Cache enabled?
+	if (cacheMax == 0) return;
+
+	// Cache full, use frame at front
+	if (cache.size() >= cacheMax) {
+		cache.push_back(cache.front());
+		cache.pop_front();
+	}
+
+	// Cache not full, insert new one
+	else {
+		cache.push_back(CachedFrame());
+	}
+
+	// Cache
+	cache.front().n = n;
+	cache.front().frame.CopyFrom(frame);
+}
+
+
+///////////////
+// Clear cache
+void VideoProviderCache::ClearCache() {
+	while (cache.size()) {
+		cache.front().frame.Clear();
+		cache.pop_front();
+	}
+}
+
+
+///////////////////
+// Wrapper methods
+SubtitlesProvider *VideoProviderCache::GetAsSubtitlesProvider() {
+	return master->GetAsSubtitlesProvider();
+}
+int VideoProviderCache::GetPosition() {
+	return pos;
+}
+int VideoProviderCache::GetFrameCount() {
+	return master->GetFrameCount();
+}
+int VideoProviderCache::GetWidth() {
+	return master->GetWidth();
+}
+int VideoProviderCache::GetHeight() {
+	return master->GetHeight();
+}
+double VideoProviderCache::GetFPS() {
+	return master->GetFPS();
+}
+void VideoProviderCache::OverrideFrameTimeList(wxArrayInt list) {
+	master->OverrideFrameTimeList(list);
+}
+bool VideoProviderCache::IsNativelyByFrames() {
+	return master->IsNativelyByFrames();
+}
+wxString VideoProviderCache::GetWarning() {
+	return master->GetWarning();
+}
+wxString VideoProviderCache::GetDecoderName() {
+	return master->GetDecoderName();
+}
diff --git a/aegisub/video_provider_cache.h b/aegisub/video_provider_cache.h
new file mode 100644
index 0000000000000000000000000000000000000000..f15613582a4d4646538b4c96909b18ba5b3a910f
--- /dev/null
+++ b/aegisub/video_provider_cache.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2008, Rodrigo Braz Monteiro, Fredrik Mellbin
+// 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
+
+
+//////////
+// Headers
+#include <list>
+#include "video_provider.h"
+
+
+////////////////
+// Cached frame
+class CachedFrame {
+public:
+	AegiVideoFrame frame;
+	int n;
+};
+
+
+////////////////////////////
+// Video Provider interface
+class VideoProviderCache : public VideoProvider {
+private:
+	VideoProvider *master;
+	unsigned int cacheMax;
+	std::list<CachedFrame> cache;
+	AegiVideoFrame tempRGBFrame;
+	int pos;
+
+	void Cache(int n,const AegiVideoFrame frame);
+	AegiVideoFrame GetCachedFrame(int n);
+
+protected:
+	// Cache functions
+	void SetCacheMax(int n_frames);
+	void ClearCache();
+
+public:
+	// Base methods
+	void GetFloatFrame(float* Buffer, int n);	// Get frame as float
+	const AegiVideoFrame GetFrame(int n, int formatMask);
+	VideoProviderCache(VideoProvider *master);
+	virtual ~VideoProviderCache();
+
+	// Subtitles
+	virtual SubtitlesProvider *GetAsSubtitlesProvider();	// Get subtitles provider
+
+	// Override the following methods:
+	virtual int GetPosition();				// Get the number of the last frame loaded
+	virtual int GetFrameCount();			// Get total number of frames
+	virtual int GetWidth();					// Returns the video width in pixels
+	virtual int GetHeight();				// Returns the video height in pixels
+	virtual double GetFPS();				// Get framerate in frames per second
+	virtual void OverrideFrameTimeList(wxArrayInt list);	// Override the list with the provided one, for VFR handling
+	virtual bool IsNativelyByFrames();
+	virtual wxString GetWarning();
+	virtual wxString GetDecoderName();
+};
diff --git a/aegisub/video_provider_dshow.h b/aegisub/video_provider_dshow.h
index 33ad2ffcf34bcac5d7cb20323e42900737a14ea6..631d9e9ee1e6932c18683a9d992277cc17c63238 100644
--- a/aegisub/video_provider_dshow.h
+++ b/aegisub/video_provider_dshow.h
@@ -102,7 +102,7 @@ public:
 
 	void RefreshSubtitles();
 
-	const AegiVideoFrame DoGetFrame(int n);
+	const AegiVideoFrame GetFrame(int n, int formatMask);
 	void GetFloatFrame(float* Buffer, int n);
 
 	int GetPosition() { return last_fnum; };
@@ -111,6 +111,7 @@ public:
 	int GetWidth() { return width; };
 	int GetHeight() { return height; };
 	wxString GetDecoderName() { return _("DirectShow"); }
+	bool IsNativelyByFrames() { return false; }
 
 	void OverrideFrameTimeList(wxArrayInt list);
 };
diff --git a/aegisub/video_provider_dummy.cpp b/aegisub/video_provider_dummy.cpp
index 8274bfdf97d7a468419f86797359da68b9b238fc..3422fbf1e4e5a220f79c8141d785819713214eff 100644
--- a/aegisub/video_provider_dummy.cpp
+++ b/aegisub/video_provider_dummy.cpp
@@ -203,7 +203,7 @@ wxString DummyVideoProvider::MakeFilename(double fps, int frames, int _width, in
 
 /////////////
 // Get frame
-const AegiVideoFrame DummyVideoProvider::DoGetFrame(int n) {
+const AegiVideoFrame DummyVideoProvider::GetFrame(int n,int formatMask) {
 	lastFrame = n;
 	return frame;
 }
diff --git a/aegisub/video_provider_dummy.h b/aegisub/video_provider_dummy.h
index aaff8c3fb3d5961260d5e36116e6080c941af091..90dd111021c3c2b2dd68d96a67cfc5ccd82da604 100644
--- a/aegisub/video_provider_dummy.h
+++ b/aegisub/video_provider_dummy.h
@@ -58,14 +58,12 @@ private:
 
 	void Create(double fps, int frames, int _width, int _height, const wxColour &colour, bool pattern);
 
-protected:
-	const AegiVideoFrame DoGetFrame(int n);
-
 public:
 	DummyVideoProvider(wxString filename, double fps);
 	DummyVideoProvider(double fps, int frames, int _width, int _height, const wxColour &colour, bool pattern);
 	~DummyVideoProvider();
 
+	const AegiVideoFrame GetFrame(int n, int formatMask);
 	static wxString MakeFilename(double fps, int frames, int _width, int _height, const wxColour &colour, bool pattern);
 
 	int GetPosition();
diff --git a/aegisub/video_provider_lavc.cpp b/aegisub/video_provider_lavc.cpp
index 84c3b17ac57a4f9dbb82c0ec938c7fd535502933..9877cf31027566185a21425a5ebb9445aa5582d1 100644
--- a/aegisub/video_provider_lavc.cpp
+++ b/aegisub/video_provider_lavc.cpp
@@ -298,7 +298,7 @@ wxBitmap LAVCVideoProvider::AVFrameToWX(AVFrame *source, int n) {
 
 /////////////
 // Get frame
-const AegiVideoFrame LAVCVideoProvider::DoGetFrame(int n) {
+const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
 	// Return stored frame
 	n = MID(0,n,GetFrameCount()-1);
 	if (n == frameNumber) {
diff --git a/aegisub/video_provider_lavc.h b/aegisub/video_provider_lavc.h
index c357b5f7c588110646633af3ca6292b90f9213e2..120e70bf38820fcdad319f324f63f40f00cac619 100644
--- a/aegisub/video_provider_lavc.h
+++ b/aegisub/video_provider_lavc.h
@@ -92,12 +92,12 @@ private:
 	void Close();
 
 protected:
-	const AegiVideoFrame DoGetFrame(int n);
 
 public:
 	LAVCVideoProvider(wxString filename, double fps);
 	~LAVCVideoProvider();
 
+	const AegiVideoFrame GetFrame(int n,int formatType);
 	int GetPosition();
 	int GetFrameCount();