view data/fileio/VideoFileReader.cpp @ 150:c946c19e6329

(none)
author ivand_qmul
date Wed, 14 Nov 2007 15:11:46 +0000
parents e63e8272dcb0
children 2ac52ea3c1c4
line wrap: on
line source
/* -*- 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"

#ifdef WIN32
#include <Windows.h>
#endif
extern float zoomFivan;
long long countFreq;
int Videow=320;
int Videoh=240;
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);

	MainWindow * MWins=MainWindow::instance();
	MWins->setSDLInitSize(w,h);


	SDL_Init(SDL_INIT_VIDEO);
	Videow=w=320*zoomFivan;
	Videoh=h=240*zoomFivan;
    // Open the Video device
    screen = SDL_SetVideoMode(w, h, 0, SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL);
	//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);
	QueryPerformanceFrequency((LARGE_INTEGER *)(&countFreq));
	countFreq/=1000;
	film->countFreq=countFreq;
	QueryPerformanceCounter((LARGE_INTEGER *)(&film->timer));
	film->timer=film->timer/(film->countFreq);
	film->timebase=1;
	film->vs[film->videoStream]->lastTimeStamp=0;
	while( film->videoThreadActive ) {

		if (!(w==(int)(320*zoomFivan))){
		w=320*zoomFivan;
		h=240*zoomFivan;
		// Open the Video device
		screen = SDL_SetVideoMode(w, h, 0, SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL);
		}
		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(1);
    }
	// 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