Video.cpp 17.4 KB
Newer Older
daFischer's avatar
first  
daFischer committed
1 2
/* 
 * File:   Video.cpp
3
 * Author: Johannes Fischer
daFischer's avatar
first  
daFischer committed
4 5 6 7 8
 * 
 * Created on April 30, 2014, 8:36 PM
 */

#include "Video.h"
9 10
#include "Controls.h"
#include "Messages/Annotation.h"
11
#include "Messages/CursorMessage.h"
daFischer's avatar
daFischer committed
12
#include "Messages/WhiteboardMessage.h"
daFischer's avatar
first  
daFischer committed
13

daFischer's avatar
daFischer committed
14
Video::Video(const char* path) {
daFischer's avatar
first  
daFischer committed
15 16
    
    original=true;
17
    failed=true;
18
    FILE* f = fopen(path, "r");
daFischer's avatar
daFischer committed
19 20 21 22 23
    progress=0;
    fseek(f,0,SEEK_END);
    fileSize=ftell(f);
    fseek(f,0,SEEK_SET);
    
daFischer's avatar
daFischer committed
24
    fread(prefs.versionMsg, 1, 12, f);
daFischer's avatar
first  
daFischer committed
25
    // TODO: test version
daFischer's avatar
daFischer committed
26 27
    if(VERBOSE)
        printf("File Version: %s",prefs.versionMsg);
daFischer's avatar
first  
daFischer committed
28
    
daFischer's avatar
daFischer committed
29
    inflater=new Inflater(f);
daFischer's avatar
first  
daFischer committed
30 31
    
    if(readServerInit(inflater))
daFischer's avatar
daFischer committed
32 33
        if(VERBOSE)
            printf("Video Initialization success: \n%s\n", prefs.name);
34

daFischer's avatar
daFischer committed
35
    index=NULL;
daFischer's avatar
daFischer committed
36
    loadPhase=0;
37 38 39 40 41 42
    
    if (ProtocolPreferences::bitsPerPixel!=16)
    {
        printf("Error, only 16 bit videos are supported.\n");
        loadPhase=100;//error state
    }
daFischer's avatar
daFischer committed
43 44
}

45 46 47 48
/**
 * Without emscripten: prints "x%" when x% of the .ttt file have been read.
 * with emscripten: sets the progress of an html5 progress bar
 */
daFischer's avatar
daFischer committed
49 50
void Video::showProgress() {
    if(loadPhase<=7)
51
    {
daFischer's avatar
daFischer committed
52 53 54 55 56 57 58 59 60 61 62
        long int old=progress;
        progress=inflater->getProgress();
        if(progress/(fileSize/100)>old/(fileSize/100))
#ifdef EMSCRIPTEN
            EM_ASM_INT({
                x_setProgress($0);
                return 0;
            },progress/(fileSize/100));
#else
            printf("%ld percent\n",progress/(fileSize/100));
#endif
63
    }
daFischer's avatar
daFischer committed
64 65
}

66 67 68 69
/**
 * Completely parses the .ttt File asynchronously
 * @return true if there is still something left to load, false if loading has finished (failed or successful)
 */
daFischer's avatar
daFischer committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
bool Video::loadAsync() {
    showProgress();
    long int old;
    switch(loadPhase){
        case 0:
            readExtension(inflater);
            break;
        case 1: //IndexExtension has been found
            for(int i=0;i<10;i++)
            if(!index->readIndexEntry(inflater))
                {
                    loadPhase=0;    // read the next extension
                    break;
                }
            break;
            
             //keeping space for extensions that might be added in the future
        case 5:
            if(inflater->readInt(&prefs.starttime)!=Z_OK)
                return false;
            inflater->readInt(&prefs.starttime);
            //printf("Sizeof long = %d\n",sizeof(long));

            //Start reading Messages
            loadPhase++;
        case 6:
            old=progress;
            do{
                for(int i=0;i<30;i++)
                    if(!readMessages(&m, inflater, &prefs))
                    {
                        loadPhase++;
                        return failed;
                    }
            }while(inflater->getProgress()/(fileSize/100)<=old/(fileSize/100));
            break;
        case 7:
            delete(inflater);
            inflater=NULL;
            numMessages=m.size();
            messages=(Message**) malloc(numMessages*sizeof(Message*));
            currentMessage=0;
            for(int i=0; i<numMessages; i++)
            {
                messages[i] = m.front();
                m.pop_front();
            }

            if(VERBOSE)
                printf("Read %d messages successfully\n", numMessages);

            if(VERBOSE)
                printf("%d x %d, color depth: %d\n",prefs.framebufferWidth, prefs.framebufferHeight, prefs.bytesPerPixel);
123
            screen = SDL_SetVideoMode(prefs.framebufferWidth, prefs.framebufferHeight+48, prefs.bitsPerPixel, SDL_ANYFORMAT|SDL_DOUBLEBUF);
daFischer's avatar
daFischer committed
124
            //prefs.format->Amask=0xffffffff-screen->format->Rmask-screen->format->Gmask-screen->format->Bmask;
125
            rawScreen=SDL_CreateRGBSurface(screen->flags,screen->w,prefs.framebufferHeight,screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0xffffffff-screen->format->Rmask-screen->format->Gmask-screen->format->Bmask);
daFischer's avatar
daFischer committed
126 127 128
            prefs.format=rawScreen->format;
            if(containsAnnotations)
            {
129
                annScreen=SDL_CreateRGBSurface(screen->flags,screen->w,prefs.framebufferHeight,screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0xffffffff-screen->format->Rmask-screen->format->Gmask-screen->format->Bmask);
daFischer's avatar
daFischer committed
130 131 132 133 134 135 136 137 138 139
            }

            if (screen == NULL)
            {
                printf("Unable to set video mode: %s\n", SDL_GetError());
                return false;
            }
            if(index==NULL)
                index=new Index(messages,numMessages);
            lastThumbnail.w=0;
140 141
            loadPhase=9;
        /*case 8:               //Inefficient, instead using gradual or "Just In Time" (when needed) filling of Surfaces
daFischer's avatar
daFischer committed
142 143 144 145 146 147
            for(int i=0;i<3;i++)
                if(!index->fillSurface(screen,messages,numMessages,&prefs))
                {
                    loadPhase=9;
                    break;
                }
148
            break;*/
daFischer's avatar
daFischer committed
149
        case 9:
150
            lastTime=-1;
daFischer's avatar
daFischer committed
151 152
            failed=false;
            break;
153 154 155 156 157 158
            
            
        case 100:               //error state
            failed=true;
            return false;
            break;
daFischer's avatar
first  
daFischer committed
159
    }
daFischer's avatar
daFischer committed
160
    return failed;
daFischer's avatar
first  
daFischer committed
161 162
}

163 164 165 166 167
/**
 * Handles messages that have to be drawn and updates the screen
 * @param time position in the audio stream, used to determine which messages have to paint
 * @param controls the Controls object, needs to be drawn to the screen as well
 */
168
void Video::update(int time, Controls* controls)
daFischer's avatar
first  
daFischer committed
169
{
170 171
    bool blitRaw=false;
    bool blitAnn=false;
daFischer's avatar
daFischer committed
172
    //check whether audio has restarted
173
    if(time<=2000 && (currentMessage>0 && messages[currentMessage-1]->timestamp>time+5000))
daFischer's avatar
daFischer committed
174 175
        currentMessage=0;
    
176
    int updatedArea=0;
177
    
daFischer's avatar
first  
daFischer committed
178 179
    while(currentMessage<numMessages)
    {
180
        if(messages[currentMessage]->timestamp > time)
daFischer's avatar
first  
daFischer committed
181
            break;
182 183 184
        switch(messages[currentMessage]->type){
            case ANNOTATION:
                blitAnn=true;
daFischer's avatar
daFischer committed
185
                messages[currentMessage]->paint(annScreen, &prefs);
186
                break;
187
            case FRAMEBUFFER:
188 189
                blitRaw=true;
                messages[currentMessage]->paint(rawScreen, &prefs);
190
                updatedArea+=messages[currentMessage]->getArea();
191 192
                break;
            case CURSOR:
193
                SDL_Rect cm=CursorMessage::getMask();
194
                cm.x=max(min((int)cm.x,(int)screen->w-cm.w),0);
195
                cm.y=max(min((int)cm.y,(int)prefs.framebufferHeight-cm.h),0);
196 197 198 199 200 201 202
                if(CursorMessage::showCursor){
                    // repair place on screen, where the cursor was
                    SDL_BlitSurface(rawScreen,&cm,screen,&cm);
                    SDL_BlitSurface(annScreen,&cm,screen,&cm);
                }
                messages[currentMessage]->paint(screen, &prefs);
                //printf("CURSOR encoding = %d\n",messages[currentMessage]->encoding);
203 204
                if(CursorMessage::showCursor)
                    SDL_BlitSurface(CursorMessage::cursor,NULL,screen,&cm);
205 206
                break;
        }
daFischer's avatar
first  
daFischer committed
207 208
        currentMessage++;
    }
209 210 211
    if(blitAnn)
    {
        //redraw annScreen if needed, doesn't matter which annotation does it
daFischer's avatar
daFischer committed
212 213
        if(Annotation::mustRedraw)
            Annotation::redraw(annScreen,&prefs);
214
    }
215 216 217 218
    redrawScreen(controls, blitAnn || blitRaw);

    index->fillSurface(rawScreen,messages,numMessages,screen->w*prefs.framebufferHeight-updatedArea,&prefs);
    controls->progress=index->progress;
daFischer's avatar
first  
daFischer committed
219 220
}

221 222 223 224
void Video::redrawScreen(Controls* controls, bool fully) {
    if(fully)
    {
        //redraw screen completely
225
        SDL_Rect rect = {0,0,screen->w,prefs.framebufferHeight};
daFischer's avatar
daFischer committed
226 227 228 229
        if(WhiteboardMessage::number>0)
            SDL_FillRect(screen,&rect,SDL_MapRGBA(ProtocolPreferences::format,0xff,0xff,0xff,0xff));
        else
            SDL_BlitSurface(rawScreen,&rect,screen,&rect);
230
        SDL_BlitSurface(annScreen,&rect,screen,&rect);
231 232 233 234 235
        if(CursorMessage::showCursor)
        {
            SDL_Rect cm=CursorMessage::getMask();
            SDL_BlitSurface(CursorMessage::cursor,NULL,screen,&cm);
        }
236
    }
daFischer's avatar
daFischer committed
237
    else
238
    {
daFischer's avatar
daFischer committed
239 240 241
        if(lastThumbnail.w!=0)
        {
            //redraw screen where thumbnail was before
daFischer's avatar
daFischer committed
242 243 244 245
            if(WhiteboardMessage::number>0)
                SDL_FillRect(screen,&lastThumbnail,SDL_MapRGBA(ProtocolPreferences::format,0xff,0xff,0xff,0xff));
            else
                SDL_BlitSurface(rawScreen,&lastThumbnail,screen,&lastThumbnail);
daFischer's avatar
daFischer committed
246
            SDL_BlitSurface(annScreen,&lastThumbnail,screen,&lastThumbnail);
247 248 249 250 251
            if(CursorMessage::showCursor)
            {
                SDL_Rect cm=CursorMessage::getMask();
                SDL_BlitSurface(CursorMessage::cursor,NULL,screen,&cm);
            }
daFischer's avatar
daFischer committed
252 253
            lastThumbnail.w=0;
        }
254
        /*if(controls->videoUpdate.h!=0)
daFischer's avatar
daFischer committed
255 256
        {
            //only redraw the part that controls releases when moving downwards
daFischer's avatar
daFischer committed
257 258 259 260
            if(WhiteboardMessage::number>0)
                SDL_FillRect(screen,&controls->videoUpdate,SDL_MapRGBA(ProtocolPreferences::format,0xff,0xff,0xff,0xff));
            else
                SDL_BlitSurface(rawScreen,&controls->videoUpdate,screen,&controls->videoUpdate);
daFischer's avatar
daFischer committed
261
            SDL_BlitSurface(annScreen,&controls->videoUpdate,screen,&controls->videoUpdate);
262 263 264 265 266
            if(CursorMessage::showCursor)
            {
                SDL_Rect cm=CursorMessage::getMask();
                SDL_BlitSurface(CursorMessage::cursor,NULL,screen,&cm);
            }
267
        }*/
268 269
    }
    //always redraw controls
270
    controls->draw(screen);
daFischer's avatar
daFischer committed
271 272 273
    SDL_Flip(screen);
}

274 275 276 277 278 279
/**
 * Called by Controls, draws the last thumbnail before timestamp on the given position
 * @param timestamp
 * @param x
 * @param y
 */
280
void Video::drawThumbnail(int timestamp, int x, int y) {
daFischer's avatar
daFischer committed
281 282
    if(index!=NULL)
    {
283 284
        index->lastBefore(timestamp)->paintThumbnail(screen,x,y);
        lastThumbnail=index->lastBefore(timestamp)->getRect(screen,x,y);
daFischer's avatar
daFischer committed
285
    }
286 287
}

288 289 290 291
/**
 * This is only being called, when no Emscripten has been used.
 * Should toggle the Fullscreen mode, but doesn't work yet.
 */
292 293 294 295 296 297 298 299
void Video::toggleFullscreen(){
    return; //doesn't work yet, but not really important
    {
        printf("toggle fullscreen %d,%d\n",screen->flags,screen->flags & SDL_FULLSCREEN);
        screen=SDL_SetVideoMode(screen->w, screen->h, screen->format->BitsPerPixel, screen->flags ^ SDL_FULLSCREEN);
    }
}

300 301 302 303 304
/**
 * This is used when the user jumps to a different position in the Video.
 * @param position the position to jump to in milliseconds
 * @param controls needed for redrawing
 */
305
void Video::seekPosition(int position, Controls* controls){
306 307 308
    
    //binary search for message closest to position
    int min, max;
309
    min=1;
310
    max= numMessages;
311 312
    
    //use binary search to find the last message before position
313
    while(min!=max)
314
        if(messages[(min+max)/2]->timestamp > position)
315 316 317
            max=(min+max)/2;
        else
            min=(min+max)/2+1;
daFischer's avatar
daFischer committed
318
    min--;
319
    if(VERBOSE)
320
        printf("Seeking position at %d seconds. Last message was at %d seconds.\n",position/1000,messages[min]->timestamp/1000);
321
    
322 323
    //find the last IndexEntry before position and load its Surface if not done already
    IndexEntry* indexEntry=index->lastBefore(position);
324
    int lastEntry=indexEntry->timestamp;
325 326 327 328 329 330 331
    if(VERBOSE)
        printf("Last IndexEntry was at %d seconds\n",lastEntry/1000);
    if(!indexEntry->hasImages)
    {
        index->loadUntil(indexEntry,screen,messages,numMessages,&prefs);
        controls->progress=index->progress;
    }
332
    
333
    //cancel WhiteBoard effects
daFischer's avatar
daFischer committed
334
    WhiteboardMessage::number=std::min(WhiteboardMessage::number,0);
335 336
    int firstAnnotation=0;
    int firstRaw=0;
337
    char foundCursor=0;
daFischer's avatar
daFischer committed
338
    for(int i= min;i>=0;i--)
339
    {
340
        //continue the search until the first message to redraw has been found for all types
341
        if(     (firstRaw!=0 || (lastEntry >= messages[i]->timestamp)) &&
342
                (firstAnnotation!=0 || !containsAnnotations) &&
343
                (foundCursor==3 || !containsCursorMessages))
daFischer's avatar
daFischer committed
344 345 346
        {
            firstRaw=std::max(i,firstRaw);
            firstAnnotation=std::max(i,firstAnnotation);
347
            break;
daFischer's avatar
daFischer committed
348
        }
daFischer's avatar
daFischer committed
349 350 351 352 353 354
        if(i==currentMessage && currentMessage<=min)
        {
            firstRaw=std::max(i,firstRaw);
            firstAnnotation=std::max(i,firstAnnotation);
            break;
        }
355 356 357 358 359 360
        
        switch(messages[i]->type){
            case ANNOTATION:
                if(firstAnnotation==0 && messages[i]->completeScreen(prefs.framebufferWidth, prefs.framebufferHeight))
                    firstAnnotation=i;
                break;
361
            case FRAMEBUFFER:
362
                if(firstRaw==0 && ((lastEntry >= messages[i]->timestamp) || messages[i]->completeScreen(prefs.framebufferWidth, prefs.framebufferHeight)))
daFischer's avatar
daFischer committed
363
                    firstRaw=i;
364 365
                break;
            case CURSOR:
366 367 368 369 370 371 372 373 374
                if(foundCursor % 2 == 0 && messages[i]->encoding==ENCODINGTTTCURSORPOSITION)
                {
                    //Is a CursorPositionMessage
                    messages[i]->paint(screen,&prefs);
                    foundCursor |= 1;
                }
                else if(foundCursor < 2 && messages[i]->encoding!=ENCODINGTTTCURSORPOSITION)
                {
                    //Is a CursorMessage
375
                    messages[i]->paint(screen,&prefs);
376 377
                    foundCursor |= 2;
                }
378 379 380
                break;
        }
    }
381 382 383 384 385 386 387 388 389
    //WhiteboardMessages are not deleted by DeleteAllAnnotation. Therefore we have to search for them separately
    if(containsWhiteboard)
        for(int i= min;i>=0;i--)
            if(messages[i]->encoding==ENCODINGWHITEBOARD)
            {
                messages[i]->paint(annScreen,&prefs);
                break;
            }
    
390 391
    if(VERBOSE)
        printf("Redrawing raw messages beginning at %d s,\nAnnotations at %d s\n\n",messages[firstRaw]->timestamp/1000,messages[firstAnnotation]->timestamp/1000);
392
    
393
    if(lastEntry>=messages[firstRaw]->timestamp)
394 395 396
    {
        indexEntry->paintWaypoint(rawScreen);
    }
daFischer's avatar
daFischer committed
397
    for(int i=firstRaw;i<min;i++)
398
        if(messages[i]->type==FRAMEBUFFER)
399 400
            messages[i]->paint(rawScreen,&prefs);
    
daFischer's avatar
daFischer committed
401 402 403 404 405 406 407 408 409 410 411 412 413
    if(containsAnnotations){
        if(firstAnnotation!=currentMessage)
        {
            //after the search, Annotations must be redrawn
            Annotation::mustRedraw=true;
            Annotation::annotations.clear();
        }
        for(int i=firstAnnotation;i<min;i++)
            if(messages[i]->type==ANNOTATION)
                messages[i]->paint(annScreen,&prefs);

        if(firstAnnotation!=currentMessage||Annotation::mustRedraw)
            Annotation::redraw(annScreen,&prefs);
daFischer's avatar
daFischer committed
414 415 416
    }
    
    currentMessage=min;
417 418 419
    
    redrawScreen(controls,true);
    
420 421
}

422 423 424 425 426
/**
 * Reads a few global values from the File
 * @param in The Inflater object used to inflate the file
 * @return returns true if there weren't any errors
 */
daFischer's avatar
first  
daFischer committed
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
bool readServerInit(Inflater* in)
{
    ProtocolPreferences prefs;
    in->readShort(&prefs.framebufferWidth);
    in->readShort(&prefs.framebufferHeight);
    in->readByte(&prefs.bitsPerPixel);
    switch(prefs.bitsPerPixel){
        case 8:
            prefs.bytesPerPixel=1;
            break;
        case 16:
            prefs.bytesPerPixel=2;
            break;
        default:
            prefs.bytesPerPixel=4;
            break;
    }
    in->readByte(&prefs.depth);
    char toRead;
    in->readByte(&toRead);
    prefs.bigEndian = toRead != 0;
    in->readByte(&toRead);
    prefs.trueColour = toRead != 0;
    in->readShort(&prefs.redMax);
    in->readShort(&prefs.greenMax);
    in->readShort(&prefs.blueMax);
    in->readByte(&prefs.redShift);
    in->readByte(&prefs.greenShift);
    in->readByte(&prefs.blueShift);
    
daFischer's avatar
daFischer committed
457
    in->skipBytes(3);        //padding
daFischer's avatar
first  
daFischer committed
458 459 460 461
    
    int nameLength;
    if(in->readInt(&nameLength)==Z_OK)
    {
daFischer's avatar
daFischer committed
462
        prefs.name=in->readCharArray(nameLength,true);
daFischer's avatar
daFischer committed
463 464
        
        // For some reason sometimes nameLength is 1 larger than it should be
465
        // In that case we have to give a byte back, or the Inflater becomes corrupted.
daFischer's avatar
daFischer committed
466
        if(nameLength>strlen(prefs.name))
467
            in->addChar(prefs.name[nameLength-1]);
daFischer's avatar
first  
daFischer committed
468 469 470 471 472
        return true;
    }
    return false;
}

daFischer's avatar
daFischer committed
473
void Video::readExtension(Inflater* in){
daFischer's avatar
daFischer committed
474 475 476
    int len;
    in->readInt(&len);
    char tag;
daFischer's avatar
daFischer committed
477
    if (len > 0) {
daFischer's avatar
daFischer committed
478 479 480
        in->readByte(&tag);
        switch(tag){
            case EXTENSION_INDEX_TABLE:
daFischer's avatar
daFischer committed
481
                //printf("read EXTENSION_INDEX_TABLE\n");
482
                int error;
daFischer's avatar
daFischer committed
483 484 485
                // no original, but modified recording
                original = false;
                index=new Index(in, len-1);
daFischer's avatar
daFischer committed
486
                loadPhase=1;
daFischer's avatar
daFischer committed
487 488
                break;
                
489
                //TODO: EXTENSION_SEARCHBASE_TABLE_WITH_COORDINATES
daFischer's avatar
daFischer committed
490 491 492 493 494 495 496 497 498
            /*case EXTENSION_SEARCHBASE_TABLE_WITH_COORDINATES:
                // no original, but modified recording
                original = false;
                
                break;*/
                
            default:
                //'tag' is the first byte, so skip one less than len
                printf("Unknown extension: %d. Skipping %d bytes.\n",tag,len-1);
499 500 501 502 503
                if(in->skipBytes(len-1)!=Z_OK)
                {
                    printf("killed by extensions\n");
                    return;
                }
daFischer's avatar
daFischer committed
504 505 506
                break;
        }
    }
daFischer's avatar
daFischer committed
507 508
    else
        loadPhase=5;
daFischer's avatar
first  
daFischer committed
509 510 511
}

Video::~Video() {
512 513 514 515 516 517 518 519 520 521 522
    SDL_FreeSurface(screen);
    SDL_FreeSurface(rawScreen);
    SDL_FreeSurface(annScreen);
    for(int i=0;i<numMessages;i++)
    {
        delete(messages[i]);
        messages[i]=NULL;
    }
    free(messages);
    messages=NULL;
    delete(index);
daFischer's avatar
first  
daFischer committed
523 524
}