mlv_play.c 87 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 
 */

/*
 * Copyright (C) 2013 Magic Lantern Team
 * 
 * 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.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

#include <module.h>
#include <dryos.h>
#include <property.h>
#include <bmp.h>
g3gg0's avatar
g3gg0 committed
29
#include <beep.h>
30
31
32
33
34
#include <menu.h>
#include <config.h>
#include <cropmarks.h>
#include <edmac.h>
#include <raw.h>
g3gg0's avatar
g3gg0 committed
35
#include <zebra.h>
36
#include <util.h>
37
#include <timer.h>
38
#include <shoot.h>
39

40
41
#include <string.h>

42
43
44
45
46
#include "../ime_base/ime_base.h"
#include "../trace/trace.h"
#include "../mlv_rec/mlv.h"
#include "../file_man/file_man.h"
#include "../lv_rec/lv_rec.h"
47
#include "../raw_twk/raw_twk.h"
48

49
50
51
/* uncomment for live debug messages */
//~ #define trace_write(trace, fmt, ...) { printf(fmt, ## __VA_ARGS__); printf("\n"); msleep(500); }

52
53
#define MAX_PATH                 100

54
55
56
57
/* declare the video.bmp we have added */
/* icon from: http://www.softicons.com/free-icons/system-icons/touch-of-gold-icons-by-skingcito/video-black-icon */
EXTLD(video_bmp);

58

59
/* only works if the raw_rec/mlv_rec module has its config variable non-static */
60
61
62
static int video_enabled_dummy = 0;
extern int WEAK_FUNC(video_enabled_dummy) raw_video_enabled;
extern int WEAK_FUNC(video_enabled_dummy) mlv_video_enabled;
63

64
65
static char *movie_filename_dummy = "";
extern char *WEAK_FUNC(movie_filename_dummy) raw_movie_filename;
66
extern char *WEAK_FUNC(movie_filename_dummy) mlv_movie_filename;
67
68
69
70

static int raw_exposure_adjust_dummy = 0;
extern int WEAK_FUNC(raw_exposure_adjust_dummy) raw_exposure_adjust;

71
72
static int res_x = 0;
static int res_y = 0;
73
static int frame_count = 0;
74
static int frame_size = 0;
75
static int bits_per_pixel = 0;
76
static unsigned int fps1000 = 0; /* used for RAW playback */
77

78
79
static volatile uint32_t mlv_play_render_abort = 0;
static volatile uint32_t mlv_play_rendering = 0;
80
static volatile uint32_t mlv_play_stopfile = 0;
81

82
static CONFIG_INT("play.quality", mlv_play_quality, 0); /* range: 0-1, RAW_PREVIEW_* in raw.h  */
83
static CONFIG_INT("play.exact_fps", mlv_play_exact_fps, 0);
84

85
86
87
static int mlv_play_zoom = 0;
static int mlv_play_zoom_x_pct = 0;
static int mlv_play_zoom_y_pct = 0;
88

89
90
static uint32_t mlv_play_trace_ctx = TRACE_ERROR;

91
92
93
94
95
96
97
98
/* OSD menu items */
#define MLV_PLAY_MENU_IDLE    0
#define MLV_PLAY_MENU_FADEIN  1
#define MLV_PLAY_MENU_FADEOUT 2
#define MLV_PLAY_MENU_SHOWN   3
#define MLV_PLAY_MENU_HIDDEN  4


g3gg0's avatar
g3gg0 committed
99
100
101
static uint32_t mlv_play_osd_state = MLV_PLAY_MENU_IDLE;
static int32_t mlv_play_osd_x = 360;
static int32_t mlv_play_osd_y = 0;
102
103
static uint32_t mlv_play_render_timestep = 10;
static uint32_t mlv_play_idle_timestep = 1000;
g3gg0's avatar
g3gg0 committed
104
static uint32_t mlv_play_osd_force_redraw = 0;
105
static uint32_t mlv_play_osd_idle = 1000;
g3gg0's avatar
g3gg0 committed
106
static uint32_t mlv_play_osd_item = 0;
107
static uint32_t mlv_play_paused = 0;
108
static uint32_t mlv_play_info = 1;
109
110
static uint32_t mlv_play_timer_stop = 1;
static uint32_t mlv_play_frames_skipped = 0;
111

112
113
114
115
116
/* this structure is used to build the mlv_xref_t table */
typedef struct 
{
    uint64_t    frameTime;
    uint64_t    frameOffset;
117
118
    uint16_t    fileNumber;
    uint16_t    frameType;
119
120
} frame_xref_t;

121
122
123
124
125
126
127
128
typedef struct
{
    char fullPath[MAX_PATH];
    uint32_t fileSize;
    uint32_t timestamp;
} playlist_entry_t;


129
#define SCREEN_MSG_LEN  64
130

131
132
133
134
135
136
137
138
139
140
141
142
typedef struct 
{
    char topLeft[SCREEN_MSG_LEN];
    char topRight[SCREEN_MSG_LEN];
    char botLeft[SCREEN_MSG_LEN];
    char botRight[SCREEN_MSG_LEN];
} screen_msg_t;

typedef struct 
{
    uint32_t frameSize;
    void *frameBuffer;
143
    void *frameBufferAligned;
144
145
146
147
    screen_msg_t messages;
    uint16_t xRes;
    uint16_t yRes;
    uint16_t bitDepth;
148
    uint16_t blackLevel;
149
150
151
152
153
} frame_buf_t;

/* set up two queues - one with empty buffers and one with buffers to render */
static struct msg_queue *mlv_play_queue_empty;
static struct msg_queue *mlv_play_queue_render;
g3gg0's avatar
g3gg0 committed
154
static struct msg_queue *mlv_play_queue_osd;
155
static struct msg_queue *mlv_play_queue_fps;
156

157
158
159
160
161
162
163
164
165
166
/* queue for playlist item submits by scanner tasks */
static struct msg_queue *mlv_playlist_queue;
static struct msg_queue *mlv_playlist_scan_queue;
static playlist_entry_t *mlv_playlist = NULL;
static uint32_t mlv_playlist_entries = 0;

static char mlv_play_next_filename[MAX_PATH];
static char mlv_play_current_filename[MAX_PATH];
static playlist_entry_t mlv_playlist_next(playlist_entry_t current);
static playlist_entry_t mlv_playlist_prev(playlist_entry_t current);
g3gg0's avatar
g3gg0 committed
167
static void mlv_playlist_delete(playlist_entry_t current);
168
static void mlv_playlist_build(uint32_t priv);
169
static uint32_t mlv_play_should_stop();
170

171
172
/* microsecond durations for one frame */
static uint32_t mlv_play_frame_div_pos = 0;
173
static uint32_t mlv_play_frame_dividers[3];
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* decompression stuff wizardry goes here */
struct struc_DecodeLosslessSetup
{
    void *address;
    int depth_type;
    int is_not_16bpp;
    int x_2;
    int x_1;
    int x_mul;
    int y_2;
    int ysize_raw;
    int y_mul;
};

void (*Setup_DecodeLosslessRawPath) (struct struc_DecodeLosslessSetup *args, void (*read_cbr)(int *), void (*done_cbr)(int *), int *cbr_ctx, int *a5) = NULL;
void (*Start_DecodeLosslessPath) (struct memSuite *a1) = NULL;
void (*Cleanup_DecodeLosslessPath) (void) = NULL;
void *mlv_play_decomp_buf = NULL;
static struct semaphore *mlv_play_decomp_sem = NULL;
struct memSuite *mlv_play_decomp_suite = NULL;

/* this one is called when decompression is done */
void mlv_play_decomp_DoneCBR(int *done)
{
    give_semaphore(mlv_play_decomp_sem);
}

/* the read cbr is not used */
void mlv_play_decomp_ReadCBR(int *done)
{
}




210
211
212
213
214
215
216
217
218
219
220
221
222
static void mlv_play_flush_queue(struct msg_queue *queue)
{
    uint32_t messages = 0;

    msg_queue_count(queue, &messages);
    while(messages > 0)
    {
        uint32_t tmp_buf = 0;
        msg_queue_receive(queue, &tmp_buf, 0);
        msg_queue_count(queue, &messages);
    }
}

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
static void mlv_play_next()
{
    playlist_entry_t current;
    playlist_entry_t next;
    
    strncpy(current.fullPath, mlv_play_current_filename, sizeof(current.fullPath));
    
    next = mlv_playlist_next(current);
    
    if(strlen(next.fullPath))
    {
        strncpy(mlv_play_next_filename, next.fullPath, sizeof(mlv_play_next_filename));
        mlv_play_stopfile = 1;
        mlv_play_paused = 0;
    }
}

static void mlv_play_prev()
{
    playlist_entry_t current;
    playlist_entry_t next;
    
    strncpy(current.fullPath, mlv_play_current_filename, sizeof(current.fullPath));
    
    next = mlv_playlist_prev(current);
    
    if(strlen(next.fullPath))
    {
        strncpy(mlv_play_next_filename, next.fullPath, sizeof(mlv_play_next_filename));
        mlv_play_stopfile = 1;
        mlv_play_paused = 0;
    }
}
256

257
258
static void mlv_play_progressbar(int pct, char *msg)
{
259
    static int last_pct = -1;
260
261
262
263
264
265
266
267
268
269
270
271
    int border = 4;
    int height = 40;
    int width = 720 - 100;

    int x = (720 - width) / 2;
    int y = (480 - height) / 2;

    if(pct == 0)
    {
        bmp_fill(COLOR_BLACK, x, y - font_med.height - border, width, font_med.height);
        bmp_fill(COLOR_WHITE, x, y, width, height);
        bmp_fill(COLOR_BLACK, x + border - 1, y + border - 1, width - 2 * (border - 1), height - 2 * (border - 1));
272
        last_pct = -1;
273
274
    }
    
275
276
277
278
279
280
    if(last_pct != pct)
    {
        bmp_fill(COLOR_BLUE, x + border, y + border, ((width - 2 * border) * pct) / 100, height - 2 * border);
        bmp_printf(FONT_MED, x, y - font_med.height - border, msg);
        last_pct = pct;
    }
281
282
}

283
284
285
/* call with positive duration and some string => print it for a while, then clear it */
/* call with 0 duration and some string => print it without clearing */
/* call with 0 duration and null string => just clear it */
286
287
288
289
290
291
292
293
static void mlv_play_show_dlg(uint32_t duration, char *string)
{
    uint32_t width = 500;
    uint32_t height = 128 + 20;
    uint32_t pos_y = (480 - height) / 2;
    uint32_t pos_x = (720 - width) / 2;
    struct bmp_file_t *icon = NULL;
    
294
    if (string)
295
    {
296
297
298
299
300
301
        icon = bmp_load_ram((uint8_t *)LDVAR(video_bmp), LDLEN(video_bmp), 0);
        
        bmp_fill(COLOR_BG, pos_x, pos_y, width, height);
        
        /* redraw 4 times in case it gets overdrawn */
        for(int loop = 0; loop < 4; loop++)
302
        {
303
304
305
306
307
308
309
310
311
312
313
            if(icon)
            {
                bmp_draw_scaled_ex(icon, pos_x + 10, pos_y + 10, 128, 128, 0);
            }
            else
            {
                bmp_printf(FONT_CANON, pos_x + 10, pos_y + 10 - font_med.height / 2, "[X]");
            }
            
            bmp_printf(FONT_CANON, pos_x + 128 + 20, pos_y + (128 / 2) - 16, string);
            msleep(duration / 10);
314
315
        }
        
316
        free(icon);
317
318
    }
    
319
320
321
322
    if (duration || !string)
    {
        bmp_fill(COLOR_EMPTY, pos_x, pos_y, width, height);
    }
323
324
}

325
326
327
328
329
330
/* run deletion in the same task as playback, to sync with file closing etc */
static int mlv_play_delete_requested = 0;

static void mlv_play_delete_request()
{
    mlv_play_delete_requested = 1;
331
    mlv_play_show_dlg(0, "Stopping...");
332
333
}

334
335
/* returns 1 on success, 0 on failure */
static int mlv_play_delete_work(char *filename)
g3gg0's avatar
g3gg0 committed
336
337
{
    uint32_t size = 0;
338
    uint32_t state = 0;
g3gg0's avatar
g3gg0 committed
339
    uint32_t seq_number = 0;
340
    char current_ext[4];
g3gg0's avatar
g3gg0 committed
341
    char current_file[128];
g3gg0's avatar
g3gg0 committed
342
    
343
344
345
    /* original extension, to be used as read-only */
    const char* main_ext = filename + strlen(filename) - 3;
    
346
    trace_write(mlv_play_trace_ctx, "[Delete] Delete request for: '%s'", filename);
g3gg0's avatar
g3gg0 committed
347
    
348
    strncpy(current_file, filename, sizeof(current_file));
g3gg0's avatar
g3gg0 committed
349
    
350
    /* file does not exist. the specified base file must exist, so abort */
g3gg0's avatar
g3gg0 committed
351
352
    if(FIO_GetFileSize(filename, &size))
    {
353
        trace_write(mlv_play_trace_ctx, "  Does not exist!");
354
        return 0;
g3gg0's avatar
g3gg0 committed
355
356
    }
    
357
358
    int is_mlv = streq(main_ext, "MLV") || streq(main_ext, "mlv");
    
359
    while (1)
g3gg0's avatar
g3gg0 committed
360
    {
361
        switch(state)
g3gg0's avatar
g3gg0 committed
362
        {
363
            case 0:
364
365
                /* try to delete main file */
                strcpy(current_ext, main_ext);
366
                break;
367

368
            case 1:
369
370
371
372
373
374
375
376
377
378
379
                /* try to delete index file */
                if (is_mlv)
                {
                    strcpy(current_ext, "IDX");
                }
                else
                {
                    /* index files are only used on MLVs, skip for other files */
                    state++;
                    continue;
                }
380
381
382
383
                break;
                
            case 2:
                /* try to delete chunk files */
384
385
                snprintf(current_ext, 4, "X%02d", seq_number);
                current_ext[0] = main_ext[0];
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
                break;
                
            case 3:
                /* next chunk file */
                if(seq_number >= 99)
                {
                    /* that would be the hundreth chunk, so lets say that was it */
                    state++;
                }
                else
                {
                    /* increase chunk counter and delete next file */
                    seq_number++;
                    state--;
                }
g3gg0's avatar
g3gg0 committed
401
                continue;
402
403
404
                
            case 4:
                /* we are done */
405
                return 1;
g3gg0's avatar
g3gg0 committed
406
        }
407
408

        for(int drive = 0; drive < 2; drive++)
g3gg0's avatar
g3gg0 committed
409
        {
410
411
412
413
414
415
            /* set extension */
            strcpy(&current_file[strlen(current_file) - 3], current_ext);
            
            /* set drive letter */
            current_file[0] = drive ? 'A' : 'B';
            
416
417
418
419
420
421
422
423
424
425
            if (!is_mlv)
            {
                if (current_file[0] != filename[0])
                {
                    /* non-MLV files can't be spanned, do not delete the file with the same name from the other card */
                    trace_write(mlv_play_trace_ctx, "[Delete] Skip drive: '%s'", current_file);
                    continue;
                }
            }
            
426
427
428
            trace_write(mlv_play_trace_ctx, "[Delete] Next file: '%s'", current_file);
            
            /* check if file exists */
g3gg0's avatar
g3gg0 committed
429
430
            if(FIO_GetFileSize(current_file, &size))
            {
431
432
433
434
435
                if (state == 0 && current_file[0] == filename[0])
                {
                    trace_write(mlv_play_trace_ctx, "[Delete] main file does not exist '%s'", current_file);
                    return 0;
                }
436
437
                continue;
            }
438

439
440
441
442
443
444
            trace_write(mlv_play_trace_ctx, "  existing, deleting");
            
            /* if existing, try to delete and set fail flag if that doesnt work */
            if(FIO_RemoveFile(current_file))
            {
                trace_write(mlv_play_trace_ctx, "  FAILED");
445
                return 0;
g3gg0's avatar
g3gg0 committed
446
447
448
            }
            else
            {
449
                trace_write(mlv_play_trace_ctx, "  deleted");
g3gg0's avatar
g3gg0 committed
450
451
            }
        }
452
453
        
        /* if there was no failure in deleting the file, next state */
454
        state++;
g3gg0's avatar
g3gg0 committed
455
456
    }
    
457
    return 1;
g3gg0's avatar
g3gg0 committed
458
459
}

460
static int mlv_play_delete()
g3gg0's avatar
g3gg0 committed
461
462
463
464
465
{
    playlist_entry_t current;
    playlist_entry_t next;
    playlist_entry_t prev;
    
g3gg0's avatar
g3gg0 committed
466
    /* save the currently played file */
g3gg0's avatar
g3gg0 committed
467
    strncpy(current.fullPath, mlv_play_current_filename, sizeof(current.fullPath));
g3gg0's avatar
g3gg0 committed
468
469
    
    /* check next/prev */
g3gg0's avatar
g3gg0 committed
470
471
472
    next = mlv_playlist_next(current);
    prev = mlv_playlist_prev(current);
    
g3gg0's avatar
g3gg0 committed
473
    /* remove the currently played from the playlist */
g3gg0's avatar
g3gg0 committed
474
    mlv_playlist_delete(current);
475
476

    /* delete the files */
477
    int ok = mlv_play_delete_work(current.fullPath);
478
    trace_write(mlv_play_trace_ctx, "[Delete] returned %s", ok ? "success" : "FAILURE");
479
480

    /* check which of the files is available and play it */
g3gg0's avatar
g3gg0 committed
481
482
483
484
485
486
487
488
489
490
    if(strlen(next.fullPath))
    {
        strncpy(mlv_play_next_filename, next.fullPath, sizeof(mlv_play_next_filename));
    }
    else if(strlen(prev.fullPath))
    {
        strncpy(mlv_play_next_filename, prev.fullPath, sizeof(mlv_play_next_filename));
    }
    else
    {
g3gg0's avatar
g3gg0 committed
491
        /* if none is available, just stop playback */
g3gg0's avatar
g3gg0 committed
492
493
        mlv_play_render_abort = 1;
    }
494
495
    
    return ok;
496
497
498
499
500
501
}

static void mlv_play_delete_if_requested()
{
    if (mlv_play_delete_requested)
    {
502
        mlv_play_show_dlg(0, "Deleting...");
503
504
        int ok = mlv_play_delete();
        mlv_play_show_dlg(3000, ok ? "Deleted." : "Delete failed.");
505
506
        mlv_play_delete_requested = 0;
    }
g3gg0's avatar
g3gg0 committed
507
508
}

g3gg0's avatar
g3gg0 committed
509
static void mlv_play_osd_quality(char *msg, uint32_t msg_len, uint32_t selected)
510
511
512
{
    if(selected)
    {
513
        mlv_play_quality = MOD(mlv_play_quality + 1, 2);
514
515
516
517
    }
    
    if(msg)
    {
g3gg0's avatar
g3gg0 committed
518
        snprintf(msg, msg_len, mlv_play_quality?"fast":"color");
g3gg0's avatar
g3gg0 committed
519
520
521
    }
}

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
static void mlv_play_osd_exact(char *msg, uint32_t msg_len, uint32_t selected)
{
    if(selected)
    {
        mlv_play_exact_fps = MOD(mlv_play_exact_fps + 1, 2);
    }
    
    if(msg)
    {
        /* any idea for a meaningful text here? feature description in long: 
                if enabled: 
                  play at exact fps with dropping if necessary
                  grayscale might keep up with video rate, but color requires dropping for sure
                if disabled:
                  play frame by frame as fast as possible
       */
        snprintf(msg, msg_len, mlv_play_exact_fps?"exact":"all");
    }
}

542

g3gg0's avatar
g3gg0 committed
543
544
545
546
static void mlv_play_osd_prev(char *msg, uint32_t msg_len, uint32_t selected)
{
    if(selected)
    {
547
        mlv_play_prev();
g3gg0's avatar
g3gg0 committed
548
549
550
551
552
553
554
555
556
557
558
559
    }
    
    if(msg)
    {
        snprintf(msg, msg_len, "<<");
    }
}

static void mlv_play_osd_next(char *msg, uint32_t msg_len, uint32_t selected)
{
    if(selected)
    {
560
        mlv_play_next();
g3gg0's avatar
g3gg0 committed
561
562
563
564
565
    }
    
    if(msg)
    {
        snprintf(msg, msg_len, ">>");
566
567
568
    }
}

g3gg0's avatar
g3gg0 committed
569
static void mlv_play_osd_pause(char *msg, uint32_t msg_len, uint32_t selected)
570
571
572
{
    if(selected)
    {
573
        mlv_play_paused = MOD(mlv_play_paused + 1, 2);
574
575
576
577
    }
    
    if(msg)
    {
g3gg0's avatar
g3gg0 committed
578
        snprintf(msg, msg_len, mlv_play_paused?"|>":"||");
579
580
581
    }
}

g3gg0's avatar
g3gg0 committed
582
static void mlv_play_osd_quit(char *msg, uint32_t msg_len, uint32_t selected)
583
584
585
586
587
588
589
590
{
    if(selected)
    {
        mlv_play_render_abort = 1;
    }
    
    if(msg)
    {
g3gg0's avatar
g3gg0 committed
591
        snprintf(msg, msg_len, "Exit");
592
593
594
    }
}

595
static uint32_t mlv_play_osd_delete_selected = 0;
596

g3gg0's avatar
g3gg0 committed
597
598
599
600
601
602
static void mlv_play_osd_delete(char *msg, uint32_t msg_len, uint32_t selected)
{
    uint32_t max_time = 5000;

    if(selected)
    {
603
        if(!mlv_play_osd_delete_selected || selected == 2)
g3gg0's avatar
g3gg0 committed
604
605
        {
            mlv_play_osd_force_redraw = 1;
606
            mlv_play_osd_delete_selected = get_ms_clock_value();
g3gg0's avatar
g3gg0 committed
607
608
609
610
        }
        else
        {
            mlv_play_osd_force_redraw = 0;
611
            mlv_play_osd_delete_selected = 0;
612
            mlv_play_delete_request();
g3gg0's avatar
g3gg0 committed
613
614
615
616
617
        }
    }
    
    if(msg)
    {
618
        uint32_t time_passed = get_ms_clock_value() - mlv_play_osd_delete_selected;
g3gg0's avatar
g3gg0 committed
619
620
        uint32_t seconds = (max_time - time_passed) / 1000;
        
621
        if(mlv_play_osd_delete_selected && seconds > 0)
g3gg0's avatar
g3gg0 committed
622
623
624
625
626
        {
            snprintf(msg, msg_len, "[delete? %ds]", seconds);
        }
        else
        {
627
            mlv_play_osd_delete_selected = 0;
g3gg0's avatar
g3gg0 committed
628
629
630
631
632
633
            mlv_play_osd_force_redraw = 0;
            snprintf(msg, msg_len, "Del");
        }
    }
}

634
static void(*mlv_play_osd_items[])(char *, uint32_t,  uint32_t) = { &mlv_play_osd_prev, &mlv_play_osd_pause, &mlv_play_osd_next, &mlv_play_osd_quality, &mlv_play_osd_exact, &mlv_play_osd_delete, &mlv_play_osd_quit };
635

g3gg0's avatar
g3gg0 committed
636
static uint32_t mlv_play_osd_handle(uint32_t msg)
637
638
639
640
{
    switch(msg)
    {
        case MODULE_KEY_PRESS_SET:
641
        case MODULE_KEY_JOY_CENTER:
642
643
        {
            /* execute menu item */
g3gg0's avatar
g3gg0 committed
644
            mlv_play_osd_items[mlv_play_osd_item](NULL, 0, 1);
645
646
647
648
            break;
        }
        
        case MODULE_KEY_WHEEL_UP:
649
        //case MODULE_KEY_WHEEL_LEFT:
650
        case MODULE_KEY_PRESS_LEFT:
651
        {
g3gg0's avatar
g3gg0 committed
652
            if(mlv_play_osd_item > 0)
653
            {
g3gg0's avatar
g3gg0 committed
654
                mlv_play_osd_item--;
655
656
657
658
659
            }
            break;
        }
        
        case MODULE_KEY_WHEEL_DOWN:
660
        //case MODULE_KEY_WHEEL_RIGHT:
661
        case MODULE_KEY_PRESS_RIGHT:
662
        {
g3gg0's avatar
g3gg0 committed
663
            if(mlv_play_osd_item < COUNT(mlv_play_osd_items) - 1)
664
            {
g3gg0's avatar
g3gg0 committed
665
                mlv_play_osd_item++;
666
667
668
669
            }
            break;
        }
    }
670
671
    
    return 0;
672
673
}

g3gg0's avatar
g3gg0 committed
674
static uint32_t mlv_play_osd_draw()
675
676
{
    uint32_t redraw = 0;
677
678
    uint32_t border = 4;
    uint32_t y_offset = 28;
679

680
    /* undraw last drawn OSD item */
g3gg0's avatar
g3gg0 committed
681
    static char osd_line[64] = "";
682
    
g3gg0's avatar
g3gg0 committed
683
    uint32_t w = bmp_string_width(FONT_LARGE, osd_line);
684
    uint32_t h = fontspec_height(FONT_LARGE);
685
    bmp_fill(COLOR_EMPTY, mlv_play_osd_x - w/2 - border, mlv_play_osd_y - border, w + 2 * border, h + 2 * border);
686
687
    
    /* handle animation */
g3gg0's avatar
g3gg0 committed
688
    switch(mlv_play_osd_state)
689
690
691
692
693
694
695
696
697
698
    {
        case MLV_PLAY_MENU_SHOWN:
        {
            redraw = 0;
            break;
        }
        
        case MLV_PLAY_MENU_HIDDEN:
        case MLV_PLAY_MENU_IDLE:
        {
g3gg0's avatar
g3gg0 committed
699
            mlv_play_osd_y = os.y_max + 1;
700
701
702
703
704
705
            redraw = 0;
            break;
        }
        
        case MLV_PLAY_MENU_FADEIN:
        {
706
707
708
            int y_top = os.y_max - font_large.height - y_offset;
            mlv_play_osd_y = MAX(mlv_play_osd_y - border, y_top);
            if(mlv_play_osd_y <= y_top)
709
            {
g3gg0's avatar
g3gg0 committed
710
                mlv_play_osd_state = MLV_PLAY_MENU_SHOWN;
711
712
713
714
715
716
717
            }
            redraw = 1;
            break;
        }
        
        case MLV_PLAY_MENU_FADEOUT:
        {
718
719
720
            int y_bottom = os.y_max + 1;
            mlv_play_osd_y = MIN(mlv_play_osd_y + border, y_bottom);
            if(mlv_play_osd_y >= y_bottom)
721
            {
g3gg0's avatar
g3gg0 committed
722
                mlv_play_osd_state = MLV_PLAY_MENU_HIDDEN;
723
724
725
726
727
728
            }
            redraw = 1;
            break;
        }
    }
    
g3gg0's avatar
g3gg0 committed
729
730
731
732
733
    /* draw a line with all OSD buttons */
    char selected_item[64];
    uint32_t selected_x = 0;
    
    strcpy(osd_line, "");
734
    for(uint32_t pos = 0; pos < COUNT(mlv_play_osd_items); pos++)
735
    {
g3gg0's avatar
g3gg0 committed
736
737
738
739
740
741
742
743
744
745
746
747
748
        char msg[64];
        mlv_play_osd_items[pos](msg, sizeof(msg), 0);
        
        /* if this is the selected one, keep the position for redrawing highlighted */
        if(pos == mlv_play_osd_item)
        {
            strcpy(selected_item, msg);
            selected_x = bmp_string_width(FONT_LARGE, osd_line);
        }
        
        strcat(osd_line, "  ");
        strcat(osd_line, msg);
        strcat(osd_line, "  ");
749
750
    }
    
g3gg0's avatar
g3gg0 committed
751
    w = bmp_string_width(FONT_LARGE, osd_line);
752
    bmp_fill(COLOR_BG, mlv_play_osd_x - w/2 - border, mlv_play_osd_y - border, w + 2 * border, h + 2 * border);
g3gg0's avatar
g3gg0 committed
753
754
755
756
757
    bmp_printf(FONT(FONT_LARGE,COLOR_WHITE,COLOR_BG), mlv_play_osd_x - w/2, mlv_play_osd_y, osd_line);
    
    /* draw selected item over with blue background */
    bmp_printf(FONT(FONT_LARGE,COLOR_WHITE,COLOR_BLUE), mlv_play_osd_x - w/2 + selected_x, mlv_play_osd_y, "  %s  ", selected_item);
    
758
759
760
    return redraw;
}

g3gg0's avatar
g3gg0 committed
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
static void mlv_play_osd_act(void *handler)
{
    int entry = 0;
    
    for(entry = 0; entry < COUNT(mlv_play_osd_items); entry++)
    {
        if(mlv_play_osd_items[entry] == handler)
        {
            mlv_play_osd_item = entry;
            mlv_play_osd_state = MLV_PLAY_MENU_FADEIN;
            return;
        }
    }
}

g3gg0's avatar
g3gg0 committed
776
static void mlv_play_osd_task(void *priv)
777
778
779
{
    uint32_t next_render_time = get_ms_clock_value() + mlv_play_render_timestep;
 
g3gg0's avatar
g3gg0 committed
780
    mlv_play_osd_state = MLV_PLAY_MENU_IDLE;
781
    mlv_play_osd_item = 1;
782
783
    mlv_play_paused = 0;   
    
784
    uint32_t last_keypress_time = get_ms_clock_value();
785
786
    TASK_LOOP
    {
787
        uint32_t key;
788
789
790
791
        uint32_t timeout = next_render_time - get_ms_clock_value();
        
        timeout = MIN(timeout, mlv_play_idle_timestep);
        
g3gg0's avatar
g3gg0 committed
792
        if(!msg_queue_receive(mlv_play_queue_osd, &key, timeout))
793
        {
794
795
796
797
798
799
800
801
802
803
804
805
806
            /* there was a keypress */
            last_keypress_time = get_ms_clock_value();
            
            /* no matter which state - these are handled */
            switch(key)
            {
                case MODULE_KEY_WHEEL_LEFT:
                    mlv_play_prev();
                    if(mlv_play_osd_state != MLV_PLAY_MENU_SHOWN)
                    {
                        mlv_play_osd_state = MLV_PLAY_MENU_FADEIN;
                    }
                    break;
g3gg0's avatar
g3gg0 committed
807
                    
808
809
810
811
812
813
814
                case MODULE_KEY_WHEEL_RIGHT:
                    mlv_play_next();
                    if(mlv_play_osd_state != MLV_PLAY_MENU_SHOWN)
                    {
                        mlv_play_osd_state = MLV_PLAY_MENU_FADEIN;
                    }
                    break;
g3gg0's avatar
g3gg0 committed
815
816
817
818
819
                    
                case MODULE_KEY_TRASH:
                    mlv_play_osd_act(&mlv_play_osd_delete);
                    mlv_play_osd_delete(NULL, 0, 2);
                    break;
820
821
822
823
824

                case MODULE_KEY_PLAY:
                    mlv_play_osd_act(&mlv_play_osd_pause);
                    mlv_play_osd_pause(NULL, 0, 1);
                    break;
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850

                case MODULE_KEY_PRESS_ZOOMIN:
                    mlv_play_osd_state = MLV_PLAY_MENU_HIDDEN;
                    mlv_play_zoom = (mlv_play_zoom + 1) % 4;
                    raw_twk_set_zoom(mlv_play_zoom, mlv_play_zoom_x_pct, mlv_play_zoom_y_pct);
                    break;
                    
                case MODULE_KEY_PRESS_UP:
                    mlv_play_zoom_y_pct = MAX(0, mlv_play_zoom_y_pct - 10);
                    raw_twk_set_zoom(mlv_play_zoom, mlv_play_zoom_x_pct, mlv_play_zoom_y_pct);
                    break;

                case MODULE_KEY_PRESS_DOWN:
                    mlv_play_zoom_y_pct = MIN(100, mlv_play_zoom_y_pct + 10);
                    raw_twk_set_zoom(mlv_play_zoom, mlv_play_zoom_x_pct, mlv_play_zoom_y_pct);
                    break;

                case MODULE_KEY_PRESS_LEFT:
                    mlv_play_zoom_x_pct = MAX(0, mlv_play_zoom_x_pct - 10);
                    raw_twk_set_zoom(mlv_play_zoom, mlv_play_zoom_x_pct, mlv_play_zoom_y_pct);
                    break;

                case MODULE_KEY_PRESS_RIGHT:
                    mlv_play_zoom_x_pct = MIN(100, mlv_play_zoom_x_pct + 10);
                    raw_twk_set_zoom(mlv_play_zoom, mlv_play_zoom_x_pct, mlv_play_zoom_y_pct);
                    break;
851
852
            }
            
g3gg0's avatar
g3gg0 committed
853
            switch(mlv_play_osd_state)
854
855
856
857
            {
                case MLV_PLAY_MENU_SHOWN:
                case MLV_PLAY_MENU_FADEIN:
                {
858
                    if(key == MODULE_KEY_Q || key == MODULE_KEY_PICSTYLE)
859
                    {
860
861
862
863
                        if (!mlv_play_osd_force_redraw)
                        {
                            mlv_play_osd_state = MLV_PLAY_MENU_FADEOUT;
                        }
864
865
866
                    }
                    else
                    {
g3gg0's avatar
g3gg0 committed
867
                        mlv_play_osd_handle(key);
868
869
870
                    }
                    break;
                }
871
872
873
874

                /* when fading out, still handle and fade back in */
                case MLV_PLAY_MENU_FADEOUT:
                {
875
876
877
878
879
                    if(!mlv_play_zoom)
                    {
                        mlv_play_osd_state = MLV_PLAY_MENU_FADEIN;
                        mlv_play_osd_handle(key);
                    }
880
881
                    break;
                }
882
883
884
885
                
                case MLV_PLAY_MENU_IDLE:
                case MLV_PLAY_MENU_HIDDEN:
                {
886
                    if(!mlv_play_zoom)
887
                    {
888
889
890
891
892
893
894
895
896
                        if (key == MODULE_KEY_PRESS_SET || key == MODULE_KEY_Q || key == MODULE_KEY_PICSTYLE)
                        {
                            mlv_play_osd_state = MLV_PLAY_MENU_FADEIN;
                        }
                        if (key == MODULE_KEY_INFO)
                        {
                            clrscr();
                            mlv_play_info = MOD(mlv_play_info + 1, 2) ? 2 : 0;
                        }
897
                    }
898
899
900
901
                    break;
                }
            }
        }
902
        uint32_t idle_time = get_ms_clock_value() - last_keypress_time;
903
904
905
906
907
908
        
        if(mlv_play_render_abort)
        {
            break;
        }
        
g3gg0's avatar
g3gg0 committed
909
        if(mlv_play_osd_draw())
910
911
912
913
914
915
        {
            next_render_time = get_ms_clock_value() + mlv_play_render_timestep;
        }
        else
        {
            next_render_time = get_ms_clock_value() + mlv_play_idle_timestep;
916
            
g3gg0's avatar
g3gg0 committed
917
918
919
920
921
922
            /* when redrawing is forced, keep OSD shown */
            if(mlv_play_osd_force_redraw)
            {
                mlv_play_osd_state = MLV_PLAY_MENU_SHOWN;
            }
            else if(idle_time > mlv_play_osd_idle && mlv_play_osd_state == MLV_PLAY_MENU_SHOWN)
923
924
925
            {
                mlv_play_osd_state = MLV_PLAY_MENU_FADEOUT;
            }
926
927
928
929
930
931
        }
        continue;
    }
}


932
static void mlv_play_xref_resize(frame_xref_t **table, uint32_t entries, uint32_t *allocated)
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
{
    /* make sure there is no crappy pointer before using */
    if(*allocated == 0)
    {
        *table = NULL;
    }
    
    /* only resize if the buffer is too small */
    if(entries * sizeof(frame_xref_t) > *allocated)
    {
        *allocated += (entries + 1) * sizeof(frame_xref_t);
        *table = realloc(*table, *allocated);
    }
}

948
static void mlv_play_xref_sort(frame_xref_t *table, uint32_t entries)
949
{
950
951
    if (!entries) return;
    
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
    uint32_t n = entries;
    do
    {
        uint32_t newn = 1;
        for (uint32_t i = 0; i < n-1; ++i)
        {
            if (table[i].frameTime > table[i+1].frameTime)
            {
                frame_xref_t tmp = table[i+1];
                table[i+1] = table[i];
                table[i] = tmp;
                newn = i + 1;
            }
        }
        n = newn;
    } while (n > 1);
}

970
static mlv_xref_hdr_t *mlv_play_load_index(char *base_filename)
971
972
973
974
975
976
977
978
{
    mlv_xref_hdr_t *block_hdr = NULL;
    char filename[128];
    FILE *in_file = NULL;

    strncpy(filename, base_filename, sizeof(filename));
    strcpy(&filename[strlen(filename) - 3], "IDX");
    
979
    in_file = FIO_OpenFile(filename, O_RDONLY | O_SYNC);
980
    
981
    if (!in_file)
982
983
984
985
    {
        return NULL;
    }
    
986
    TASK_LOOP
987
988
    {
        mlv_hdr_t buf;
989
        int64_t position = 0;
990
        
991
        position = FIO_SeekSkipFile(in_file, 0, SEEK_CUR);
992
993
994
995
996
997
998
        
        if(FIO_ReadFile(in_file, &buf, sizeof(mlv_hdr_t)) != sizeof(mlv_hdr_t))
        {
            break;
        }
        
        /* jump back to the beginning of the block just read */
999
        FIO_SeekSkipFile(in_file, position, SEEK_SET);
1000
1001
1002
1003
1004
1005

        /* we should check the MLVI header for matching UID value to make sure its the right index... */
        if(!memcmp(buf.blockType, "XREF", 4))
        {
            block_hdr = malloc(buf.blockSize);

1006
            if(FIO_ReadFile(in_file, block_hdr, buf.blockSize) != (int32_t)buf.blockSize)
1007
1008
1009
1010
1011
1012
1013
            {
                free(block_hdr);
                block_hdr = NULL;
            }
        }
        else
        {
1014
            FIO_SeekSkipFile(in_file, position + buf.blockSize, SEEK_SET);
1015
1016
1017
        }
        
        /* we are at the same position as before, so abort */
1018
        if(position == FIO_SeekSkipFile(in_file, 0, SEEK_CUR))
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
        {
            break;
        }
    }
    
    FIO_CloseFile(in_file);
    
    return block_hdr;
}

1029
static void mlv_play_save_index(char *base_filename, mlv_file_hdr_t *ref_file_hdr, int fileCount, frame_xref_t *index, int entries)
1030
1031
1032
1033
1034
1035
1036
{
    char filename[128];
    FILE *out_file = NULL;

    strncpy(filename, base_filename, sizeof(filename));
    strcpy(&filename[strlen(filename) - 3], "IDX");
    
1037
    out_file = FIO_CreateFile(filename);
1038
    
1039
    if (!out_file)
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
    {
        return;
    }
    
    /* first write MLVI header */
    mlv_file_hdr_t file_hdr = *ref_file_hdr;
    
    /* update fields */
    file_hdr.blockSize = sizeof(mlv_file_hdr_t);
    file_hdr.videoFrameCount = 0;
    file_hdr.audioFrameCount = 0;
    file_hdr.fileNum = fileCount + 1;
    
    FIO_WriteFile(out_file, &file_hdr, sizeof(mlv_file_hdr_t));

    /* now write XREF block */
    mlv_xref_hdr_t hdr;
    
1058
    memset(&hdr, 0x00, sizeof(mlv_xref_hdr_t));
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
    memcpy(hdr.blockType, "XREF", 4);
    hdr.blockSize = sizeof(mlv_xref_hdr_t) + entries * sizeof(mlv_xref_t);
    hdr.entryCount = entries;
    
    if(FIO_WriteFile(out_file, &hdr, sizeof(mlv_xref_hdr_t)) != sizeof(mlv_xref_hdr_t))
    {
        FIO_CloseFile(out_file);
        return;
    }
    
    uint32_t last_pct = 0;
1070
    mlv_play_progressbar(0, "");
1071
1072
1073
1074
1075
1076
1077
1078
1079
    
    /* and then the single entries */
    for(int entry = 0; entry < entries; entry++)
    {
        mlv_xref_t field;
        uint32_t pct = (entry*100)/entries;
        
        if(last_pct != pct)
        {
1080
1081
            char msg[100];
            
1082
            snprintf(msg, sizeof(msg), "Saving index (%d entries)...", entries);
1083
            mlv_play_progressbar(pct, msg);
1084
1085
1086
1087
1088
1089
            last_pct = pct;
        }
        memset(&field, 0x00, sizeof(mlv_xref_t));
        
        field.frameOffset = index[entry].frameOffset;
        field.fileNumber = index[entry].fileNumber;
1090
        field.frameType = index[entry].frameType;
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
        
        if(FIO_WriteFile(out_file, &field, sizeof(mlv_xref_t)) != sizeof(mlv_xref_t))
        {
            FIO_CloseFile(out_file);
            return;
        }
    }
    
    FIO_CloseFile(out_file);
}

1102
static void mlv_play_build_index(char *filename, FILE **chunk_files, uint32_t chunk_count)
1103
1104
1105
1106
1107
1108
{
    frame_xref_t *frame_xref_table = NULL;
    uint32_t frame_xref_entries = 0;
    uint32_t frame_xref_allocated = 0;
    mlv_file_hdr_t main_header;
    
1109
    for(uint32_t chunk = 0; chunk < chunk_count; chunk++)
1110
1111
    {
        uint32_t last_pct = 0;
1112
1113
        int64_t size = 0;
        int64_t position = 0;
1114
        
1115
        size = FIO_SeekSkipFile(chunk_files[chunk], 0, SEEK_END);
1116
        FIO_SeekSkipFile(chunk_files[chunk], 0, SEEK_SET);
1117
        
1118
        mlv_play_progressbar(0, "");
1119
1120
1121
        
        while(1)
        {
1122
1123
1124
1125
1126
            if(ml_shutdown_requested)
            {
                break;
            }
            
1127
            mlv_hdr_t buf;
1128
            uint64_t timestamp = 0;
1129
1130
1131
1132
1133
            
            uint32_t pct = ((position / 10) / (size / 1000));
            
            if(last_pct != pct)
            {
1134
1135
1136
1137
                char msg[100];
                
                snprintf(msg, sizeof(msg), "Building index... (%d/%d)", chunk + 1, chunk_count);
                mlv_play_progressbar(pct, msg);
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
                last_pct = pct;
            }
            
            int read = FIO_ReadFile(chunk_files[chunk], &buf, sizeof(mlv_hdr_t));
            
            if(read != sizeof(mlv_hdr_t))
            {
                if(read <= 0)
                {
                    break;
                }
                else
                {
1151
                    bmp_printf(FONT_MED, 30, 190, "File #%d ends prematurely, %d bytes read", chunk, read);
1152
                    beep();
1153
1154
                    msleep(2000);
                    return;
1155
1156
1157
                }
            }
            
1158
            /* unexpected block header size? */
a1ex's avatar
a1ex committed
1159
            if(buf.blockSize < sizeof(mlv_hdr_t) || buf.blockSize > 50 * 1024 * 1024)
1160
1161
1162
            {
                bmp_printf(FONT_MED, 30, 190, "Invalid header size: %d bytes at 0x%08X", buf.blockSize, position);
                beep();
1163
1164
                msleep(2000);
                return;
1165
1166
            }

1167
1168
1169
1170
1171
            /* file header */
            if(!memcmp(buf.blockType, "MLVI", 4))
            {
                mlv_file_hdr_t file_hdr;
                uint32_t hdr_size = MIN(sizeof(mlv_file_hdr_t), buf.blockSize);
1172
                
1173
                FIO_SeekSkipFile(chunk_files[chunk], position, SEEK_SET);
1174
                
1175
                /* read the whole header block, but limit size to either our local type size or the written block size */
1176
                if(FIO_ReadFile(chunk_files[chunk], &file_hdr, hdr_size) != (int32_t)hdr_size)
1177
                {
1178
                    bmp_printf(FONT_MED, 30, 190, "File ends prematurely during MLVI");
1179
                    beep();
1180
1181
                    msleep(2000);
                    return;
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
                }

                /* is this the first file? */
                if(file_hdr.fileNum == 0)
                {
                    memcpy(&main_header, &file_hdr, sizeof(mlv_file_hdr_t));
                }
                else
                {
                    /* no, its another chunk */
                    if(main_header.fileGuid != file_hdr.fileGuid)
                    {
                        bmp_printf(FONT_MED, 30, 190, "Error: GUID within the file chunks mismatch!");
                        beep();
1196
1197
                        msleep(2000);
                        return;
1198
1199
1200
                    }
                }
                
1201
                /* emulate timestamp zero (will overwrite version string) */
1202
1203
1204
1205
1206
1207
                timestamp = 0;
            }
            else
            {
                /* all other blocks have a timestamp */
                timestamp = buf.timestamp;
1208
1209
            }
            
1210
1211
1212
            /* dont index NULL blocks */
            if(memcmp(buf.blockType, "NULL", 4))
            {
1213
                mlv_play_xref_resize(&frame_xref_table, frame_xref_entries + 1, &frame_xref_allocated);
1214
1215
1216
1217
1218
                
                /* add xref data */
                frame_xref_table[frame_xref_entries].frameTime = timestamp;
                frame_xref_table[frame_xref_entries].frameOffset = position;
                frame_xref_table[frame_xref_entries].fileNumber = chunk;