diff data/fileio/VideoFileReader.cpp @ 125:66af7c1b10d9

(none)
author ivand_qmul
date Mon, 22 Oct 2007 13:59:27 +0000
parents
children 587ad94d6ac2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fileio/VideoFileReader.cpp	Mon Oct 22 13:59:27 2007 +0000
@@ -0,0 +1,282 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*   
+	Sound Access
+		EASAIER client application.
+		Queen Mary 2007. Ivan Damnjanovic.
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License as
+	published by the Free Software Foundation; either version 2 of the
+	License, or (at your option) any later version.  See the file
+	COPYING included with this distribution for more information.
+*/
+
+#ifdef HAVE_FFMPEG
+
+
+//#include "system/System.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <iostream>
+
+#include <QApplication>
+#include <QFileInfo>
+#include <QProgressDialog>
+
+#include "VideoFileReader.h"
+
+
+VideoFileReader::VideoFileReader(QString path, bool showProgress, CacheMode mode) :
+    CodedAudioFileReader(mode),
+    m_path(path)
+{
+    m_frameCount = 0;
+    m_channelCount = 0;
+    m_sampleRate = 0;
+    m_fileSize = 0;
+    m_bitrateNum = 0;
+    m_bitrateDenom = 0;
+    m_cancelled = false;
+
+    struct stat stat;
+    if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
+	m_error = QString("File %1 does not exist.").arg(path);
+	return;
+    }
+	
+	// standard SDL initialization stuff
+    if(SDL_Init(SDL_INIT_VIDEO|SDL_DOUBLEBUF|SDL_INIT_AUDIO) < 0) {
+        m_error = QString("Failed to open file %1 for reading.").arg(SDL_GetError());
+        return;
+    }
+    m_fileSize = stat.st_size;
+
+    
+    // open file from arg[1]
+    film = SDL_ffmpegOpen(path.toLocal8Bit().data());
+    if (path.endsWith("mpg")) film->delay=1;
+	if (path.endsWith("divx")) film->delay=1;
+	if(!film) 
+			{
+				m_error = QString("Failed to open file %1 for reading.").arg(path);
+				return;
+			}	
+  // print some info on detected stream to output
+    film->skipAudio=0;
+	film->skipVideo=0;
+	int s;
+    SDL_ffmpegStream *str;
+	for(s = 0; s<film->VStreams; s++)
+		str = SDL_ffmpegGetVideoStream(film, s);
+	SDL_ffmpegSelectVideoStream(film, 0);
+	film->skipVideo=1;
+    for(s = 0; s<film->AStreams; s++) 
+        str = SDL_ffmpegGetAudioStream(film, s);
+
+	SDL_ffmpegSelectAudioStream(film, 0);
+
+
+    if (showProgress) {
+		m_progress = new QProgressDialog
+	    (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
+	    QObject::tr("Stop"), 0, 100);
+		m_progress->hide();
+    }
+
+	m_sampleRate=str->sampleRate;
+	int channels=str->channels;
+	m_channelCount=channels;
+	SDL_ffmpegPause(film, 0);
+	SDL_ffmpegStartDecoding(film);
+	
+	if (!decodeAudio(film)) {
+		m_error = QString("Failed to decode audio from file %1 for reading.").arg(path);
+		return;
+	}	
+	film->skipAudio=1;
+	//SDL_ffmpegSeek(film, 0);
+    //SDL_ffmpegStopDecoding(film);
+	SDL_Delay(5);
+	film->skipVideo=0;
+	
+	film->videoThread=SDL_CreateThread(videoPlayCall,this);
+	
+ 
+    
+    
+
+    if (showProgress) {
+	delete m_progress;
+	m_progress = 0;
+    }
+
+    //delete[] filebuffer;
+}
+
+
+VideoFileReader::~VideoFileReader()
+{
+	film->videoThreadActive = 0;
+	if(film->videoThread) SDL_WaitThread(film->videoThread, 0);
+}
+bool 
+VideoFileReader::decodeAudio(SDL_ffmpegFile* file)
+{
+	
+	
+	int64_t duration=((AVFormatContext *)file->_ffmpeg)->duration;
+    double elapsed = 0;
+	m_cancelled=false;
+	int audio_ends=0;
+	while((elapsed < duration)&&!m_cancelled ) {
+          
+        elapsed = double(m_frameCount)*1000000 /(m_channelCount*m_sampleRate);
+        double percent = (elapsed * 100) / duration;
+        int progress = int(percent);
+        if (progress < 1) progress = 1;
+        if (progress > 99) progress = 99;
+        if (progress > m_progress->value()) {
+            m_progress->setValue(progress);
+            m_progress->show();
+            m_progress->raise();
+            qApp->processEvents();
+            if (m_progress->wasCanceled()) {
+                m_cancelled = true;
+            }
+        }
+		
+		// we tell SDL_ffmpegGetAudio how many bytes we need, the function then
+		// fills this pointer with the amount of bytes it could actually give
+		int gotLength = 100000;
+		if (!isDecodeCacheInitialised()) {
+				initialiseDecodeCache();
+		}
+		// we try to get some data from our file
+		// important! this call is paired with SDL_ffmpegReleaseAudio
+		int16_t* audio =(int16_t *) SDL_ffmpegGetAudio(file, &gotLength);
+		for (int i=0; i<gotLength/2;i++)
+		{
+			float sample=float(*audio++)/float(2*32768);
+			addSampleToDecodeCache(sample);
+		}
+		// copy the bytes we got to audiocard
+		// if(audio) memcpy(stream, audio, gotLength);
+
+		// we release our audio data, so the decode thread can fill it again
+		// we also inform this function of the amount of bytes we used, so it can
+		// move the buffer accordingly
+		// important! this call is paired with SDL_ffmpegGetAudio
+		SDL_ffmpegReleaseAudio(file, gotLength);
+			//decode_audio(film, 1000000);
+        m_frameCount+=gotLength/2;
+		if ((progress > 97)&&(gotLength<=0)) audio_ends++;
+		if (audio_ends>=2000) m_cancelled = true;
+    }
+	m_frameCount/=m_channelCount;
+	if (isDecodeCacheInitialised()) finishDecodeCache();
+	return true;
+}
+bool 
+VideoFileReader::videoInit(SDL_ffmpegFile* file)
+{
+	int w,h;
+    // we get the size from our active video stream, if no active video stream
+    // exists, width and height are set to default values (320x240)
+    SDL_ffmpegGetVideoSize(file, &w, &h);
+	
+    // Open the Video device
+    screen = SDL_SetVideoMode(w, h, 0, SDL_DOUBLEBUF|SDL_HWSURFACE);
+	SDL_WM_SetCaption("EASAIER Video Player", "EASAIER Video Player");
+    if(!screen) {
+        printf("Couldn't open video: %s\n", SDL_GetError());
+        return false;
+    }
+	return true;
+}
+
+int VideoFileReader::videoPlayCall(void *t)
+{
+        return ((VideoFileReader *)t)->videoPlay();
+} 
+
+int
+VideoFileReader::videoPlay()
+{
+
+
+	film->videoThreadActive = 1;
+	MainWindow * MWins=MainWindow::instance();
+	
+	if (!videoInit(film)) {
+		m_error = QString("Failed to failed to initalized video file for reading.");
+		return 0;
+	}
+	//const SDL_VideoInfo * vid=SDL_GetVideoInfo();
+	film->audioTime =0;
+	int w,h;
+	SDL_ffmpegGetVideoSize(film, &w, &h);
+	//SDL_ffmpegStartDecoding(film);
+    SDL_Delay(1000);
+	
+	while( film->videoThreadActive ) {
+
+		
+		if (MWins->isAudioPlaying())
+		{	
+			if ((SDL_ffmpegGetState(film))||((long)(abs((long)(film->audioTime - (int64_t)(MWins->Get_CurAudioTime()))))>=1000)) 
+			{
+				//SDL_Delay(1000);
+				film->audioTime = MWins->Get_CurAudioTime();
+				SDL_ffmpegSeek(film, film->audioTime);
+				SDL_ffmpegPause(film, 0);
+			}
+			else
+				film->audioTime = MWins->Get_CurAudioTime();
+		}
+		else
+		{
+			SDL_ffmpegPause(film, 1);	
+			
+		}
+
+        // we retrieve the current image from the file
+        // we get 0 if no file could be retrieved
+        // important! please note this call should be paired with SDL_ffmpegReleaseVideo
+        SDL_Surface* bmp = SDL_ffmpegGetVideo((SDL_ffmpegFile *)film);
+		
+        if(bmp) {
+
+			
+            // we got a frame, so we better show this one
+            SDL_BlitSurface(bmp, 0, screen, 0);
+
+            // we flip the double buffered screen so we might actually see something
+            SDL_Flip(screen);
+
+            // After releasing bmp, you can no longer use it.
+            // you should call this function every time you get a frame!
+            SDL_ffmpegReleaseVideo((SDL_ffmpegFile *)film, bmp);
+        }
+
+        // we wish not to kill our poor cpu, so we give it some timeoff
+        SDL_Delay(10);
+    }
+	// after all is said and done, we should call this
+    SDL_ffmpegFree(film);
+	return 0;
+}
+
+void
+VideoFileReader::getSupportedExtensions(std::set<QString> &extensions)
+{
+    extensions.insert("mpg");
+	extensions.insert("avi");
+	extensions.insert("divx");
+	extensions.insert("mov");
+}
+
+#endif