Browse Source

Linux video player updates.

Frank Zago 16 years ago
parent
commit
6be7a77fee
6 changed files with 225 additions and 148 deletions
  1. 0 2
      client/CMT.cpp
  2. 3 4
      client/CPreGame.cpp
  3. 0 65
      client/CSpellWindow.cpp
  4. 2 4
      client/GUIClasses.cpp
  5. 200 67
      hch/CVideoHandler.cpp
  6. 20 6
      hch/CVideoHandler.h

+ 0 - 2
client/CMT.cpp

@@ -370,12 +370,10 @@ void processCommand(const std::string &message, CClient *&client)
 //plays intro, ends when intro is over or button has been pressed (handles events)
 void playIntro()
 {
-#ifdef _WIN32
 	if(CGI->videoh->openAndPlayVideo("3DOLOGO.SMK", 60, 40, screen, true))
 	{
 		CGI->videoh->openAndPlayVideo("AZVS.SMK", 60, 80, screen, true);
 	}
-#endif
 }
 
 void dispose()

+ 3 - 4
client/CPreGame.cpp

@@ -2055,6 +2055,8 @@ StartInfo CPreGame::runLoop()
 
 #ifdef _WIN32
 	CGI->videoh->open("ACREDIT.SMK");
+#else
+	CGI->videoh->open("ACREDIT.SMK", true, false);
 #endif
 
 	while(run)
@@ -2324,10 +2326,8 @@ StartInfo CPreGame::runLoop()
 			}
 		} HANDLE_EXCEPTION
 
-#ifdef _WIN32
 		if(currentItems())
 			CGI->videoh->update(8, 105, screen, true, false);
-#endif
 
 		CGI->curh->draw1();
 		SDL_Flip(screen);
@@ -2335,9 +2335,8 @@ StartInfo CPreGame::runLoop()
 		SDL_Delay(20); //give time for other apps
 	}
 	ret.mode = (fromMenu==newGame) ? 0 : 1;
-#ifdef _WIN32
+
 	CGI->videoh->close();
-#endif
 
 	return ret;
 }

+ 0 - 65
client/CSpellWindow.cpp

@@ -638,77 +638,12 @@ void CSpellWindow::deactivate()
 
 void CSpellWindow::turnPageLeft()
 {
-	// Note: video decoders are different, and one is buggy.
-
-#ifndef _WIN32
-	if (CGI->videoh->open("PGTRNLFT.SMK", pos.x+13, pos.y+14)) 
-	{
-		while(CGI->videoh->nextFrame()) 
-		{
-			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-		}
-		CGI->videoh->close();
-	}
-#else
 	CGI->videoh->openAndPlayVideo("PGTRNLFT.SMK", pos.x+13, pos.y+15, screen);
-#endif
-
-
-//#ifdef _WIN32
-//	const int y = pos.y+15;
-//
-//#endif	
-//
-//	if (CGI->videoh->open("PGTRNLFT.SMK", pos.x+13, y)) {
-//		while(CGI->videoh->nextFrame()) {
-//			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-//#ifndef _WIN32
-//			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-//			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-//#endif
-//		}
-//		CGI->videoh->close();
-//	}
 }
 
 void CSpellWindow::turnPageRight()
 {
-	// Note: video decoders are different, and one is buggy.
-
-
-#ifndef _WIN32
-	if (CGI->videoh->open("PGTRNRGH.SMK", pos.x+13, pos.y+14)) 
-	{
-		while(CGI->videoh->nextFrame()) 
-		{
-			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-		}
-		CGI->videoh->close();
-	}
-#else
 	CGI->videoh->openAndPlayVideo("PGTRNRGH.SMK", pos.x+13, pos.y+15, screen);
-#endif
-
-//#ifdef _WIN32
-//	const int y = pos.y+15;
-//#else
-//	const int y = pos.y+14;
-//#endif	
-//
-//	if (CGI->videoh->open("PGTRNRGH.SMK", pos.x+13, y)) {
-//		while(CGI->videoh->nextFrame()) {
-//			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-//#ifndef _WIN32
-//			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-//			SDL_framerateDelay(LOCPLINT->mainFPSmng);
-//#endif
-//		}
-//		CGI->videoh->close();
-//	}
 }
 
 CSpellWindow::SpellArea::SpellArea(SDL_Rect pos, CSpellWindow * owner)

+ 2 - 4
client/GUIClasses.cpp

@@ -2795,6 +2795,8 @@ CTavernWindow::CTavernWindow(const CGHeroInstance *H1, const CGHeroInstance *H2,
 
 #ifdef _WIN32
 	CGI->videoh->open("TAVERN.BIK");
+#else
+	CGI->videoh->open("tavern.mjpg", true, false);
 #endif
 }
 
@@ -2807,9 +2809,7 @@ void CTavernWindow::recruitb()
 
 CTavernWindow::~CTavernWindow()
 {
-#ifdef _WIN32
 	CGI->videoh->close();
-#endif
 	SDL_FreeSurface(bg);
 	delete cancel;
 	delete thiefGuild;
@@ -2848,9 +2848,7 @@ void CTavernWindow::close()
 void CTavernWindow::show(SDL_Surface * to)
 {
 	blitAt(bg,pos.x,pos.y,to);
-#ifdef _WIN32
 	CGI->videoh->update(pos.x+70, pos.y+56, to, true, false);
-#endif
 	if(h1.h)
 		h1.show(to);
 	if(h2.h)

+ 200 - 67
hch/CVideoHandler.cpp

@@ -3,10 +3,22 @@
 #include "CSndHandler.h"
 #include "CVideoHandler.h"
 #include <SDL.h>
+#include "../client/SDL_Extensions.h"
+
+//reads events and throws on key down
+static bool keyDown()
+{
+	SDL_Event ev;
+	while(SDL_PollEvent(&ev))
+	{
+		if(ev.type == SDL_KEYDOWN || ev.type == SDL_MOUSEBUTTONDOWN)
+			return true;
+	}
+	return false;
+}
 
 #ifdef _WIN32
 
-#include "../client/SDL_Extensions.h"
 #include <boost/algorithm/string/predicate.hpp>
 
 
@@ -409,18 +421,6 @@ void CVideoPlayer::redraw( int x, int y, SDL_Surface *dst, bool update )
 	current->redraw(x, y, dst, update);
 }
 
-//reads events and throws on key down
-bool keyDown()
-{
-	SDL_Event ev;
-	while(SDL_PollEvent(&ev))
-	{
-		if(ev.type == SDL_KEYDOWN || ev.type == SDL_MOUSEBUTTONDOWN)
-			return true;
-	}
-	return false;
-}
-
 bool CVideoPlayer::playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey)
 {
 	int frame = 0;
@@ -443,8 +443,6 @@ bool CVideoPlayer::playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey)
 
 #else
 
-#include "../client/SDL_Extensions.h"
-
 extern "C" {
 #include <libavformat/avformat.h>
 #include <libswscale/swscale.h>
@@ -491,12 +489,24 @@ static int lod_read(URLContext *context, unsigned char *buf, int size)
 	return size;
 }
 
+static int64_t lod_seek(URLContext *context, int64_t pos, int whence)
+{
+	CVideoPlayer *video = (CVideoPlayer *)context->priv_data;
+
+	// Not sure what the parameter whence is. Assuming it always
+	// indicates an absolute value;
+	video->offset = pos;
+	amin(video->offset, video->length);
+
+	return -1;//video->offset;
+}
+
 static URLProtocol lod_protocol = {
 	protocol_name,
 	lod_open,
 	lod_read,
 	NULL,						// no write
-	NULL,						// no seek
+    lod_seek,
 	lod_close
 };
 
@@ -507,6 +517,7 @@ CVideoPlayer::CVideoPlayer()
 	codec = NULL;
 	sws = NULL;
 	overlay = NULL;
+	dest = NULL;
 
 	// Register codecs. TODO: May be overkill. Should call a
 	// combination of av_register_input_format() /
@@ -519,34 +530,39 @@ CVideoPlayer::CVideoPlayer()
 	vidh = new CVidHandler(std::string(DATA_DIR "Data" PATHSEPARATOR "VIDEO.VID"));
 }
 
-bool CVideoPlayer::open(std::string fname, int x, int y)
+// loop = to loop through the video
+// useOverlay = directly write to the screen.
+bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay)
 {
-	char url[100];
-
 	close();
 
-	data = vidh->extract(fname, length);
-
-	if (data == NULL)
-		return false;
-
 	offset = 0;
+	refreshWait = 3;
+	refreshCount = -1;
+	doLoop = loop;
 
-	// Create our URL name with the 'lod' protocol as a prefix and a
-	// back pointer to our object. Should be 32 and 64 bits compatible.
-	sprintf(url, "%s:0x%016llx", protocol_name, (unsigned long long)(uintptr_t)this);
+	data = vidh->extract(fname, length);
 
-	if (av_open_input_file(&format, url, NULL, 0, NULL)!=0)
-		return false;
+	if (data) {
+		// Create our URL name with the 'lod' protocol as a prefix and a
+		// back pointer to our object. Should be 32 and 64 bits compatible.
+		char url[100];
+		sprintf(url, "%s:0x%016llx", protocol_name, (unsigned long long)(uintptr_t)this);
+
+		if (av_open_input_file(&format, url, NULL, 0, NULL)!=0) {
+			return false;
+		}
+	} else {
+		// File is not in a container
+		if (av_open_input_file(&format, (DATA_DIR "Data/video/" + fname).c_str(), NULL, 0, NULL)!=0) {
+			// tlog1 << "Video file not found: " DATA_DIR "Data/video/" + fname << std::endl;
+			return false;
+		}
+	}
 
 	// Retrieve stream information
 	if (av_find_stream_info(format) < 0)
 		return false;
-  
-#if 0
-	// Dump information about file onto standard error
-	dump_format(format, 0, url, 0);
-#endif
 
 	// Find the first video stream
 	stream = -1;
@@ -583,64 +599,138 @@ bool CVideoPlayer::open(std::string fname, int x, int y)
 	frame = avcodec_alloc_frame();
 
 	// Allocate a place to put our YUV image on that screen
-	overlay = SDL_CreateYUVOverlay(codecContext->width, codecContext->height,
-								   SDL_YV12_OVERLAY, screen);
+	if (useOverlay) {
+		overlay = SDL_CreateYUVOverlay(codecContext->width, codecContext->height,
+									   SDL_YV12_OVERLAY, screen);
+	} else {
+		dest = CSDL_Ext::newSurface(codecContext->width, codecContext->height);
+		destRect.x = destRect.y = 0;
+		destRect.w = codecContext->width;
+		destRect.h = codecContext->height;
+	}
+
+	if (overlay == NULL && dest == NULL)
+		return false;
 
 	// Convert the image into YUV format that SDL uses
-	sws = sws_getContext(codecContext->width, codecContext->height, 
-						 codecContext->pix_fmt, codecContext->width, codecContext->height, 
-						 PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
+	if (overlay) {
+		sws = sws_getContext(codecContext->width, codecContext->height, 
+							 codecContext->pix_fmt, codecContext->width, codecContext->height, 
+							 PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
+	} else {
+		sws = sws_getContext(codecContext->width, codecContext->height, 
+							 codecContext->pix_fmt, codecContext->width, codecContext->height, 
+							 PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+	}
+
 	if (sws == NULL)
 		return false;
 
-	pos.x = x;
-	pos.y = y;
 	pos.w = codecContext->width;
 	pos.h = codecContext->height;
 
 	return true;
 }
 
-// Display the next frame. Return false on error/end of file.
+// Read the next frame. Return false on error/end of file.
 bool CVideoPlayer::nextFrame()
 {
 	AVPacket packet;
 	int frameFinished = 0;
+	bool gotError = false;
 
-	while(!frameFinished && av_read_frame(format, &packet)>=0) {
+	if (sws == NULL)
+		return false;
 
-		// Is this a packet from the video stream?
-		if (packet.stream_index == stream) {
-			// Decode video frame
-			avcodec_decode_video(codecContext, frame, &frameFinished, 
-								 packet.data, packet.size);
-      
-			// Did we get a video frame?
-			if (frameFinished) {
-				SDL_LockYUVOverlay(overlay);
+	while(!frameFinished) {
+
+		int ret = av_read_frame(format, &packet);
+		if (ret < 0) {
+			// Error. It's probably an end of file.
+			if (doLoop && !gotError) {
+				// Rewind
+				if (av_seek_frame(format, stream, 0, 0) < 0)
+					break;
+				gotError = true;
+			} else {
+				break;
+			}
+		} else {
+			// Is this a packet from the video stream?
+			if (packet.stream_index == stream) {
+				// Decode video frame
+				avcodec_decode_video(codecContext, frame, &frameFinished, 
+									 packet.data, packet.size);
+
+				// Did we get a video frame?
+				if (frameFinished) {
+					AVPicture pict;
+
+					if (overlay) {
+						SDL_LockYUVOverlay(overlay);
+				
+						pict.data[0] = overlay->pixels[0];
+						pict.data[1] = overlay->pixels[2];
+						pict.data[2] = overlay->pixels[1];
+
+						pict.linesize[0] = overlay->pitches[0];
+						pict.linesize[1] = overlay->pitches[2];
+						pict.linesize[2] = overlay->pitches[1];
+
+						sws_scale(sws, frame->data, frame->linesize,
+								  0, codecContext->height, pict.data, pict.linesize);
+
+						SDL_UnlockYUVOverlay(overlay);
+					} else {
+						pict.data[0] = (uint8_t *)dest->pixels;
+						pict.linesize[0] = dest->pitch;
+
+						sws_scale(sws, frame->data, frame->linesize,
+								  0, codecContext->height, pict.data, pict.linesize);
+					}
+				}
+			}
 
-				AVPicture pict;
-				pict.data[0] = overlay->pixels[0];
-				pict.data[1] = overlay->pixels[2];
-				pict.data[2] = overlay->pixels[1];
+			av_free_packet(&packet);
+		}
+	}
 
-				pict.linesize[0] = overlay->pitches[0];
-				pict.linesize[1] = overlay->pitches[2];
-				pict.linesize[2] = overlay->pitches[1];
+	return frameFinished != 0;
+}
 
-				sws_scale(sws, frame->data, frame->linesize,
-						  0, codecContext->height, pict.data, pict.linesize);
+void CVideoPlayer::show( int x, int y, SDL_Surface *dst, bool update )
+{
+	if (sws == NULL)
+		return;
 
-				SDL_UnlockYUVOverlay(overlay);
+	pos.x = x;
+	pos.y = y;
+	SDL_BlitSurface(dest, &destRect, dst, &pos);
 	
-				SDL_DisplayYUVOverlay(overlay, &pos);
-			}
-		}
+	if (update)
+		SDL_UpdateRect(dst, pos.x, pos.y, pos.w, pos.h);
+}
+
+void CVideoPlayer::redraw( int x, int y, SDL_Surface *dst, bool update )
+{
+	show(x, y, dst, update);
+}
 
-		av_free_packet(&packet);
+void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, bool update )
+{
+	if (sws == NULL)
+		return;
+
+	if (refreshCount <= 0)
+	{
+		refreshCount = refreshWait;
+		if (nextFrame())
+			show(x,y,dst,update);
+	} else {
+		redraw(x, y, dst, update);
 	}
 
-	return frameFinished != 0;
+	refreshCount --;
 }
 
 void CVideoPlayer::close()
@@ -655,6 +745,11 @@ void CVideoPlayer::close()
 		overlay = NULL;
 	}
 
+	if (dest) {
+		SDL_FreeSurface(dest);
+		dest = NULL;
+	}
+
 	if (frame) {
 		av_free(frame);
 		frame = NULL;
@@ -671,6 +766,44 @@ void CVideoPlayer::close()
 		format = NULL;
 	}
 }
+
+// Plays a video. Only works for overlays.
+bool CVideoPlayer::playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey)
+{
+	// Note: either the windows player or the linux player is
+	// broken. Compensate here until the bug is found.
+	y--;
+
+	pos.x = x;
+	pos.y = y;
+
+	FPSmanager mainFPSmng;
+    SDL_initFramerate(&mainFPSmng);
+    SDL_setFramerate(&mainFPSmng, 48);
+
+	while(nextFrame()) {
+		
+		if(stopOnKey && keyDown())
+			return false;
+
+		SDL_DisplayYUVOverlay(overlay, &pos);
+
+		// Wait 3 frames
+		SDL_framerateDelay(&mainFPSmng);
+		SDL_framerateDelay(&mainFPSmng);
+		SDL_framerateDelay(&mainFPSmng);
+	}
+
+	return true;
+}
+
+bool CVideoPlayer::openAndPlayVideo(std::string name, int x, int y, SDL_Surface *dst, bool stopOnKey)
+{
+	open(name, false, true);
+	bool ret = playVideo(x, y, dst, stopOnKey);
+	close();
+	return ret;
+}
 	
 CVideoPlayer::~CVideoPlayer()
 {

+ 20 - 6
hch/CVideoHandler.h

@@ -2,12 +2,12 @@
 #define __CVIDEOHANDLER_H__
 #include "../global.h"
 
+struct SDL_Surface;
+
 #ifdef _WIN32
 
 #include <windows.h>
 
-struct SDL_Surface;
-
 #pragma pack(push,1)
 struct BINK_STRUCT
 {
@@ -57,7 +57,6 @@ typedef void(__stdcall*  BinkDoFrame)(HBINK);
 typedef ui8(__stdcall*  BinkWait)(HBINK);
 typedef si32(__stdcall*  BinkCopyToBuffer)(HBINK, void* buffer, int stride, int height, int x, int y, int mode);
 
-
 class IVideoPlayer
 {
 public:
@@ -194,7 +193,7 @@ typedef struct AVFrame AVFrame;
 struct SwsContext;
 class CVidHandler;
 
-class CVideoPlayer
+class CVideoPlayer //: public IVideoPlayer
 {
 private:
 	int stream;					// stream index in video
@@ -204,20 +203,35 @@ private:
 	AVFrame *frame; 
 	struct SwsContext *sws;
 
+	// Destination. Either overlay or dest.
 	SDL_Overlay *overlay;
-	SDL_Rect pos;				// overlay position
+	SDL_Surface *dest;
+	SDL_Rect destRect;			// valid when dest is used
+	SDL_Rect pos;				// destination on screen
 
 	CVidHandler *vidh;
 
+	int refreshWait; // Wait several refresh before updating the image
+	int refreshCount;
+	bool doLoop;				// loop through video
+
 public:
 	CVideoPlayer();
 	~CVideoPlayer();
 
 	bool init();
-	bool open(std::string fname, int x, int y);
+	bool open(std::string fname, bool loop=false, bool useOverlay=false);
 	void close();
 	bool nextFrame();			// display next frame
 
+	void show(int x, int y, SDL_Surface *dst, bool update = true); //blit current frame
+	void redraw(int x, int y, SDL_Surface *dst, bool update = true); //reblits buffer
+	void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true); //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true
+	
+	// Opens video, calls playVideo, closes video; returns playVideo result (if whole video has been played)
+	bool playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey);
+	bool openAndPlayVideo(std::string name, int x, int y, SDL_Surface *dst, bool stopOnKey = false);
+
 	const char *data;			// video buffer
 	int length;					// video size
 	unsigned int offset;		// current data offset