annotate data/fileio/VideoFileReader.cpp @ 282:d9319859a4cf tip

(none)
author benoitrigolleau
date Fri, 31 Oct 2008 11:00:24 +0000
parents 6eeb195adbb4
children
rev   line source
ivand_qmul@125 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
ivand_qmul@125 2
ivand_qmul@125 3 /*
ivand_qmul@125 4 Sound Access
ivand_qmul@125 5 EASAIER client application.
ivand_qmul@125 6 Queen Mary 2007. Ivan Damnjanovic.
ivand_qmul@125 7
ivand_qmul@125 8 This program is free software; you can redistribute it and/or
ivand_qmul@125 9 modify it under the terms of the GNU General Public License as
ivand_qmul@125 10 published by the Free Software Foundation; either version 2 of the
ivand_qmul@125 11 License, or (at your option) any later version. See the file
ivand_qmul@125 12 COPYING included with this distribution for more information.
lbajardsilogic@191 13 */
lbajardsilogic@191 14
lbajardsilogic@191 15 #ifdef HAVE_FFMPEG
lbajardsilogic@191 16
lbajardsilogic@191 17
lbajardsilogic@191 18 //#include "system/System.h"
lbajardsilogic@191 19
lbajardsilogic@191 20 #include <sys/types.h>
lbajardsilogic@191 21 #include <sys/stat.h>
lbajardsilogic@191 22 #include <fcntl.h>
lbajardsilogic@191 23 #include <cassert>
lbajardsilogic@191 24
lbajardsilogic@191 25 #include <iostream>
lbajardsilogic@191 26
lbajardsilogic@191 27 #include <QApplication>
lbajardsilogic@191 28 #include <QFileInfo>
lbajardsilogic@191 29 #include <QProgressDialog>
lbajardsilogic@191 30
lbajardsilogic@191 31 #include <avformat.h>
lbajardsilogic@191 32
lbajardsilogic@191 33 #include "VideoFileReader.h"
lbajardsilogic@191 34
ivand_qmul@129 35 #ifdef WIN32
ivand_qmul@129 36 #include <Windows.h>
lbajardsilogic@191 37 #endif
lbajardsilogic@191 38 long long countFreq;
lbajardsilogic@191 39 int Videow=320;
lbajardsilogic@191 40 int Videoh=240;
benoitrigolleau@256 41 int zoomWivan=320;
benoitrigolleau@256 42 int zoomHivan=240;
benoitrigolleau@256 43 int haveFilm=0;
benoitrigolleau@256 44
lbajardsilogic@191 45 VideoFileReader::VideoFileReader(QString path, bool showProgress, CacheMode mode) :
lbajardsilogic@191 46 CodedAudioFileReader(mode),
lbajardsilogic@191 47 m_path(path)
lbajardsilogic@191 48 {
lbajardsilogic@191 49 m_frameCount = 0;
lbajardsilogic@191 50 m_channelCount = 0;
lbajardsilogic@191 51 m_sampleRate = 0;
lbajardsilogic@191 52 m_fileSize = 0;
lbajardsilogic@191 53 m_bitrateNum = 0;
lbajardsilogic@191 54 m_bitrateDenom = 0;
lbajardsilogic@191 55 m_cancelled = false;
lbajardsilogic@191 56 m_zoomfactor = 1;
lbajardsilogic@191 57
lbajardsilogic@191 58 struct stat stat;
lbajardsilogic@191 59 if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
lbajardsilogic@191 60 m_error = QString("File %1 does not exist.").arg(path);
lbajardsilogic@191 61 return;
lbajardsilogic@191 62 }
ivand_qmul@125 63
ivand_qmul@125 64 // standard SDL initialization stuff
ivand_qmul@125 65 if(SDL_Init(SDL_INIT_VIDEO|SDL_DOUBLEBUF|SDL_INIT_AUDIO) < 0) {
ivand_qmul@125 66 m_error = QString("Failed to open file %1 for reading.").arg(SDL_GetError());
ivand_qmul@125 67 return;
lbajardsilogic@191 68 }
lbajardsilogic@191 69 m_fileSize = stat.st_size;
lbajardsilogic@191 70
benoitrigolleau@256 71 if (haveFilm)
benoitrigolleau@256 72 {
benoitrigolleau@256 73 haveFilm=1;
benoitrigolleau@256 74 SDL_ffmpegFree(film);
benoitrigolleau@256 75 }
ivand_qmul@125 76 // open file from arg[1]
ivand_qmul@125 77 film = SDL_ffmpegOpen(path.toLocal8Bit().data());
ivand_qmul@125 78 if (path.endsWith("mpg")) film->delay=1;
ivand_qmul@125 79 if (path.endsWith("divx")) film->delay=1;
ivand_qmul@125 80 if(!film)
lbajardsilogic@191 81 {
lbajardsilogic@191 82 m_error = QString("Failed to open file %1 for reading.").arg(path);
lbajardsilogic@191 83 return;
lbajardsilogic@191 84 }
ivand_qmul@125 85 // print some info on detected stream to output
ivand_qmul@125 86 film->skipAudio=0;
ivand_qmul@125 87 film->skipVideo=0;
ivand_qmul@125 88 int s;
ivand_qmul@125 89 SDL_ffmpegStream *str;
ivand_qmul@125 90 for(s = 0; s<film->VStreams; s++)
ivand_qmul@125 91 str = SDL_ffmpegGetVideoStream(film, s);
ivand_qmul@125 92 SDL_ffmpegSelectVideoStream(film, 0);
ivand_qmul@125 93 film->skipVideo=1;
ivand_qmul@125 94 for(s = 0; s<film->AStreams; s++)
ivand_qmul@125 95 str = SDL_ffmpegGetAudioStream(film, s);
lbajardsilogic@191 96
lbajardsilogic@191 97 SDL_ffmpegSelectAudioStream(film, 0);
lbajardsilogic@191 98
lbajardsilogic@191 99
lbajardsilogic@191 100 if (showProgress) {
lbajardsilogic@191 101 m_progress = new QProgressDialog
lbajardsilogic@191 102 (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
lbajardsilogic@191 103 QObject::tr("Stop"), 0, 100);
lbajardsilogic@191 104 m_progress->hide();
lbajardsilogic@191 105 }
lbajardsilogic@191 106
ivand_qmul@125 107 m_sampleRate=str->sampleRate;
ivand_qmul@125 108 int channels=str->channels;
ivand_qmul@125 109 m_channelCount=channels;
ivand_qmul@125 110 SDL_ffmpegPause(film, 0);
ivand_qmul@125 111 SDL_ffmpegStartDecoding(film);
ivand_qmul@125 112
lbajardsilogic@191 113 if (!decodeAudio(film)) {
lbajardsilogic@191 114 m_error = QString("Failed to decode audio from file %1 for reading.").arg(path);
lbajardsilogic@191 115 return;
ivand_qmul@125 116 }
ivand_qmul@125 117 film->skipAudio=1;
ivand_qmul@125 118 //SDL_ffmpegSeek(film, 0);
ivand_qmul@125 119 //SDL_ffmpegStopDecoding(film);
ivand_qmul@125 120 SDL_Delay(5);
ivand_qmul@125 121 film->skipVideo=0;
ivand_qmul@125 122
ivand_qmul@125 123 film->videoThread=SDL_CreateThread(videoPlayCall,this);
lbajardsilogic@191 124
lbajardsilogic@191 125
lbajardsilogic@191 126
lbajardsilogic@191 127
lbajardsilogic@191 128
lbajardsilogic@191 129 if (showProgress) {
lbajardsilogic@191 130 delete m_progress;
lbajardsilogic@191 131 m_progress = 0;
lbajardsilogic@191 132 }
lbajardsilogic@191 133
lbajardsilogic@191 134 //delete[] filebuffer;
lbajardsilogic@191 135 }
lbajardsilogic@191 136
lbajardsilogic@191 137
lbajardsilogic@191 138 VideoFileReader::~VideoFileReader()
lbajardsilogic@191 139 {
lbajardsilogic@191 140 film->videoThreadActive = 0;
lbajardsilogic@191 141 if(film->videoThread) SDL_WaitThread(film->videoThread, 0);
lbajardsilogic@191 142 }
lbajardsilogic@191 143 bool
lbajardsilogic@191 144 VideoFileReader::decodeAudio(SDL_ffmpegFile* file)
lbajardsilogic@191 145 {
ivand_qmul@125 146
ivand_qmul@125 147
ivand_qmul@125 148 int64_t duration=((AVFormatContext *)file->_ffmpeg)->duration;
ivand_qmul@125 149 double elapsed = 0;
ivand_qmul@125 150 m_cancelled=false;
ivand_qmul@125 151 int audio_ends=0;
ivand_qmul@125 152 while((elapsed < duration)&&!m_cancelled ) {
lbajardsilogic@191 153
lbajardsilogic@191 154 elapsed = double(m_frameCount)*1000000 /(m_channelCount*m_sampleRate);
lbajardsilogic@191 155 double percent = (elapsed * 100) / duration;
lbajardsilogic@191 156 int progress = int(percent);
lbajardsilogic@191 157 if (progress < 1) progress = 1;
lbajardsilogic@191 158 if (progress > 99) progress = 99;
lbajardsilogic@191 159 if (progress > m_progress->value()) {
lbajardsilogic@191 160 m_progress->setValue(progress);
lbajardsilogic@191 161 m_progress->show();
lbajardsilogic@191 162 m_progress->raise();
lbajardsilogic@191 163 qApp->processEvents();
lbajardsilogic@191 164 if (m_progress->wasCanceled()) {
lbajardsilogic@191 165 m_cancelled = true;
lbajardsilogic@191 166 }
lbajardsilogic@191 167 }
lbajardsilogic@191 168
ivand_qmul@125 169 // we tell SDL_ffmpegGetAudio how many bytes we need, the function then
ivand_qmul@125 170 // fills this pointer with the amount of bytes it could actually give
ivand_qmul@125 171 int gotLength = 100000;
lbajardsilogic@191 172 if (!isDecodeCacheInitialised()) {
lbajardsilogic@191 173 initialiseDecodeCache();
ivand_qmul@125 174 }
ivand_qmul@125 175 // we try to get some data from our file
ivand_qmul@125 176 // important! this call is paired with SDL_ffmpegReleaseAudio
ivand_qmul@125 177 int16_t* audio =(int16_t *) SDL_ffmpegGetAudio(file, &gotLength);
ivand_qmul@125 178 for (int i=0; i<gotLength/2;i++)
ivand_qmul@125 179 {
ivand_qmul@125 180 float sample=float(*audio++)/float(2*32768);
ivand_qmul@125 181 addSampleToDecodeCache(sample);
ivand_qmul@125 182 }
ivand_qmul@125 183 // copy the bytes we got to audiocard
ivand_qmul@125 184 // if(audio) memcpy(stream, audio, gotLength);
ivand_qmul@125 185
ivand_qmul@125 186 // we release our audio data, so the decode thread can fill it again
ivand_qmul@125 187 // we also inform this function of the amount of bytes we used, so it can
ivand_qmul@125 188 // move the buffer accordingly
ivand_qmul@125 189 // important! this call is paired with SDL_ffmpegGetAudio
ivand_qmul@125 190 SDL_ffmpegReleaseAudio(file, gotLength);
ivand_qmul@125 191 //decode_audio(film, 1000000);
ivand_qmul@125 192 m_frameCount+=gotLength/2;
ivand_qmul@125 193 if ((progress > 97)&&(gotLength<=0)) audio_ends++;
ivand_qmul@125 194 if (audio_ends>=2000) m_cancelled = true;
ivand_qmul@125 195 }
ivand_qmul@125 196 m_frameCount/=m_channelCount;
ivand_qmul@125 197 if (isDecodeCacheInitialised()) finishDecodeCache();
lbajardsilogic@191 198 return true;
lbajardsilogic@191 199 }
lbajardsilogic@191 200 bool
lbajardsilogic@191 201 VideoFileReader::videoInit(SDL_ffmpegFile* file)
lbajardsilogic@191 202 {
ivand_qmul@125 203 int w,h;
ivand_qmul@125 204 // we get the size from our active video stream, if no active video stream
ivand_qmul@125 205 // exists, width and height are set to default values (320x240)
ivand_qmul@125 206 SDL_ffmpegGetVideoSize(file, &w, &h);
benoitrigolleau@145 207
benoitrigolleau@145 208 MainWindow * MWins=MainWindow::instance();
benoitrigolleau@167 209
benoitrigolleau@145 210
benoitrigolleau@145 211
benoitrigolleau@130 212 SDL_Init(SDL_INIT_VIDEO);
benoitrigolleau@167 213 Videow=w;//=320*m_zoomfactor;
benoitrigolleau@167 214 Videoh=h;//=240*m_zoomfactor;
benoitrigolleau@167 215
benoitrigolleau@167 216 //TODO update the size according to the component size
benoitrigolleau@167 217 // this will not work if video size is bigger than window size.
benoitrigolleau@167 218 m_width =w;
benoitrigolleau@167 219 m_height =h;
benoitrigolleau@256 220 zoomWivan =w;
benoitrigolleau@256 221 zoomHivan =h;
benoitrigolleau@167 222 MWins->setSDLInitSize(w,h);
ivand_qmul@125 223 // Open the Video device
ivand_qmul@150 224 screen = SDL_SetVideoMode(w, h, 0, SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL);
benoitrigolleau@130 225 //SDL_WM_SetCaption("EASAIER Video Player", "EASAIER Video Player");
ivand_qmul@125 226 if(!screen) {
benoitrigolleau@167 227 fprintf(stderr, "Couldn't open video: %s\n", SDL_GetError());
ivand_qmul@125 228 return false;
lbajardsilogic@191 229 }
lbajardsilogic@191 230 return true;
lbajardsilogic@191 231 }
lbajardsilogic@191 232
ivand_qmul@125 233 int VideoFileReader::videoPlayCall(void *t)
ivand_qmul@125 234 {
ivand_qmul@125 235 return ((VideoFileReader *)t)->videoPlay();
lbajardsilogic@191 236 }
lbajardsilogic@191 237
benoitrigolleau@167 238 void VideoFileReader::processEvents()
benoitrigolleau@167 239 {
benoitrigolleau@167 240 SDL_Event event;
benoitrigolleau@167 241
benoitrigolleau@167 242 while (SDL_PollEvent(&event))
benoitrigolleau@167 243 {
benoitrigolleau@167 244 float signe = 1;
benoitrigolleau@167 245 switch (event.type)
benoitrigolleau@167 246 {
benoitrigolleau@167 247 case SDL_MOUSEMOTION:
benoitrigolleau@167 248 break;
benoitrigolleau@167 249 case SDL_MOUSEBUTTONDOWN:
benoitrigolleau@167 250 switch(event.button.button){
benoitrigolleau@167 251 case SDL_BUTTON_LEFT :
benoitrigolleau@167 252 break;
benoitrigolleau@167 253 case SDL_BUTTON_MIDDLE:
benoitrigolleau@167 254 break;
benoitrigolleau@167 255 case SDL_BUTTON_RIGHT:
benoitrigolleau@167 256 break;
benoitrigolleau@167 257 default:
benoitrigolleau@167 258 break;
benoitrigolleau@167 259 }
benoitrigolleau@167 260 break;
benoitrigolleau@167 261 case SDL_MOUSEBUTTONUP:
benoitrigolleau@167 262
benoitrigolleau@167 263 switch(event.button.button){
benoitrigolleau@167 264 case SDL_BUTTON_LEFT :
benoitrigolleau@167 265 break;
benoitrigolleau@167 266 case SDL_BUTTON_MIDDLE:
benoitrigolleau@167 267 break;
benoitrigolleau@167 268 case SDL_BUTTON_RIGHT:
benoitrigolleau@167 269 break;
benoitrigolleau@167 270 case SDL_BUTTON_WHEELDOWN:
benoitrigolleau@167 271 signe = -1;
benoitrigolleau@167 272 case SDL_BUTTON_WHEELUP:
benoitrigolleau@167 273 float f,f2;
benoitrigolleau@167 274 f = m_zoomfactor + signe * 0.1;
benoitrigolleau@167 275 f2 = f;
benoitrigolleau@167 276 if((int)(m_width*f+0.5)%4){
benoitrigolleau@167 277 f = (m_width*f - (int)(m_width*f+0.5)%4)/m_width;
benoitrigolleau@167 278 }
benoitrigolleau@167 279 m_zoomfactor=f;
benoitrigolleau@167 280
benoitrigolleau@167 281 break;
benoitrigolleau@167 282 default:
benoitrigolleau@167 283 break;
benoitrigolleau@167 284 }
benoitrigolleau@167 285 break;
benoitrigolleau@167 286
benoitrigolleau@167 287 default:
benoitrigolleau@167 288 break;
benoitrigolleau@167 289 }
benoitrigolleau@167 290 }
lbajardsilogic@191 291 }
lbajardsilogic@191 292
lbajardsilogic@191 293 int
lbajardsilogic@191 294 VideoFileReader::videoPlay()
lbajardsilogic@191 295 {
ivand_qmul@125 296
ivand_qmul@125 297
ivand_qmul@125 298 film->videoThreadActive = 1;
ivand_qmul@125 299 MainWindow * MWins=MainWindow::instance();
ivand_qmul@125 300
ivand_qmul@125 301 if (!videoInit(film)) {
lbajardsilogic@191 302 m_error = QString("Failed to failed to initalized video file for reading.");
lbajardsilogic@191 303 return 0;
ivand_qmul@125 304 }
ivand_qmul@125 305 //const SDL_VideoInfo * vid=SDL_GetVideoInfo();
ivand_qmul@125 306 film->audioTime =0;
ivand_qmul@125 307 int w,h;
ivand_qmul@125 308 SDL_ffmpegGetVideoSize(film, &w, &h);
ivand_qmul@125 309 //SDL_ffmpegStartDecoding(film);
ivand_qmul@125 310 SDL_Delay(1000);
ivand_qmul@129 311 QueryPerformanceFrequency((LARGE_INTEGER *)(&countFreq));
ivand_qmul@129 312 countFreq/=1000;
ivand_qmul@129 313 film->countFreq=countFreq;
ivand_qmul@129 314 QueryPerformanceCounter((LARGE_INTEGER *)(&film->timer));
ivand_qmul@129 315 film->timer=film->timer/(film->countFreq);
ivand_qmul@129 316 film->timebase=1;
ivand_qmul@129 317 film->vs[film->videoStream]->lastTimeStamp=0;
ivand_qmul@125 318 while( film->videoThreadActive ) {
ivand_qmul@125 319
benoitrigolleau@167 320 if (!(w==(int)(Videow*m_zoomfactor))){
benoitrigolleau@167 321 w=Videow*m_zoomfactor;
benoitrigolleau@167 322 h=Videoh*m_zoomfactor;
benoitrigolleau@167 323 if (w<80 || h<60){
benoitrigolleau@167 324 w=80;
benoitrigolleau@167 325 h=60;
benoitrigolleau@167 326 m_zoomfactor = 80.0/Videow;
benoitrigolleau@167 327 }
benoitrigolleau@167 328 // Open the Video device
benoitrigolleau@256 329 //zoomFivan = m_zoomfactor;
benoitrigolleau@167 330 screen = SDL_SetVideoMode(w, h, 0, SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL);
benoitrigolleau@167 331 MWins->setSDLInitSize(w,h);
benoitrigolleau@167 332 m_width =w;
benoitrigolleau@167 333 m_height =h;
benoitrigolleau@256 334 zoomWivan =w;
benoitrigolleau@256 335 zoomHivan =h;
benoitrigolleau@167 336
ivand_qmul@150 337 }
benoitrigolleau@167 338 // calls process events function, to take in account the user input
benoitrigolleau@167 339 processEvents();
ivand_qmul@125 340 if (MWins->isAudioPlaying())
ivand_qmul@125 341 {
ivand_qmul@125 342 if ((SDL_ffmpegGetState(film))||((long)(abs((long)(film->audioTime - (int64_t)(MWins->Get_CurAudioTime()))))>=1000))
ivand_qmul@125 343 {
ivand_qmul@125 344 //SDL_Delay(1000);
ivand_qmul@125 345 film->audioTime = MWins->Get_CurAudioTime();
ivand_qmul@125 346 SDL_ffmpegSeek(film, film->audioTime);
ivand_qmul@125 347 SDL_ffmpegPause(film, 0);
ivand_qmul@125 348 }
ivand_qmul@125 349 else
ivand_qmul@125 350 film->audioTime = MWins->Get_CurAudioTime();
ivand_qmul@125 351 }
ivand_qmul@125 352 else
ivand_qmul@125 353 {
ivand_qmul@125 354 SDL_ffmpegPause(film, 1);
ivand_qmul@125 355
ivand_qmul@125 356 }
ivand_qmul@125 357
ivand_qmul@125 358 // we retrieve the current image from the file
ivand_qmul@125 359 // we get 0 if no file could be retrieved
ivand_qmul@125 360 // important! please note this call should be paired with SDL_ffmpegReleaseVideo
ivand_qmul@125 361 SDL_Surface* bmp = SDL_ffmpegGetVideo((SDL_ffmpegFile *)film);
ivand_qmul@125 362
ivand_qmul@125 363 if(bmp) {
ivand_qmul@125 364
ivand_qmul@125 365
ivand_qmul@125 366 // we got a frame, so we better show this one
ivand_qmul@125 367 SDL_BlitSurface(bmp, 0, screen, 0);
ivand_qmul@125 368
ivand_qmul@125 369 // we flip the double buffered screen so we might actually see something
ivand_qmul@125 370 SDL_Flip(screen);
ivand_qmul@125 371
ivand_qmul@125 372 // After releasing bmp, you can no longer use it.
ivand_qmul@125 373 // you should call this function every time you get a frame!
ivand_qmul@125 374 SDL_ffmpegReleaseVideo((SDL_ffmpegFile *)film, bmp);
ivand_qmul@125 375 }
ivand_qmul@125 376
ivand_qmul@125 377 // we wish not to kill our poor cpu, so we give it some timeoff
ivand_qmul@129 378 // SDL_Delay(1);
lbajardsilogic@191 379 }
ivand_qmul@125 380 // after all is said and done, we should call this
lbajardsilogic@191 381 SDL_ffmpegFree(film);
lbajardsilogic@191 382 return 0;
lbajardsilogic@191 383 }
lbajardsilogic@191 384
lbajardsilogic@191 385 void
lbajardsilogic@191 386 VideoFileReader::getSupportedExtensions(std::set<QString> &extensions)
lbajardsilogic@191 387 {
lbajardsilogic@191 388 extensions.insert("mpg");
lbajardsilogic@191 389 extensions.insert("avi");
lbajardsilogic@191 390 extensions.insert("divx");
lbajardsilogic@191 391 extensions.insert("mov");
lbajardsilogic@191 392 }
lbajardsilogic@191 393
lbajardsilogic@191 394 #endif