FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/ffplay.c
Date: 2026-09-27 07:50:39
Exec Total Coverage
Lines: 0 2832 0.0%
Functions: 0 131 0.0%
Branches: 0 1933 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2003 Fabrice Bellard
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * @file
23 * simple media player based on the FFmpeg libraries
24 */
25
26 #include "config.h"
27 #include "config_components.h"
28 #include <math.h>
29 #include <limits.h>
30 #include <signal.h>
31 #include <stdint.h>
32
33 #include "libavutil/attributes.h"
34 #include "libavutil/avstring.h"
35 #include "libavutil/channel_layout.h"
36 #include "libavutil/mathematics.h"
37 #include "libavutil/mem.h"
38 #include "libavutil/pixdesc.h"
39 #include "libavutil/dict.h"
40 #include "libavutil/fifo.h"
41 #include "libavutil/parseutils.h"
42 #include "libavutil/samplefmt.h"
43 #include "libavutil/time.h"
44 #include "libavutil/bprint.h"
45 #include "libavcodec/bsf.h"
46 #include "libavformat/avformat.h"
47 #include "libavdevice/avdevice.h"
48 #include "libswscale/swscale.h"
49 #include "libavutil/opt.h"
50 #include "libavutil/tx.h"
51 #include "libswresample/swresample.h"
52
53 #include "libavfilter/avfilter.h"
54 #include "libavfilter/buffersink.h"
55 #include "libavfilter/buffersrc.h"
56
57 #include <SDL.h>
58 #include <SDL_thread.h>
59
60 #include "cmdutils.h"
61 #include "ffplay_renderer.h"
62 #include "opt_common.h"
63
64 const char program_name[] = "ffplay";
65 const int program_birth_year = 2003;
66
67 #define MAX_QUEUE_SIZE (15 * 1024 * 1024)
68 #define MIN_FRAMES 25
69 #define EXTERNAL_CLOCK_MIN_FRAMES 2
70 #define EXTERNAL_CLOCK_MAX_FRAMES 10
71
72 /* Minimum SDL audio buffer size, in samples. */
73 #define SDL_AUDIO_MIN_BUFFER_SIZE 512
74 /* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
75 #define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30
76
77 /* Step size for volume control in dB */
78 #define SDL_VOLUME_STEP (0.75)
79
80 /* no AV sync correction is done if below the minimum AV sync threshold */
81 #define AV_SYNC_THRESHOLD_MIN 0.04
82 /* AV sync correction is done if above the maximum AV sync threshold */
83 #define AV_SYNC_THRESHOLD_MAX 0.1
84 /* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
85 #define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
86 /* no AV correction is done if too big error */
87 #define AV_NOSYNC_THRESHOLD 10.0
88
89 /* maximum audio speed change to get correct sync */
90 #define SAMPLE_CORRECTION_PERCENT_MAX 10
91
92 /* external clock speed adjustment constants for realtime sources based on buffer fullness */
93 #define EXTERNAL_CLOCK_SPEED_MIN 0.900
94 #define EXTERNAL_CLOCK_SPEED_MAX 1.010
95 #define EXTERNAL_CLOCK_SPEED_STEP 0.001
96
97 /* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
98 #define AUDIO_DIFF_AVG_NB 20
99
100 /* polls for possible required screen refresh at least this often, should be less than 1/fps */
101 #define REFRESH_RATE 0.01
102
103 /* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
104 /* TODO: We assume that a decoded and resampled frame fits into this buffer */
105 #define SAMPLE_ARRAY_SIZE (8 * 65536)
106
107 #define CURSOR_HIDE_DELAY 1000000
108
109 #define CHAPTER_LIST_FADE_TIME 250000
110 #define CHAPTER_LIST_HOLD_TIME 500000
111 #define CHAPTER_LIST_ROWS 7
112
113 enum {
114 CHAPTER_SORT_NUMBER,
115 CHAPTER_SORT_ARTIST,
116 CHAPTER_SORT_TITLE,
117 CHAPTER_SORT_LENGTH,
118 CHAPTER_SORT_SHUFFLE,
119 CHAPTER_SORT_NB
120 };
121
122 typedef struct ChapterSortKey {
123 int key;
124 int descending;
125 } ChapterSortKey;
126
127 typedef struct ChapterRow {
128 int index;
129 const char *artist;
130 const char *title;
131 int64_t length;
132 } ChapterRow;
133
134 /* what lies under a window position: the row and the sort button, -1 for none, and whether it is inside the list at all */
135 typedef struct ChapterListHit {
136 int inside;
137 int row;
138 int button;
139 int detail;
140 } ChapterListHit;
141
142 #define USE_ONEPASS_SUBTITLE_RENDER 1
143
144 typedef struct MyAVPacketList {
145 AVPacket *pkt;
146 int serial;
147 } MyAVPacketList;
148
149 typedef struct PacketQueue {
150 AVFifo *pkt_list;
151 int nb_packets;
152 int size;
153 int64_t duration;
154 int abort_request;
155 int serial;
156 SDL_mutex *mutex;
157 SDL_cond *cond;
158 } PacketQueue;
159
160 #define VIDEO_PICTURE_QUEUE_SIZE 3
161 #define SUBPICTURE_QUEUE_SIZE 16
162 #define SAMPLE_QUEUE_SIZE 9
163 #define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))
164
165 typedef struct AudioParams {
166 int freq;
167 AVChannelLayout ch_layout;
168 enum AVSampleFormat fmt;
169 int frame_size;
170 int bytes_per_sec;
171 } AudioParams;
172
173 typedef struct Clock {
174 double pts; /* clock base */
175 double pts_drift; /* clock base minus time at which we updated the clock */
176 double last_updated;
177 double speed;
178 int serial; /* clock is based on a packet with this serial */
179 int paused;
180 int *queue_serial; /* pointer to the current packet queue serial, used for obsolete clock detection */
181 } Clock;
182
183 typedef struct FrameData {
184 int64_t pkt_pos;
185 } FrameData;
186
187 /* Common struct for handling all types of decoded data and allocated render buffers. */
188 typedef struct Frame {
189 AVFrame *frame;
190 AVSubtitle sub;
191 int serial;
192 double pts; /* presentation timestamp for the frame */
193 double duration; /* estimated duration of the frame */
194 int64_t pos; /* byte position of the frame in the input file */
195 int width;
196 int height;
197 int format;
198 AVRational sar;
199 int uploaded;
200 int flip_v;
201 } Frame;
202
203 typedef struct FrameQueue {
204 Frame queue[FRAME_QUEUE_SIZE];
205 int rindex;
206 int windex;
207 int size;
208 int max_size;
209 int keep_last;
210 int rindex_shown;
211 SDL_mutex *mutex;
212 SDL_cond *cond;
213 PacketQueue *pktq;
214 } FrameQueue;
215
216 typedef struct StreamGroup {
217 AVStreamGroup *stg;
218 AVBitStreamFilterGraph *graph;
219 AVBitStreamFilterContext *sink;
220 } StreamGroup;
221
222 typedef struct Stream {
223 StreamGroup *group;
224 AVBitStreamFilterContext *filter;
225 } Stream;
226
227 typedef struct FormatContext {
228 Stream **streams;
229 int nb_streams;
230 StreamGroup **stream_groups;
231 int nb_stream_groups;
232 } FormatContext;
233
234 enum {
235 AV_SYNC_AUDIO_MASTER, /* default choice */
236 AV_SYNC_VIDEO_MASTER,
237 AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */
238 };
239
240 typedef struct Decoder {
241 AVPacket *pkt;
242 PacketQueue *queue;
243 AVCodecContext *avctx;
244 int pkt_serial;
245 int finished;
246 int packet_pending;
247 SDL_cond *empty_queue_cond;
248 int64_t start_pts;
249 AVRational start_pts_tb;
250 int64_t next_pts;
251 AVRational next_pts_tb;
252 SDL_Thread *decoder_tid;
253 } Decoder;
254
255 typedef struct VideoState {
256 SDL_Thread *read_tid;
257 const AVInputFormat *iformat;
258 int abort_request;
259 int force_refresh;
260 int paused;
261 int last_paused;
262 int queue_attachments_req;
263 int seek_req;
264 int seek_flags;
265 int64_t seek_pos;
266 int64_t seek_rel;
267 int read_pause_return;
268 AVFormatContext *ic;
269 int realtime;
270
271 Clock audclk;
272 Clock vidclk;
273 Clock extclk;
274
275 FrameQueue pictq;
276 FrameQueue subpq;
277 FrameQueue sampq;
278
279 Decoder auddec;
280 Decoder viddec;
281 Decoder subdec;
282
283 int audio_stream;
284
285 int av_sync_type;
286
287 double audio_clock;
288 int audio_clock_serial;
289 double audio_diff_cum; /* used for AV difference average computation */
290 double audio_diff_avg_coef;
291 double audio_diff_threshold;
292 int audio_diff_avg_count;
293 AVStream *audio_st;
294 PacketQueue audioq;
295 int audio_hw_buf_size;
296 uint8_t *audio_buf;
297 uint8_t *audio_buf1;
298 unsigned int audio_buf_size; /* in bytes */
299 unsigned int audio_buf1_size;
300 int audio_buf_index; /* in bytes */
301 int audio_write_buf_size;
302 int audio_volume;
303 int muted;
304 struct AudioParams audio_src;
305 struct AudioParams audio_filter_src;
306 struct AudioParams audio_tgt;
307 struct SwrContext *swr_ctx;
308 int frame_drops_early;
309 int frame_drops_late;
310
311 enum ShowMode {
312 SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, SHOW_MODE_WAVES, SHOW_MODE_RDFT, SHOW_MODE_NB
313 } show_mode;
314 int16_t sample_array[SAMPLE_ARRAY_SIZE];
315 int sample_array_index;
316 int last_i_start;
317 AVTXContext *rdft;
318 av_tx_fn rdft_fn;
319 int rdft_bits;
320 float *real_data;
321 AVComplexFloat *rdft_data;
322 int xpos;
323 double last_vis_time;
324 RenderParams render_params;
325 SDL_Texture *vis_texture;
326 SDL_Texture *sub_texture;
327 SDL_Texture *vid_texture;
328 SDL_Texture *chapter_texture;
329 SDL_Rect chapter_rect;
330 AVFilterGraph *chapter_graph;
331 AVFilterContext *chapter_sink;
332 ChapterRow *chapter_rows;
333 int nb_chapter_rows;
334 char chapter_search[64];
335 int chapter_selected;
336 int chapter_playing;
337 int chapter_mouse_x, chapter_mouse_y;
338 ChapterListHit chapter_hover;
339 int chapter_render_pending;
340 int chapter_pinned;
341 int chapter_pin_requested;
342 int64_t chapter_fade_start;
343 int64_t chapter_last_input;
344 double chapter_drawn_alpha;
345
346 int subtitle_stream;
347 AVStream *subtitle_st;
348 PacketQueue subtitleq;
349
350 double frame_timer;
351 double frame_last_returned_time;
352 double frame_last_filter_delay;
353 int video_stream;
354 AVStream *video_st;
355 PacketQueue videoq;
356 double max_frame_duration; // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity
357 struct SwsContext *sub_convert_ctx;
358 int eof;
359
360 char *filename;
361 int width, height, xleft, ytop;
362 int step;
363
364 int vfilter_idx;
365 AVFilterContext *in_video_filter; // the first filter in the video chain
366 AVFilterContext *out_video_filter; // the last filter in the video chain
367 AVFilterContext *in_audio_filter; // the first filter in the audio chain
368 AVFilterContext *out_audio_filter; // the last filter in the audio chain
369 AVFilterGraph *agraph; // audio filter graph
370
371 int last_video_stream, last_audio_stream, last_subtitle_stream;
372
373 SDL_cond *continue_read_thread;
374 } VideoState;
375
376 /* options specified by the user */
377 static const AVInputFormat *file_iformat;
378 static const char *input_filename;
379 static const char *window_title;
380 static int default_width = 640;
381 static int default_height = 480;
382 static int screen_width = 0;
383 static int screen_height = 0;
384 static int screen_left = SDL_WINDOWPOS_CENTERED;
385 static int screen_top = SDL_WINDOWPOS_CENTERED;
386 static int audio_disable;
387 static int video_disable;
388 static int subtitle_disable;
389 static const char* wanted_stream_spec[AVMEDIA_TYPE_NB] = {0};
390 static int seek_by_bytes = -1;
391 static float seek_interval = 10;
392 static int display_disable;
393 static int borderless;
394 static int alwaysontop;
395 static int startup_volume = 100;
396 static int show_status = -1;
397 static int av_sync_type = AV_SYNC_AUDIO_MASTER;
398 static int64_t start_time = AV_NOPTS_VALUE;
399 static int64_t duration = AV_NOPTS_VALUE;
400 static int fast = 0;
401 static int genpts = 0;
402 static int lowres = 0;
403 static int decoder_reorder_pts = -1;
404 static int autoexit;
405 static int exit_on_keydown;
406 static int exit_on_mousedown;
407 static int loop = 1;
408 static int framedrop = -1;
409 static int infinite_buffer = -1;
410 static enum ShowMode show_mode = SHOW_MODE_NONE;
411 /* the columns the chapter list is sorted by, the most recently clicked first */
412 static ChapterSortKey chapter_sort[CHAPTER_SORT_NB] = { { CHAPTER_SORT_NUMBER }, { CHAPTER_SORT_ARTIST }, { CHAPTER_SORT_TITLE },
413 { CHAPTER_SORT_LENGTH }, { CHAPTER_SORT_SHUFFLE } };
414 static int chapter_shuffle_seed;
415 static const char *audio_codec_name;
416 static const char *subtitle_codec_name;
417 static const char *video_codec_name;
418 double rdftspeed = 0.02;
419 static int64_t cursor_last_shown;
420 static int cursor_hidden = 0;
421 static const char **vfilters_list = NULL;
422 static int nb_vfilters = 0;
423 static char *afilters = NULL;
424 static int autorotate = 1;
425 static int find_stream_info = 1;
426 static int filter_nbthreads = 0;
427 static int enable_vulkan = 0;
428 static char *vulkan_params = NULL;
429 static char *video_background = NULL;
430 static const char *hwaccel = NULL;
431
432 /* current context */
433 static int is_full_screen;
434 static int64_t audio_callback_time;
435
436 #define FF_QUIT_EVENT (SDL_USEREVENT + 2)
437
438 static volatile sig_atomic_t received_sigterm = 0;
439 static volatile int received_nb_signals = 0;
440 static int exit_status = 0;
441
442 static SDL_Window *window;
443 static SDL_Renderer *renderer;
444 static SDL_RendererInfo renderer_info = {0};
445 static SDL_AudioDeviceID audio_dev;
446
447 static VkRenderer *vk_renderer;
448
449 static const struct TextureFormatEntry {
450 enum AVPixelFormat format;
451 int texture_fmt;
452 } sdl_texture_format_map[] = {
453 { AV_PIX_FMT_RGB8, SDL_PIXELFORMAT_RGB332 },
454 { AV_PIX_FMT_RGB444, SDL_PIXELFORMAT_RGB444 },
455 { AV_PIX_FMT_RGB555, SDL_PIXELFORMAT_RGB555 },
456 { AV_PIX_FMT_BGR555, SDL_PIXELFORMAT_BGR555 },
457 { AV_PIX_FMT_RGB565, SDL_PIXELFORMAT_RGB565 },
458 { AV_PIX_FMT_BGR565, SDL_PIXELFORMAT_BGR565 },
459 { AV_PIX_FMT_RGB24, SDL_PIXELFORMAT_RGB24 },
460 { AV_PIX_FMT_BGR24, SDL_PIXELFORMAT_BGR24 },
461 { AV_PIX_FMT_0RGB32, SDL_PIXELFORMAT_RGB888 },
462 { AV_PIX_FMT_0BGR32, SDL_PIXELFORMAT_BGR888 },
463 { AV_PIX_FMT_NE(RGB0, 0BGR), SDL_PIXELFORMAT_RGBX8888 },
464 { AV_PIX_FMT_NE(BGR0, 0RGB), SDL_PIXELFORMAT_BGRX8888 },
465 { AV_PIX_FMT_RGB32, SDL_PIXELFORMAT_ARGB8888 },
466 { AV_PIX_FMT_RGB32_1, SDL_PIXELFORMAT_RGBA8888 },
467 { AV_PIX_FMT_BGR32, SDL_PIXELFORMAT_ABGR8888 },
468 { AV_PIX_FMT_BGR32_1, SDL_PIXELFORMAT_BGRA8888 },
469 { AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV },
470 { AV_PIX_FMT_YUYV422, SDL_PIXELFORMAT_YUY2 },
471 { AV_PIX_FMT_UYVY422, SDL_PIXELFORMAT_UYVY },
472 };
473
474 ✗ static int opt_add_vfilter(void *optctx, const char *opt, const char *arg)
475 {
476 ✗ int ret = GROW_ARRAY(vfilters_list, nb_vfilters);
477 ✗ if (ret < 0)
478 ✗ return ret;
479
480 ✗ vfilters_list[nb_vfilters - 1] = av_strdup(arg);
481 ✗ if (!vfilters_list[nb_vfilters - 1])
482 ✗ return AVERROR(ENOMEM);
483
484 ✗ return 0;
485 }
486
487 static inline
488 ✗ int cmp_audio_fmts(enum AVSampleFormat fmt1, int64_t channel_count1,
489 enum AVSampleFormat fmt2, int64_t channel_count2)
490 {
491 /* If channel count == 1, planar and non-planar formats are the same */
492 ✗ if (channel_count1 == 1 && channel_count2 == 1)
493 ✗ return av_get_packed_sample_fmt(fmt1) != av_get_packed_sample_fmt(fmt2);
494 else
495 ✗ return channel_count1 != channel_count2 || fmt1 != fmt2;
496 }
497
498 ✗ static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
499 {
500 MyAVPacketList pkt1;
501 int ret;
502
503 ✗ if (q->abort_request)
504 ✗ return -1;
505
506
507 ✗ pkt1.pkt = pkt;
508 ✗ pkt1.serial = q->serial;
509
510 ✗ ret = av_fifo_write(q->pkt_list, &pkt1, 1);
511 ✗ if (ret < 0)
512 ✗ return ret;
513 ✗ q->nb_packets++;
514 ✗ q->size += pkt1.pkt->size + sizeof(pkt1);
515 ✗ q->duration += pkt1.pkt->duration;
516 /* XXX: should duplicate packet data in DV case */
517 ✗ SDL_CondSignal(q->cond);
518 ✗ return 0;
519 }
520
521 ✗ static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
522 {
523 AVPacket *pkt1;
524 int ret;
525
526 ✗ pkt1 = av_packet_alloc();
527 ✗ if (!pkt1) {
528 ✗ av_packet_unref(pkt);
529 ✗ return -1;
530 }
531 ✗ av_packet_move_ref(pkt1, pkt);
532
533 ✗ SDL_LockMutex(q->mutex);
534 ✗ ret = packet_queue_put_private(q, pkt1);
535 ✗ SDL_UnlockMutex(q->mutex);
536
537 ✗ if (ret < 0)
538 ✗ av_packet_free(&pkt1);
539
540 ✗ return ret;
541 }
542
543 ✗ static int packet_queue_put_nullpacket(PacketQueue *q, AVPacket *pkt, int stream_index)
544 {
545 ✗ pkt->stream_index = stream_index;
546 ✗ return packet_queue_put(q, pkt);
547 }
548
549 /* packet queue handling */
550 ✗ static int packet_queue_init(PacketQueue *q)
551 {
552 ✗ memset(q, 0, sizeof(PacketQueue));
553 ✗ q->pkt_list = av_fifo_alloc2(1, sizeof(MyAVPacketList), AV_FIFO_FLAG_AUTO_GROW);
554 ✗ if (!q->pkt_list)
555 ✗ return AVERROR(ENOMEM);
556 ✗ q->mutex = SDL_CreateMutex();
557 ✗ if (!q->mutex) {
558 ✗ av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
559 ✗ return AVERROR(ENOMEM);
560 }
561 ✗ q->cond = SDL_CreateCond();
562 ✗ if (!q->cond) {
563 ✗ av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
564 ✗ return AVERROR(ENOMEM);
565 }
566 ✗ q->abort_request = 1;
567 ✗ return 0;
568 }
569
570 ✗ static void packet_queue_flush(PacketQueue *q)
571 {
572 MyAVPacketList pkt1;
573
574 ✗ SDL_LockMutex(q->mutex);
575 ✗ while (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0)
576 ✗ av_packet_free(&pkt1.pkt);
577 ✗ q->nb_packets = 0;
578 ✗ q->size = 0;
579 ✗ q->duration = 0;
580 ✗ q->serial++;
581 ✗ SDL_UnlockMutex(q->mutex);
582 ✗ }
583
584 ✗ static void packet_queue_destroy(PacketQueue *q)
585 {
586 ✗ packet_queue_flush(q);
587 ✗ av_fifo_freep2(&q->pkt_list);
588 ✗ SDL_DestroyMutex(q->mutex);
589 ✗ SDL_DestroyCond(q->cond);
590 ✗ }
591
592 ✗ static void packet_queue_abort(PacketQueue *q)
593 {
594 ✗ SDL_LockMutex(q->mutex);
595
596 ✗ q->abort_request = 1;
597
598 ✗ SDL_CondSignal(q->cond);
599
600 ✗ SDL_UnlockMutex(q->mutex);
601 ✗ }
602
603 ✗ static void packet_queue_start(PacketQueue *q)
604 {
605 ✗ SDL_LockMutex(q->mutex);
606 ✗ q->abort_request = 0;
607 ✗ q->serial++;
608 ✗ SDL_UnlockMutex(q->mutex);
609 ✗ }
610
611 /* return < 0 if aborted, 0 if no packet and > 0 if packet. */
612 ✗ static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial)
613 {
614 MyAVPacketList pkt1;
615 int ret;
616
617 ✗ SDL_LockMutex(q->mutex);
618
619 for (;;) {
620 ✗ if (q->abort_request) {
621 ✗ ret = -1;
622 ✗ break;
623 }
624
625 ✗ if (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0) {
626 ✗ q->nb_packets--;
627 ✗ q->size -= pkt1.pkt->size + sizeof(pkt1);
628 ✗ q->duration -= pkt1.pkt->duration;
629 ✗ av_packet_move_ref(pkt, pkt1.pkt);
630 ✗ if (serial)
631 ✗ *serial = pkt1.serial;
632 ✗ av_packet_free(&pkt1.pkt);
633 ✗ ret = 1;
634 ✗ break;
635 ✗ } else if (!block) {
636 ✗ ret = 0;
637 ✗ break;
638 } else {
639 ✗ SDL_CondWait(q->cond, q->mutex);
640 }
641 }
642 ✗ SDL_UnlockMutex(q->mutex);
643 ✗ return ret;
644 }
645
646 ✗ static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, SDL_cond *empty_queue_cond) {
647 ✗ memset(d, 0, sizeof(Decoder));
648 ✗ d->pkt = av_packet_alloc();
649 ✗ if (!d->pkt)
650 ✗ return AVERROR(ENOMEM);
651 ✗ d->avctx = avctx;
652 ✗ d->queue = queue;
653 ✗ d->empty_queue_cond = empty_queue_cond;
654 ✗ d->start_pts = AV_NOPTS_VALUE;
655 ✗ d->pkt_serial = -1;
656 ✗ return 0;
657 }
658
659 ✗ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
660 ✗ int ret = AVERROR(EAGAIN);
661
662 for (;;) {
663 ✗ if (d->queue->serial == d->pkt_serial) {
664 do {
665 ✗ if (d->queue->abort_request)
666 ✗ return -1;
667
668 ✗ switch (d->avctx->codec_type) {
669 ✗ case AVMEDIA_TYPE_VIDEO:
670 ✗ ret = avcodec_receive_frame(d->avctx, frame);
671 ✗ if (ret >= 0) {
672 ✗ if (decoder_reorder_pts == -1) {
673 ✗ frame->pts = frame->best_effort_timestamp;
674 ✗ } else if (!decoder_reorder_pts) {
675 ✗ frame->pts = frame->pkt_dts;
676 }
677 }
678 ✗ break;
679 ✗ case AVMEDIA_TYPE_AUDIO:
680 ✗ ret = avcodec_receive_frame(d->avctx, frame);
681 ✗ if (ret >= 0) {
682 ✗ AVRational tb = (AVRational){1, frame->sample_rate};
683 ✗ if (frame->pts != AV_NOPTS_VALUE)
684 ✗ frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb);
685 ✗ else if (d->next_pts != AV_NOPTS_VALUE)
686 ✗ frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
687 ✗ if (frame->pts != AV_NOPTS_VALUE) {
688 ✗ d->next_pts = frame->pts + frame->nb_samples;
689 ✗ d->next_pts_tb = tb;
690 }
691 }
692 ✗ break;
693 }
694 ✗ if (ret == AVERROR_EOF) {
695 ✗ d->finished = d->pkt_serial;
696 ✗ avcodec_flush_buffers(d->avctx);
697 ✗ return 0;
698 }
699 ✗ if (ret >= 0)
700 ✗ return 1;
701 ✗ } while (ret != AVERROR(EAGAIN));
702 }
703
704 do {
705 ✗ if (d->queue->nb_packets == 0)
706 ✗ SDL_CondSignal(d->empty_queue_cond);
707 ✗ if (d->packet_pending) {
708 ✗ d->packet_pending = 0;
709 } else {
710 ✗ int old_serial = d->pkt_serial;
711 ✗ if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0)
712 ✗ return -1;
713 ✗ if (old_serial != d->pkt_serial) {
714 ✗ avcodec_flush_buffers(d->avctx);
715 ✗ d->finished = 0;
716 ✗ d->next_pts = d->start_pts;
717 ✗ d->next_pts_tb = d->start_pts_tb;
718 }
719 }
720 ✗ if (d->queue->serial == d->pkt_serial)
721 ✗ break;
722 ✗ av_packet_unref(d->pkt);
723 } while (1);
724
725 ✗ if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
726 ✗ int got_frame = 0;
727 ✗ ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
728 ✗ if (ret < 0) {
729 ✗ ret = AVERROR(EAGAIN);
730 } else {
731 ✗ if (got_frame && !d->pkt->data) {
732 ✗ d->packet_pending = 1;
733 }
734 ✗ ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
735 }
736 ✗ av_packet_unref(d->pkt);
737 } else {
738 ✗ if (d->pkt->buf && !d->pkt->opaque_ref) {
739 FrameData *fd;
740
741 ✗ d->pkt->opaque_ref = av_buffer_allocz(sizeof(*fd));
742 ✗ if (!d->pkt->opaque_ref)
743 ✗ return AVERROR(ENOMEM);
744 ✗ fd = (FrameData*)d->pkt->opaque_ref->data;
745 ✗ fd->pkt_pos = d->pkt->pos;
746 }
747
748 ✗ if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) {
749 ✗ av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
750 ✗ d->packet_pending = 1;
751 } else {
752 ✗ av_packet_unref(d->pkt);
753 }
754 }
755 }
756 }
757
758 ✗ static void decoder_destroy(Decoder *d) {
759 ✗ av_packet_free(&d->pkt);
760 ✗ avcodec_free_context(&d->avctx);
761 ✗ }
762
763 ✗ static void frame_queue_unref_item(Frame *vp)
764 {
765 ✗ av_frame_unref(vp->frame);
766 ✗ avsubtitle_free(&vp->sub);
767 ✗ }
768
769 ✗ static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
770 {
771 int i;
772 ✗ memset(f, 0, sizeof(FrameQueue));
773 ✗ if (!(f->mutex = SDL_CreateMutex())) {
774 ✗ av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
775 ✗ return AVERROR(ENOMEM);
776 }
777 ✗ if (!(f->cond = SDL_CreateCond())) {
778 ✗ av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
779 ✗ return AVERROR(ENOMEM);
780 }
781 ✗ f->pktq = pktq;
782 ✗ f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
783 ✗ f->keep_last = !!keep_last;
784 ✗ for (i = 0; i < f->max_size; i++)
785 ✗ if (!(f->queue[i].frame = av_frame_alloc()))
786 ✗ return AVERROR(ENOMEM);
787 ✗ return 0;
788 }
789
790 ✗ static void frame_queue_destroy(FrameQueue *f)
791 {
792 int i;
793 ✗ for (i = 0; i < f->max_size; i++) {
794 ✗ Frame *vp = &f->queue[i];
795 ✗ frame_queue_unref_item(vp);
796 ✗ av_frame_free(&vp->frame);
797 }
798 ✗ SDL_DestroyMutex(f->mutex);
799 ✗ SDL_DestroyCond(f->cond);
800 ✗ }
801
802 ✗ static void frame_queue_signal(FrameQueue *f)
803 {
804 ✗ SDL_LockMutex(f->mutex);
805 ✗ SDL_CondSignal(f->cond);
806 ✗ SDL_UnlockMutex(f->mutex);
807 ✗ }
808
809 ✗ static Frame *frame_queue_peek(FrameQueue *f)
810 {
811 ✗ return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
812 }
813
814 ✗ static Frame *frame_queue_peek_next(FrameQueue *f)
815 {
816 ✗ return &f->queue[(f->rindex + f->rindex_shown + 1) % f->max_size];
817 }
818
819 ✗ static Frame *frame_queue_peek_last(FrameQueue *f)
820 {
821 ✗ return &f->queue[f->rindex];
822 }
823
824 ✗ static Frame *frame_queue_peek_writable(FrameQueue *f)
825 {
826 /* wait until we have space to put a new frame */
827 ✗ SDL_LockMutex(f->mutex);
828 ✗ while (f->size >= f->max_size &&
829 ✗ !f->pktq->abort_request) {
830 ✗ SDL_CondWait(f->cond, f->mutex);
831 }
832 ✗ SDL_UnlockMutex(f->mutex);
833
834 ✗ if (f->pktq->abort_request)
835 ✗ return NULL;
836
837 ✗ return &f->queue[f->windex];
838 }
839
840 ✗ static Frame *frame_queue_peek_readable(FrameQueue *f)
841 {
842 /* wait until we have a readable a new frame */
843 ✗ SDL_LockMutex(f->mutex);
844 ✗ while (f->size - f->rindex_shown <= 0 &&
845 ✗ !f->pktq->abort_request) {
846 ✗ SDL_CondWait(f->cond, f->mutex);
847 }
848 ✗ SDL_UnlockMutex(f->mutex);
849
850 ✗ if (f->pktq->abort_request)
851 ✗ return NULL;
852
853 ✗ return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
854 }
855
856 ✗ static void frame_queue_push(FrameQueue *f)
857 {
858 ✗ if (++f->windex == f->max_size)
859 ✗ f->windex = 0;
860 ✗ SDL_LockMutex(f->mutex);
861 ✗ f->size++;
862 ✗ SDL_CondSignal(f->cond);
863 ✗ SDL_UnlockMutex(f->mutex);
864 ✗ }
865
866 ✗ static void frame_queue_next(FrameQueue *f)
867 {
868 ✗ if (f->keep_last && !f->rindex_shown) {
869 ✗ f->rindex_shown = 1;
870 ✗ return;
871 }
872 ✗ frame_queue_unref_item(&f->queue[f->rindex]);
873 ✗ if (++f->rindex == f->max_size)
874 ✗ f->rindex = 0;
875 ✗ SDL_LockMutex(f->mutex);
876 ✗ f->size--;
877 ✗ SDL_CondSignal(f->cond);
878 ✗ SDL_UnlockMutex(f->mutex);
879 }
880
881 /* return the number of undisplayed frames in the queue */
882 ✗ static int frame_queue_nb_remaining(FrameQueue *f)
883 {
884 ✗ return f->size - f->rindex_shown;
885 }
886
887 /* return last shown position */
888 ✗ static int64_t frame_queue_last_pos(FrameQueue *f)
889 {
890 ✗ Frame *fp = &f->queue[f->rindex];
891 ✗ if (f->rindex_shown && fp->serial == f->pktq->serial)
892 ✗ return fp->pos;
893 else
894 ✗ return -1;
895 }
896
897 ✗ static void decoder_abort(Decoder *d, FrameQueue *fq)
898 {
899 ✗ packet_queue_abort(d->queue);
900 ✗ frame_queue_signal(fq);
901 ✗ SDL_WaitThread(d->decoder_tid, NULL);
902 ✗ d->decoder_tid = NULL;
903 ✗ packet_queue_flush(d->queue);
904 ✗ }
905
906 ✗ static inline void fill_rectangle(int x, int y, int w, int h)
907 {
908 SDL_Rect rect;
909 ✗ rect.x = x;
910 ✗ rect.y = y;
911 ✗ rect.w = w;
912 ✗ rect.h = h;
913 ✗ if (w && h)
914 ✗ SDL_RenderFillRect(renderer, &rect);
915 ✗ }
916
917 ✗ static int realloc_texture(SDL_Texture **texture, Uint32 new_format, int new_width, int new_height, SDL_BlendMode blendmode, int init_texture)
918 {
919 Uint32 format;
920 int access, w, h;
921 ✗ if (!*texture || SDL_QueryTexture(*texture, &format, &access, &w, &h) < 0 || new_width != w || new_height != h || new_format != format) {
922 void *pixels;
923 int pitch;
924 ✗ if (*texture)
925 ✗ SDL_DestroyTexture(*texture);
926 ✗ if (!(*texture = SDL_CreateTexture(renderer, new_format, SDL_TEXTUREACCESS_STREAMING, new_width, new_height)))
927 ✗ return -1;
928 ✗ if (SDL_SetTextureBlendMode(*texture, blendmode) < 0)
929 ✗ return -1;
930 ✗ if (init_texture) {
931 ✗ if (SDL_LockTexture(*texture, NULL, &pixels, &pitch) < 0)
932 ✗ return -1;
933 ✗ memset(pixels, 0, pitch * new_height);
934 ✗ SDL_UnlockTexture(*texture);
935 }
936 ✗ av_log(NULL, AV_LOG_VERBOSE, "Created %dx%d texture with %s.\n", new_width, new_height, SDL_GetPixelFormatName(new_format));
937 }
938 ✗ return 0;
939 }
940
941 ✗ static void calculate_display_rect(SDL_Rect *rect,
942 int scr_xleft, int scr_ytop, int scr_width, int scr_height,
943 int pic_width, int pic_height, AVRational pic_sar)
944 {
945 ✗ AVRational aspect_ratio = pic_sar;
946 int64_t width, height, x, y;
947
948 ✗ if (av_cmp_q(aspect_ratio, av_make_q(0, 1)) <= 0)
949 ✗ aspect_ratio = av_make_q(1, 1);
950
951 ✗ aspect_ratio = av_mul_q(aspect_ratio, av_make_q(pic_width, pic_height));
952
953 /* XXX: we suppose the screen has a 1.0 pixel ratio */
954 ✗ height = scr_height;
955 ✗ width = av_rescale(height, aspect_ratio.num, aspect_ratio.den) & ~1;
956 ✗ if (width > scr_width) {
957 ✗ width = scr_width;
958 ✗ height = av_rescale(width, aspect_ratio.den, aspect_ratio.num) & ~1;
959 }
960 ✗ x = (scr_width - width) / 2;
961 ✗ y = (scr_height - height) / 2;
962 ✗ rect->x = scr_xleft + x;
963 ✗ rect->y = scr_ytop + y;
964 ✗ rect->w = FFMAX((int)width, 1);
965 ✗ rect->h = FFMAX((int)height, 1);
966 ✗ }
967
968 ✗ static void get_sdl_pix_fmt_and_blendmode(int format, Uint32 *sdl_pix_fmt, SDL_BlendMode *sdl_blendmode)
969 {
970 int i;
971 ✗ *sdl_blendmode = SDL_BLENDMODE_NONE;
972 ✗ *sdl_pix_fmt = SDL_PIXELFORMAT_UNKNOWN;
973 ✗ if (format == AV_PIX_FMT_RGB32 ||
974 ✗ format == AV_PIX_FMT_RGB32_1 ||
975 ✗ format == AV_PIX_FMT_BGR32 ||
976 format == AV_PIX_FMT_BGR32_1)
977 ✗ *sdl_blendmode = SDL_BLENDMODE_BLEND;
978 ✗ for (i = 0; i < FF_ARRAY_ELEMS(sdl_texture_format_map); i++) {
979 ✗ if (format == sdl_texture_format_map[i].format) {
980 ✗ *sdl_pix_fmt = sdl_texture_format_map[i].texture_fmt;
981 ✗ return;
982 }
983 }
984 }
985
986 ✗ static int upload_texture(SDL_Texture **tex, AVFrame *frame)
987 {
988 ✗ int ret = 0;
989 Uint32 sdl_pix_fmt;
990 SDL_BlendMode sdl_blendmode;
991 ✗ get_sdl_pix_fmt_and_blendmode(frame->format, &sdl_pix_fmt, &sdl_blendmode);
992 ✗ if (realloc_texture(tex, sdl_pix_fmt == SDL_PIXELFORMAT_UNKNOWN ? SDL_PIXELFORMAT_ARGB8888 : sdl_pix_fmt, frame->width, frame->height, sdl_blendmode, 0) < 0)
993 ✗ return -1;
994 ✗ switch (sdl_pix_fmt) {
995 ✗ case SDL_PIXELFORMAT_IYUV:
996 ✗ if (frame->linesize[0] > 0 && frame->linesize[1] > 0 && frame->linesize[2] > 0) {
997 ✗ ret = SDL_UpdateYUVTexture(*tex, NULL, frame->data[0], frame->linesize[0],
998 ✗ frame->data[1], frame->linesize[1],
999 ✗ frame->data[2], frame->linesize[2]);
1000 ✗ } else if (frame->linesize[0] < 0 && frame->linesize[1] < 0 && frame->linesize[2] < 0) {
1001 ✗ ret = SDL_UpdateYUVTexture(*tex, NULL, frame->data[0] + frame->linesize[0] * (frame->height - 1), -frame->linesize[0],
1002 ✗ frame->data[1] + frame->linesize[1] * (AV_CEIL_RSHIFT(frame->height, 1) - 1), -frame->linesize[1],
1003 ✗ frame->data[2] + frame->linesize[2] * (AV_CEIL_RSHIFT(frame->height, 1) - 1), -frame->linesize[2]);
1004 } else {
1005 ✗ av_log(NULL, AV_LOG_ERROR, "Mixed negative and positive linesizes are not supported.\n");
1006 ✗ return -1;
1007 }
1008 ✗ break;
1009 ✗ default:
1010 ✗ if (frame->linesize[0] < 0) {
1011 ✗ ret = SDL_UpdateTexture(*tex, NULL, frame->data[0] + frame->linesize[0] * (frame->height - 1), -frame->linesize[0]);
1012 } else {
1013 ✗ ret = SDL_UpdateTexture(*tex, NULL, frame->data[0], frame->linesize[0]);
1014 }
1015 ✗ break;
1016 }
1017 ✗ return ret;
1018 }
1019
1020 static enum AVColorSpace sdl_supported_color_spaces[] = {
1021 AVCOL_SPC_BT709,
1022 AVCOL_SPC_BT470BG,
1023 AVCOL_SPC_SMPTE170M,
1024 };
1025
1026 static enum AVAlphaMode sdl_supported_alpha_modes[] = {
1027 AVALPHA_MODE_UNSPECIFIED,
1028 AVALPHA_MODE_STRAIGHT,
1029 };
1030
1031 ✗ static void set_sdl_yuv_conversion_mode(AVFrame *frame)
1032 {
1033 #if SDL_VERSION_ATLEAST(2,0,8)
1034 ✗ SDL_YUV_CONVERSION_MODE mode = SDL_YUV_CONVERSION_AUTOMATIC;
1035 ✗ if (frame && (frame->format == AV_PIX_FMT_YUV420P || frame->format == AV_PIX_FMT_YUYV422 || frame->format == AV_PIX_FMT_UYVY422)) {
1036 ✗ if (frame->color_range == AVCOL_RANGE_JPEG)
1037 ✗ mode = SDL_YUV_CONVERSION_JPEG;
1038 ✗ else if (frame->colorspace == AVCOL_SPC_BT709)
1039 ✗ mode = SDL_YUV_CONVERSION_BT709;
1040 ✗ else if (frame->colorspace == AVCOL_SPC_BT470BG || frame->colorspace == AVCOL_SPC_SMPTE170M)
1041 ✗ mode = SDL_YUV_CONVERSION_BT601;
1042 }
1043 ✗ SDL_SetYUVConversionMode(mode); /* FIXME: no support for linear transfer */
1044 #endif
1045 ✗ }
1046
1047 ✗ static void draw_video_background(VideoState *is)
1048 {
1049 ✗ const int tile_size = VIDEO_BACKGROUND_TILE_SIZE;
1050 ✗ SDL_Rect *rect = &is->render_params.target_rect;
1051 SDL_BlendMode blendMode;
1052
1053 ✗ if (!SDL_GetTextureBlendMode(is->vid_texture, &blendMode) && blendMode == SDL_BLENDMODE_BLEND) {
1054 ✗ switch (is->render_params.video_background_type) {
1055 ✗ case VIDEO_BACKGROUND_TILES:
1056 ✗ SDL_SetRenderDrawColor(renderer, 237, 237, 237, 255);
1057 ✗ fill_rectangle(rect->x, rect->y, rect->w, rect->h);
1058 ✗ SDL_SetRenderDrawColor(renderer, 222, 222, 222, 255);
1059 ✗ for (int x = 0; x < rect->w; x += tile_size * 2)
1060 ✗ fill_rectangle(rect->x + x, rect->y, FFMIN(tile_size, rect->w - x), rect->h);
1061 ✗ for (int y = 0; y < rect->h; y += tile_size * 2)
1062 ✗ fill_rectangle(rect->x, rect->y + y, rect->w, FFMIN(tile_size, rect->h - y));
1063 ✗ SDL_SetRenderDrawColor(renderer, 237, 237, 237, 255);
1064 ✗ for (int y = 0; y < rect->h; y += tile_size * 2) {
1065 ✗ int h = FFMIN(tile_size, rect->h - y);
1066 ✗ for (int x = 0; x < rect->w; x += tile_size * 2)
1067 ✗ fill_rectangle(x + rect->x, y + rect->y, FFMIN(tile_size, rect->w - x), h);
1068 }
1069 ✗ break;
1070 ✗ case VIDEO_BACKGROUND_COLOR: {
1071 ✗ const uint8_t *c = is->render_params.video_background_color;
1072 ✗ SDL_SetRenderDrawColor(renderer, c[0], c[1], c[2], c[3]);
1073 ✗ fill_rectangle(rect->x, rect->y, rect->w, rect->h);
1074 ✗ break;
1075 }
1076 ✗ case VIDEO_BACKGROUND_NONE:
1077 ✗ SDL_SetTextureBlendMode(is->vid_texture, SDL_BLENDMODE_NONE);
1078 ✗ break;
1079 }
1080 }
1081 ✗ }
1082
1083 static int current_chapter(VideoState *is);
1084 static void seek_chapter(VideoState *is, int i);
1085
1086 ✗ static double chapter_list_alpha(VideoState *is, int64_t now)
1087 {
1088 ✗ double alpha = (now - is->chapter_fade_start) / (double)CHAPTER_LIST_FADE_TIME;
1089
1090 ✗ if (!is->chapter_texture)
1091 ✗ return 0;
1092 ✗ if (!is->chapter_pinned)
1093 ✗ alpha = FFMIN(alpha, 1 - (now - is->chapter_last_input - CHAPTER_LIST_HOLD_TIME) / (double)CHAPTER_LIST_FADE_TIME);
1094 ✗ return av_clipd(alpha, 0, 1);
1095 }
1096
1097 ✗ static const char *chapter_tag(const AVChapter *chapter, const char *key)
1098 {
1099 ✗ const AVDictionaryEntry *tag = av_dict_get(chapter->metadata, key, NULL, 0);
1100
1101 ✗ return tag ? tag->value : "";
1102 }
1103
1104 /* case-insensitive, with missing tags after all others */
1105 ✗ static int compare_tags(const char *a, const char *b)
1106 {
1107 ✗ return !*a != !*b ? !*a - !*b : av_strcasecmp(a, b);
1108 }
1109
1110 /* the rank of a chapter in the shuffled order for the current seed */
1111 ✗ static uint32_t shuffle_rank(int index)
1112 {
1113 ✗ uint32_t rank = (index + 1) * 0x9E3779B1u ^ chapter_shuffle_seed * 0x85EBCA77u;
1114
1115 ✗ rank ^= rank >> 15;
1116 ✗ rank *= 0x2C1B3C6Du;
1117 ✗ return rank ^ rank >> 12;
1118 }
1119
1120 ✗ static int compare_chapter_rows(const void *a, const void *b)
1121 {
1122 ✗ const ChapterRow *ra = a, *rb = b;
1123
1124 ✗ for (int i = 0; i < CHAPTER_SORT_NB; i++) {
1125 int cmp;
1126
1127 ✗ switch (chapter_sort[i].key) {
1128 ✗ case CHAPTER_SORT_ARTIST: cmp = compare_tags(ra->artist, rb->artist); break;
1129 ✗ case CHAPTER_SORT_TITLE: cmp = compare_tags(ra->title, rb->title); break;
1130 ✗ case CHAPTER_SORT_LENGTH: cmp = FFDIFFSIGN(ra->length, rb->length); break;
1131 ✗ case CHAPTER_SORT_SHUFFLE: cmp = FFDIFFSIGN(shuffle_rank(ra->index), shuffle_rank(rb->index)); break;
1132 ✗ default: cmp = ra->index - rb->index; break;
1133 }
1134 ✗ if (cmp)
1135 ✗ return chapter_sort[i].descending ? -cmp : cmp;
1136 }
1137 ✗ return 0;
1138 }
1139
1140 /* a column clicked again flips its order, any other becomes the first key ahead of the previous ones;
1141 * the shuffle button instead steps its seed by the side it was clicked on */
1142 ✗ static void chapter_sort_by(int key, int seed_step)
1143 {
1144 ✗ int i = 0;
1145
1146 ✗ while (chapter_sort[i].key != key)
1147 ✗ i++;
1148 ✗ if (key == CHAPTER_SORT_SHUFFLE)
1149 ✗ chapter_shuffle_seed += seed_step;
1150 ✗ else if (i == 0)
1151 ✗ chapter_sort[0].descending ^= 1;
1152 ✗ if (i == 0)
1153 ✗ return;
1154 ✗ memmove(&chapter_sort[1], &chapter_sort[0], i * sizeof(*chapter_sort));
1155 ✗ chapter_sort[0] = (ChapterSortKey){ key, 0 };
1156 }
1157
1158 /* rebuilds the rows matching the search in the current sort order, keeping the selected chapter selected */
1159 ✗ static int chapter_list_update_rows(VideoState *is)
1160 {
1161 ✗ int selected = is->nb_chapter_rows ? is->chapter_rows[is->chapter_selected].index : 0;
1162 ✗ ChapterRow *rows = av_realloc_array(is->chapter_rows, is->ic->nb_chapters, sizeof(*rows));
1163
1164 ✗ if (!rows)
1165 ✗ return AVERROR(ENOMEM);
1166 ✗ is->chapter_rows = rows;
1167 ✗ is->nb_chapter_rows = 0;
1168 ✗ for (int i = 0; i < is->ic->nb_chapters; i++) {
1169 ✗ AVChapter *chapter = is->ic->chapters[i];
1170 ✗ ChapterRow row = { i, chapter_tag(chapter, "artist"), chapter_tag(chapter, "title"), -1 };
1171
1172 ✗ if (chapter->end != AV_NOPTS_VALUE && chapter->end > chapter->start &&
1173 ✗ (uint64_t)chapter->end - chapter->start <= INT64_MAX)
1174 ✗ row.length = av_rescale_q(chapter->end - chapter->start, chapter->time_base, AV_TIME_BASE_Q);
1175 ✗ if (av_stristr(row.artist, is->chapter_search) || av_stristr(row.title, is->chapter_search))
1176 ✗ is->chapter_rows[is->nb_chapter_rows++] = row;
1177 }
1178 ✗ qsort(is->chapter_rows, is->nb_chapter_rows, sizeof(*is->chapter_rows), compare_chapter_rows);
1179 ✗ is->chapter_selected = 0;
1180 ✗ for (int row = 0; row < is->nb_chapter_rows; row++)
1181 ✗ if (is->chapter_rows[row].index == selected)
1182 ✗ is->chapter_selected = row;
1183 ✗ return 0;
1184 }
1185
1186 static const struct {
1187 const char *label;
1188 int width;
1189 } sort_buttons[CHAPTER_SORT_NB] = {
1190 [CHAPTER_SORT_NUMBER] = { "#", 5 },
1191 [CHAPTER_SORT_ARTIST] = { "Artist", 8 },
1192 [CHAPTER_SORT_TITLE] = { "Title", 7 },
1193 [CHAPTER_SORT_LENGTH] = { "Duration", 10 },
1194 [CHAPTER_SORT_SHUFFLE] = { "Shuffle", 14 },
1195 };
1196
1197 #define CHAPTER_LIST_EVENT "Dialogue: 0:00:00.00,9999:00:00.00,"
1198
1199 ✗ static void bprint_chapter_box(AVBPrint *script, const char *style, int x, int y, int w, int h)
1200 {
1201 ✗ av_bprintf(script, CHAPTER_LIST_EVENT "%s,{\\pos(%d,%d)\\p1}m 0 0 l %d 0 %d %d 0 %d{\\p0}\n", style, x, y, w, w, h, h);
1202 ✗ }
1203
1204 /* a text cell cut off at the right and bottom of its column, with the characters libass would read as tags or line breaks blanked */
1205 ✗ static void bprint_chapter_cell(AVBPrint *script, const char *style, int x, int y, int right, int bottom, const char *text)
1206 {
1207 ✗ av_bprintf(script, CHAPTER_LIST_EVENT "%s,{\\pos(%d,%d)\\clip(0,0,%d,%d)}", style, x, y, right, bottom);
1208 ✗ for (; *text; text++)
1209 ✗ av_bprint_chars(script, strchr("{}\\\r\n", *text) ? ' ' : *text, 1);
1210 ✗ av_bprint_chars(script, '\n', 1);
1211 ✗ }
1212
1213 typedef struct ChapterListLayout {
1214 int font, line, width, height, canvas_h, nb_rows, first;
1215 int button_x[CHAPTER_SORT_NB], button_y[CHAPTER_SORT_NB];
1216 int search_x, search_y, rows_y, text_x, text_right, length_x, detail_x, canvas_w;
1217 } ChapterListLayout;
1218
1219 /* button widths are in half font sizes, a quarter apart, wrapping onto further lines when the panel is too narrow */
1220 ✗ static ChapterListLayout chapter_list_layout(VideoState *is)
1221 {
1222 ChapterListLayout l;
1223 int x, y;
1224
1225 ✗ l.font = FFMAX(is->height / 30, 12);
1226 ✗ l.line = l.font * 9 / 4;
1227 ✗ l.width = FFMIN(FFMAX(is->width * 3 / 5, l.font * 26), is->width - l.font * 2);
1228 ✗ x = y = l.font / 2;
1229 ✗ for (int b = 0; b < CHAPTER_SORT_NB; b++) {
1230 ✗ int w = sort_buttons[b].width * l.font / 2;
1231
1232 ✗ if (x > l.font / 2 && x + w > l.width - l.font / 2) {
1233 ✗ x = l.font / 2;
1234 ✗ y += l.font * 3 / 2;
1235 }
1236 ✗ l.button_x[b] = x;
1237 ✗ l.button_y[b] = y;
1238 ✗ x += w + l.font / 4;
1239 }
1240 /* the search field follows the buttons, or takes its own line when they leave it less than 8 em */
1241 ✗ if (x + l.font * 8 > l.width - l.font / 2) {
1242 ✗ x = l.font / 2;
1243 ✗ y += l.font * 3 / 2;
1244 }
1245 ✗ l.search_x = x;
1246 ✗ l.search_y = y;
1247 ✗ l.rows_y = is->chapter_pinned ? y + l.font * 3 / 2 : l.button_y[CHAPTER_SORT_NB - 1] + l.font * 3 / 2;
1248 ✗ l.canvas_h = FFMIN(y + l.font * 2 + CHAPTER_LIST_ROWS * l.line, is->height - l.font * 2);
1249 ✗ l.nb_rows = FFMIN(is->nb_chapter_rows, FFMAX(1, (l.canvas_h - l.rows_y - l.font / 2) / l.line));
1250 ✗ l.first = av_clip(is->chapter_selected - l.nb_rows / 2, 0, is->nb_chapter_rows - l.nb_rows);
1251 ✗ l.height = l.rows_y + l.nb_rows * l.line + l.font / 2;
1252 ✗ l.text_x = l.font * 3;
1253 ✗ l.text_right = l.width - l.font * 11 / 2;
1254 ✗ l.length_x = l.width - l.font * 5 / 2;
1255 ✗ l.detail_x = l.width - l.font * 5 / 4;
1256 ✗ l.canvas_w = is->width - l.font * 2;
1257 ✗ return l;
1258 }
1259
1260 ✗ static ChapterListHit chapter_list_hit(VideoState *is, const ChapterListLayout *l, int x, int y)
1261 {
1262 ✗ ChapterListHit hit = { 0, -1, -1, 0 };
1263 int line;
1264
1265 ✗ x -= l->font;
1266 ✗ y -= l->font;
1267 ✗ if (x < 0 || y < 0 || x >= l->width || y >= FFMIN(l->height, l->canvas_h))
1268 ✗ return hit;
1269 ✗ hit.inside = 1;
1270 ✗ line = (y - l->rows_y) / l->line;
1271 ✗ if (y >= l->rows_y && line < l->nb_rows) {
1272 ✗ hit.row = l->first + line;
1273 ✗ hit.detail = x >= l->width - l->font * 2;
1274 }
1275 ✗ for (int b = 0; b < FF_ARRAY_ELEMS(sort_buttons); b++)
1276 ✗ if (x >= l->button_x[b] && x < l->button_x[b] + sort_buttons[b].width * l->font / 2 &&
1277 ✗ y >= l->button_y[b] && y < l->button_y[b] + l->font * 5 / 4)
1278 ✗ hit.button = b;
1279 ✗ return hit;
1280 }
1281
1282 /* a circle outline drawn as four bezier arcs around a center */
1283 ✗ static void bprint_chapter_ring(AVBPrint *script, int cx, int cy, int r)
1284 {
1285 ✗ int k = r * 552 / 1000;
1286
1287 ✗ av_bprintf(script, CHAPTER_LIST_EVENT "Ring,{\\an5\\pos(%d,%d)\\p1}m 0 %d b 0 %d %d 0 %d 0 b %d 0 %d %d %d %d "
1288 "b %d %d %d %d %d %d b %d %d 0 %d 0 %d{\\p0}\n",
1289 cx, cy, r, r - k, r - k, r, r + k, 2 * r, r - k, 2 * r, r, 2 * r, r + k, r + k, 2 * r, r, 2 * r,
1290 r - k, 2 * r, r + k, r);
1291 ✗ }
1292
1293 /* the metadata of the chapter whose symbol the mouse is over, as a popup beside the list or over its rows when the window is narrow */
1294 ✗ static void bprint_chapter_details(VideoState *is, AVBPrint *script, const ChapterListLayout *l)
1295 {
1296 ✗ const AVChapter *chapter = is->ic->chapters[is->chapter_rows[is->chapter_hover.row].index];
1297 ✗ const AVDictionaryEntry *tag = NULL;
1298 ✗ int beside = l->canvas_w - l->width >= l->font * 12;
1299 ✗ int height = FFMIN(av_dict_count(chapter->metadata) * l->font + l->font / 2, is->chapter_rect.h);
1300 ✗ int x = beside ? l->width + l->font / 2 : l->text_x;
1301 ✗ int right = beside ? l->canvas_w : l->length_x - l->font / 2;
1302 ✗ int y = av_clip(l->rows_y + (is->chapter_hover.row - l->first) * l->line, 0, is->chapter_rect.h - height);
1303
1304 ✗ if (!av_dict_count(chapter->metadata))
1305 ✗ return;
1306 ✗ bprint_chapter_box(script, "Popup", x, y, right - x, height);
1307 ✗ for (int i = 0; (tag = av_dict_iterate(chapter->metadata, tag)); i++) {
1308 ✗ int line_y = y + l->font / 4 + i * l->font;
1309
1310 ✗ bprint_chapter_cell(script, "Detail", x + l->font / 2, line_y, x + l->font * 7, y + height, tag->key);
1311 ✗ bprint_chapter_cell(script, "Detail", x + l->font * 15 / 2, line_y, right - l->font / 4, y + height, tag->value);
1312 }
1313 }
1314
1315 ✗ static void chapter_list_script(VideoState *is, AVBPrint *script)
1316 {
1317 ✗ ChapterListLayout l = chapter_list_layout(is);
1318
1319 ✗ is->chapter_hover = chapter_list_hit(is, &l, is->chapter_mouse_x, is->chapter_mouse_y);
1320 ✗ is->chapter_rect = (SDL_Rect){ l.font, l.font, l.canvas_w, l.canvas_h };
1321 ✗ av_bprintf(script,
1322 "[Script Info]\nScriptType: v4.00+\nPlayResX: %d\nPlayResY: %d\nWrapStyle: 2\nYCbCr Matrix: None\n\n"
1323 "[V4+ Styles]\n"
1324 "Format: Name, Fontname, Fontsize, PrimaryColour, OutlineColour, Bold, Outline, Alignment\n"
1325 "Style: Panel,Sans,%d,&H40000000,&H40000000,0,0,7\n"
1326 "Style: Button,Sans,%d,&H80FFFFFF,&H80FFFFFF,0,0,7\n"
1327 "Style: Row,Sans,%d,&H00FFFFFF,&H00000000,0,%d,7\n"
1328 "Style: Selected,Sans,%d,&H0080D0FF,&H00000000,-1,%d,7\n"
1329 "Style: Artist,Sans,%d,&H00C0C0C0,&H00000000,0,%d,7\n"
1330 "Style: ArtistSelected,Sans,%d,&H0080D0FF,&H00000000,-1,%d,7\n"
1331 "Style: Playing,Sans,%d,&H00FFFFFF,&H00FFFFFF,0,0,7\n"
1332 "Style: Hint,Sans,%d,&H00A0A0A0,&H00000000,0,%d,7\n"
1333 "Style: Hover,Sans,%d,&HC8FFFFFF,&HC8FFFFFF,0,0,7\n"
1334 "Style: ButtonHover,Sans,%d,&H50FFFFFF,&H50FFFFFF,0,0,7\n"
1335 "Style: Ring,Sans,%d,&HFF000000,&H00A0A0A0,0,%d,7\n"
1336 "Style: Popup,Sans,%d,&H20000000,&H20000000,0,0,7\n"
1337 "Style: Detail,Sans,%d,&H00FFFFFF,&H00000000,0,%d,7\n\n"
1338 "[Events]\nFormat: Start, End, Style, Text\n",
1339 ✗ is->chapter_rect.w, is->chapter_rect.h, l.font, l.font, l.font, l.font / 16 + 1, l.font, l.font / 16 + 1,
1340 ✗ l.font * 4 / 5, l.font / 16 + 1, l.font * 4 / 5, l.font / 16 + 1, l.font, l.font, l.font / 16 + 1, l.font, l.font,
1341 ✗ l.font, l.font / 16 + 1, l.font, l.font * 4 / 5, l.font / 16 + 1);
1342 ✗ bprint_chapter_box(script, "Panel", 0, 0, l.width, l.height);
1343 ✗ if (is->chapter_pinned)
1344 ✗ bprint_chapter_cell(script, *is->chapter_search ? "Row" : "Hint", l.search_x, l.search_y, l.length_x,
1345 ✗ l.search_y + l.font * 3 / 2, *is->chapter_search ? is->chapter_search : "type to search");
1346 ✗ for (int b = 0; b < FF_ARRAY_ELEMS(sort_buttons); b++) {
1347 ✗ int x = l.button_x[b], y = l.button_y[b], w = sort_buttons[b].width * l.font / 2;
1348
1349 ✗ bprint_chapter_box(script, b == is->chapter_hover.button ? "ButtonHover" : "Button", x, y, w, l.font * 5 / 4);
1350 ✗ av_bprintf(script, CHAPTER_LIST_EVENT "%s,{\\an5\\pos(%d,%d)}", chapter_sort[0].key == b ? "Selected" : "Row",
1351 ✗ x + w / 2, y + l.font * 5 / 8);
1352 ✗ if (b == CHAPTER_SORT_SHUFFLE)
1353 ✗ av_bprintf(script, "\xe2\x97\x82 %s %d \xe2\x96\xb8\n", sort_buttons[b].label, chapter_shuffle_seed);
1354 else
1355 ✗ av_bprintf(script, "%s%s\n", sort_buttons[b].label,
1356 ✗ chapter_sort[0].key != b ? "" : chapter_sort[0].descending ? " \xe2\x96\xbc" : " \xe2\x96\xb2");
1357 }
1358 ✗ is->chapter_playing = current_chapter(is);
1359 ✗ for (int r = l.first; r < l.first + l.nb_rows; r++) {
1360 ✗ const ChapterRow *row = &is->chapter_rows[r];
1361 ✗ const char *style = r == is->chapter_selected ? "Selected" : "Row";
1362 ✗ int y = l.rows_y + (r - l.first) * l.line;
1363 int64_t seconds;
1364
1365 ✗ if (r == is->chapter_hover.row)
1366 ✗ bprint_chapter_box(script, "Hover", 0, y, l.width, l.line);
1367 ✗ if (row->index == is->chapter_playing)
1368 ✗ bprint_chapter_box(script, "Playing", 0, y, l.font / 6, l.line);
1369 ✗ av_bprintf(script, CHAPTER_LIST_EVENT "%s,{\\an6\\pos(%d,%d)}%d\n", style, l.font * 5 / 2, y + l.line / 2, row->index + 1);
1370 ✗ bprint_chapter_cell(script, style, l.text_x, y + l.font / 8, l.text_right, y + l.line, row->title);
1371 ✗ bprint_chapter_cell(script, r == is->chapter_selected ? "ArtistSelected" : "Artist", l.text_x, y + l.font * 5 / 4,
1372 ✗ l.text_right, y + l.line, row->artist);
1373 ✗ bprint_chapter_ring(script, l.detail_x, y + l.line / 2, l.font * 9 / 20);
1374 ✗ av_bprintf(script, CHAPTER_LIST_EVENT "Hint,{\\an5\\pos(%d,%d)}i\n", l.detail_x, y + l.line / 2);
1375 ✗ if (row->length < 0)
1376 ✗ continue;
1377 ✗ seconds = (row->length + AV_TIME_BASE / 2) / AV_TIME_BASE;
1378 ✗ av_bprintf(script, CHAPTER_LIST_EVENT "%s,{\\an6\\pos(%d,%d)}", style, l.length_x, y + l.line / 2);
1379 ✗ if (seconds >= 3600)
1380 ✗ av_bprintf(script, "%d:%02d:%02d\n", (int)(seconds / 3600), (int)(seconds / 60 % 60), (int)(seconds % 60));
1381 else
1382 ✗ av_bprintf(script, "%d:%02d\n", (int)(seconds / 60), (int)(seconds % 60));
1383 }
1384 ✗ if (is->chapter_hover.detail)
1385 ✗ bprint_chapter_details(is, script, &l);
1386 ✗ }
1387
1388 ✗ static int chapter_list_configure(VideoState *is, const char *script)
1389 {
1390 ✗ const AVFilter *ass_filter = avfilter_get_by_name("ass");
1391 AVFilterContext *source, *ass;
1392 char source_args[64];
1393 int ret;
1394
1395 ✗ if (!ass_filter) {
1396 static int reported;
1397
1398 ✗ if (!reported++)
1399 ✗ av_log(NULL, AV_LOG_ERROR, "The chapter list needs the ass filter, which this build lacks\n");
1400 ✗ return AVERROR_FILTER_NOT_FOUND;
1401 }
1402 ✗ avfilter_graph_free(&is->chapter_graph);
1403 ✗ if (!(is->chapter_graph = avfilter_graph_alloc()))
1404 ✗ return AVERROR(ENOMEM);
1405 ✗ snprintf(source_args, sizeof(source_args), "color=black@0:rate=1:size=%dx%d", is->chapter_rect.w, is->chapter_rect.h);
1406 ✗ ass = avfilter_graph_alloc_filter(is->chapter_graph, ass_filter, "ass");
1407 ✗ is->chapter_sink = avfilter_graph_alloc_filter(is->chapter_graph, avfilter_get_by_name("buffersink"), "sink");
1408 ✗ if (!ass || !is->chapter_sink)
1409 ✗ return AVERROR(ENOMEM);
1410 ✗ if ((ret = avfilter_graph_create_filter(&source, avfilter_get_by_name("color"), "source", source_args, NULL, is->chapter_graph)) < 0 ||
1411 ✗ (ret = av_opt_set(ass, "script", script, AV_OPT_SEARCH_CHILDREN)) < 0 ||
1412 ✗ (ret = av_opt_set_int(ass, "alpha", 1, AV_OPT_SEARCH_CHILDREN)) < 0 ||
1413 ✗ (ret = avfilter_init_str(ass, NULL)) < 0 ||
1414 ✗ (ret = av_opt_set_array(is->chapter_sink, "pixel_formats", AV_OPT_SEARCH_CHILDREN, 0, 1, AV_OPT_TYPE_PIXEL_FMT,
1415 ✗ &(enum AVPixelFormat){ AV_PIX_FMT_RGB32 })) < 0 ||
1416 ✗ (ret = avfilter_init_str(is->chapter_sink, NULL)) < 0 ||
1417 ✗ (ret = avfilter_link(source, 0, ass, 0)) < 0 ||
1418 ✗ (ret = avfilter_link(ass, 0, is->chapter_sink, 0)) < 0)
1419 ✗ return ret;
1420 ✗ return avfilter_graph_config(is->chapter_graph, NULL);
1421 }
1422
1423 ✗ static int chapter_list_render(VideoState *is)
1424 {
1425 AVBPrint script;
1426 ✗ AVFrame *frame = av_frame_alloc();
1427 void *pixels;
1428 int pitch, ret;
1429
1430 ✗ if (!frame)
1431 ✗ return AVERROR(ENOMEM);
1432 ✗ is->chapter_render_pending = 0;
1433 ✗ av_bprint_init(&script, 0, AV_BPRINT_SIZE_UNLIMITED);
1434 ✗ chapter_list_script(is, &script);
1435 ✗ if (!av_bprint_is_complete(&script))
1436 ✗ ret = AVERROR(ENOMEM);
1437 ✗ else if (is->chapter_graph)
1438 ✗ ret = avfilter_graph_send_command(is->chapter_graph, "ass", "script", script.str, NULL, 0, 0);
1439 else
1440 ✗ ret = chapter_list_configure(is, script.str);
1441 ✗ if (ret >= 0)
1442 ✗ ret = av_buffersink_get_frame(is->chapter_sink, frame);
1443 ✗ if (ret >= 0 && (realloc_texture(&is->chapter_texture, SDL_PIXELFORMAT_ARGB8888, frame->width, frame->height, SDL_BLENDMODE_BLEND, 0) < 0 ||
1444 ✗ SDL_LockTexture(is->chapter_texture, NULL, &pixels, &pitch) < 0))
1445 ✗ ret = AVERROR_EXTERNAL;
1446 ✗ if (ret >= 0) {
1447 ✗ int alpha = av_pix_fmt_desc_get(AV_PIX_FMT_RGB32)->comp[3].offset;
1448
1449 /* the ass filter composites onto the transparent canvas with premultiplied alpha, SDL blends straight alpha */
1450 ✗ for (int y = 0; y < frame->height; y++) {
1451 ✗ const uint8_t *src = frame->data[0] + y * frame->linesize[0];
1452 ✗ uint8_t *dst = (uint8_t *)pixels + y * pitch;
1453
1454 ✗ for (int x = 0; x < frame->width * 4; x += 4)
1455 ✗ for (int c = 0; c < 4; c++)
1456 ✗ dst[x + c] = c == alpha || !src[x + alpha] ? src[x + c] : FFMIN(src[x + c] * 255 / src[x + alpha], 255);
1457 }
1458 ✗ SDL_UnlockTexture(is->chapter_texture);
1459 ✗ is->force_refresh = 1;
1460 }
1461 ✗ if (ret < 0) {
1462 ✗ if (ret != AVERROR_FILTER_NOT_FOUND)
1463 ✗ av_log(NULL, AV_LOG_ERROR, "Could not render the chapter list: %s\n", av_err2str(ret));
1464 ✗ avfilter_graph_free(&is->chapter_graph);
1465 ✗ if (is->chapter_texture)
1466 ✗ SDL_DestroyTexture(is->chapter_texture);
1467 ✗ is->chapter_texture = NULL;
1468 ✗ is->chapter_pinned = 0;
1469 ✗ SDL_StopTextInput();
1470 }
1471 ✗ av_frame_free(&frame);
1472 ✗ av_bprint_finalize(&script, NULL);
1473 ✗ return ret;
1474 }
1475
1476 /* shows the list with the chapter selected, or with the selection kept when no row shows it */
1477 ✗ static void chapter_list_show(VideoState *is, int chapter)
1478 {
1479 ✗ int64_t now = av_gettime_relative();
1480
1481 ✗ if (!renderer || chapter_list_update_rows(is) < 0)
1482 ✗ return;
1483 ✗ for (int row = 0; row < is->nb_chapter_rows; row++)
1484 ✗ if (is->chapter_rows[row].index == chapter)
1485 ✗ is->chapter_selected = row;
1486 ✗ is->chapter_fade_start = now - chapter_list_alpha(is, now) * CHAPTER_LIST_FADE_TIME;
1487 ✗ is->chapter_last_input = now;
1488 ✗ chapter_list_render(is);
1489 }
1490
1491 ✗ static void chapter_list_move(VideoState *is, int delta)
1492 {
1493 ✗ if (is->nb_chapter_rows)
1494 ✗ chapter_list_show(is, is->chapter_rows[av_clip(is->chapter_selected + delta, 0, is->nb_chapter_rows - 1)].index);
1495 ✗ }
1496
1497 ✗ static int chapter_list_visible(VideoState *is)
1498 {
1499 ✗ return chapter_list_alpha(is, av_gettime_relative()) > 0;
1500 }
1501
1502 /* appends the typed text to the search, or erases its last character when nothing was typed */
1503 ✗ static void chapter_list_search(VideoState *is, const char *typed)
1504 {
1505 ✗ int len = strlen(is->chapter_search);
1506
1507 ✗ if (typed) {
1508 ✗ if (len + strlen(typed) < sizeof(is->chapter_search))
1509 ✗ av_strlcat(is->chapter_search, typed, sizeof(is->chapter_search));
1510 ✗ } else if (len) {
1511 ✗ while (len > 0 && (is->chapter_search[--len] & 0xC0) == 0x80)
1512 ;
1513 ✗ is->chapter_search[len] = 0;
1514 }
1515 ✗ chapter_list_update_rows(is);
1516 ✗ chapter_list_render(is);
1517 ✗ }
1518
1519 ✗ static void chapter_list_pin(VideoState *is, int pinned)
1520 {
1521 ✗ if (!renderer)
1522 ✗ return;
1523 ✗ is->chapter_pinned = pinned;
1524 ✗ if (pinned) {
1525 ✗ chapter_list_show(is, current_chapter(is));
1526 ✗ is->chapter_pinned = !!is->chapter_texture;
1527 ✗ if (is->chapter_pinned)
1528 ✗ SDL_StartTextInput();
1529 } else {
1530 ✗ SDL_StopTextInput();
1531 ✗ is->chapter_last_input = av_gettime_relative() - CHAPTER_LIST_HOLD_TIME;
1532 ✗ is->chapter_search[0] = 0;
1533 ✗ chapter_list_update_rows(is);
1534 ✗ chapter_list_render(is);
1535 }
1536 }
1537
1538 /* keys while the list is pinned; typed characters arrive as text input instead */
1539 ✗ static void chapter_list_key(VideoState *is, SDL_Keycode key)
1540 {
1541 ✗ switch (key) {
1542 ✗ case SDLK_ESCAPE: chapter_list_pin(is, 0); break;
1543 ✗ case SDLK_UP: chapter_list_move(is, -1); break;
1544 ✗ case SDLK_DOWN: chapter_list_move(is, 1); break;
1545 ✗ case SDLK_PAGEUP: seek_chapter(is, current_chapter(is) + 1); break;
1546 ✗ case SDLK_PAGEDOWN: seek_chapter(is, current_chapter(is) - 1); break;
1547 ✗ case SDLK_BACKSPACE: chapter_list_search(is, NULL); break;
1548 ✗ case SDLK_RETURN:
1549 case SDLK_KP_ENTER:
1550 ✗ if (is->nb_chapter_rows)
1551 ✗ seek_chapter(is, is->chapter_rows[is->chapter_selected].index);
1552 ✗ break;
1553 }
1554 ✗ }
1555
1556 /* keeps the mouse position for the next render and asks for one when what a visible list points at changed;
1557 * the render waits until the pending events are handled, so a sweep across the list renders once */
1558 ✗ static void chapter_list_hover(VideoState *is, int x, int y)
1559 {
1560 ChapterListLayout l;
1561 ChapterListHit hit;
1562
1563 ✗ is->chapter_mouse_x = x;
1564 ✗ is->chapter_mouse_y = y;
1565 ✗ if (!chapter_list_visible(is))
1566 ✗ return;
1567 ✗ l = chapter_list_layout(is);
1568 ✗ hit = chapter_list_hit(is, &l, x, y);
1569 ✗ if (hit.row != is->chapter_hover.row || hit.button != is->chapter_hover.button || hit.detail != is->chapter_hover.detail)
1570 ✗ is->chapter_render_pending = 1;
1571 }
1572
1573 /* seeks to the entry under a click or sorts by the button under it, returns whether the click hit the list at all */
1574 ✗ static int chapter_list_click(VideoState *is, int x, int y)
1575 {
1576 ✗ ChapterListLayout l = chapter_list_layout(is);
1577 ✗ ChapterListHit hit = chapter_list_hit(is, &l, x, y);
1578
1579 ✗ if (hit.row >= 0 && !hit.detail)
1580 ✗ seek_chapter(is, is->chapter_rows[hit.row].index);
1581 ✗ if (hit.button >= 0) {
1582 ✗ int button_w = sort_buttons[hit.button].width * l.font / 2;
1583
1584 ✗ chapter_sort_by(hit.button, (x - l.font - l.button_x[hit.button]) * 2 < button_w ? -1 : 1);
1585 ✗ chapter_list_show(is, -1);
1586 }
1587 ✗ return hit.inside;
1588 }
1589
1590 ✗ static void chapter_list_draw(VideoState *is)
1591 {
1592 ✗ double alpha = chapter_list_alpha(is, av_gettime_relative());
1593
1594 ✗ is->chapter_drawn_alpha = alpha;
1595 ✗ if (alpha > 0 && is->chapter_texture) {
1596 ✗ SDL_SetTextureAlphaMod(is->chapter_texture, alpha * 255);
1597 ✗ SDL_RenderCopy(renderer, is->chapter_texture, NULL, &is->chapter_rect);
1598 }
1599 ✗ }
1600
1601 ✗ static void video_image_display(VideoState *is)
1602 {
1603 Frame *vp;
1604 ✗ Frame *sp = NULL;
1605 ✗ SDL_Rect *rect = &is->render_params.target_rect;
1606
1607 ✗ vp = frame_queue_peek_last(&is->pictq);
1608 ✗ calculate_display_rect(rect, is->xleft, is->ytop, is->width, is->height, vp->width, vp->height, vp->sar);
1609 ✗ if (vk_renderer) {
1610 ✗ vk_renderer_display(vk_renderer, vp->frame, &is->render_params);
1611 ✗ return;
1612 }
1613
1614 ✗ if (is->subtitle_st) {
1615 ✗ if (frame_queue_nb_remaining(&is->subpq) > 0) {
1616 ✗ sp = frame_queue_peek(&is->subpq);
1617
1618 ✗ if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
1619 ✗ if (!sp->uploaded) {
1620 uint8_t* pixels[4];
1621 int pitch[4];
1622 int i;
1623 ✗ if (!sp->width || !sp->height) {
1624 ✗ sp->width = vp->width;
1625 ✗ sp->height = vp->height;
1626 }
1627 ✗ if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
1628 ✗ return;
1629
1630 ✗ for (i = 0; i < sp->sub.num_rects; i++) {
1631 ✗ AVSubtitleRect *sub_rect = sp->sub.rects[i];
1632
1633 ✗ sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
1634 ✗ sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
1635 ✗ sub_rect->w = av_clip(sub_rect->w, 0, sp->width - sub_rect->x);
1636 ✗ sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
1637
1638 ✗ is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
1639 sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
1640 sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
1641 0, NULL, NULL, NULL);
1642 ✗ if (!is->sub_convert_ctx) {
1643 ✗ av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
1644 ✗ return;
1645 }
1646 ✗ if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
1647 ✗ sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
1648 0, sub_rect->h, pixels, pitch);
1649 ✗ SDL_UnlockTexture(is->sub_texture);
1650 }
1651 }
1652 ✗ sp->uploaded = 1;
1653 }
1654 } else
1655 ✗ sp = NULL;
1656 }
1657 }
1658
1659 ✗ set_sdl_yuv_conversion_mode(vp->frame);
1660
1661 ✗ if (!vp->uploaded) {
1662 ✗ if (upload_texture(&is->vid_texture, vp->frame) < 0) {
1663 ✗ set_sdl_yuv_conversion_mode(NULL);
1664 ✗ return;
1665 }
1666 ✗ vp->uploaded = 1;
1667 ✗ vp->flip_v = vp->frame->linesize[0] < 0;
1668 }
1669
1670 ✗ draw_video_background(is);
1671 ✗ SDL_RenderCopyEx(renderer, is->vid_texture, NULL, rect, 0, NULL, vp->flip_v ? SDL_FLIP_VERTICAL : 0);
1672 ✗ set_sdl_yuv_conversion_mode(NULL);
1673 ✗ if (sp) {
1674 #if USE_ONEPASS_SUBTITLE_RENDER
1675 ✗ SDL_RenderCopy(renderer, is->sub_texture, NULL, rect);
1676 #else
1677 int i;
1678 double xratio = (double)rect->w / (double)sp->width;
1679 double yratio = (double)rect->h / (double)sp->height;
1680 for (i = 0; i < sp->sub.num_rects; i++) {
1681 SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
1682 SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
1683 .y = rect.y + sub_rect->y * yratio,
1684 .w = sub_rect->w * xratio,
1685 .h = sub_rect->h * yratio};
1686 SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
1687 }
1688 #endif
1689 }
1690 }
1691
1692 ✗ static inline int compute_mod(int a, int b)
1693 {
1694 ✗ return a < 0 ? a%b + b : a%b;
1695 }
1696
1697 ✗ static void video_audio_display(VideoState *s)
1698 {
1699 int i, i_start, x, y1, y, ys, delay, n, nb_display_channels;
1700 int ch, channels, h, h2;
1701 int64_t time_diff;
1702 int rdft_bits, nb_freq;
1703
1704 ✗ for (rdft_bits = 1; (1 << rdft_bits) < 2 * s->height; rdft_bits++)
1705 ;
1706 ✗ nb_freq = 1 << (rdft_bits - 1);
1707
1708 /* compute display index : center on currently output samples */
1709 ✗ channels = s->audio_tgt.ch_layout.nb_channels;
1710 ✗ nb_display_channels = channels;
1711 ✗ if (!s->paused) {
1712 ✗ int data_used= s->show_mode == SHOW_MODE_WAVES ? s->width : (2*nb_freq);
1713 ✗ n = 2 * channels;
1714 ✗ delay = s->audio_write_buf_size;
1715 ✗ delay /= n;
1716
1717 /* to be more precise, we take into account the time spent since
1718 the last buffer computation */
1719 ✗ if (audio_callback_time) {
1720 ✗ time_diff = av_gettime_relative() - audio_callback_time;
1721 ✗ delay -= (time_diff * s->audio_tgt.freq) / 1000000;
1722 }
1723
1724 ✗ delay += 2 * data_used;
1725 ✗ if (delay < data_used)
1726 ✗ delay = data_used;
1727
1728 ✗ i_start= x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);
1729 ✗ if (s->show_mode == SHOW_MODE_WAVES) {
1730 ✗ h = INT_MIN;
1731 ✗ for (i = 0; i < 1000; i += channels) {
1732 ✗ int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;
1733 ✗ int a = s->sample_array[idx];
1734 ✗ int b = s->sample_array[(idx + 4 * channels) % SAMPLE_ARRAY_SIZE];
1735 ✗ int c = s->sample_array[(idx + 5 * channels) % SAMPLE_ARRAY_SIZE];
1736 ✗ int d = s->sample_array[(idx + 9 * channels) % SAMPLE_ARRAY_SIZE];
1737 ✗ int score = a - d;
1738 ✗ if (h < score && (b ^ c) < 0) {
1739 ✗ h = score;
1740 ✗ i_start = idx;
1741 }
1742 }
1743 }
1744
1745 ✗ s->last_i_start = i_start;
1746 } else {
1747 ✗ i_start = s->last_i_start;
1748 }
1749
1750 ✗ if (s->show_mode == SHOW_MODE_WAVES) {
1751 ✗ SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
1752
1753 /* total height for one channel */
1754 ✗ h = s->height / nb_display_channels;
1755 /* graph height / 2 */
1756 ✗ h2 = (h * 9) / 20;
1757 ✗ for (ch = 0; ch < nb_display_channels; ch++) {
1758 ✗ i = i_start + ch;
1759 ✗ y1 = s->ytop + ch * h + (h / 2); /* position of center line */
1760 ✗ for (x = 0; x < s->width; x++) {
1761 ✗ y = (s->sample_array[i] * h2) >> 15;
1762 ✗ if (y < 0) {
1763 ✗ y = -y;
1764 ✗ ys = y1 - y;
1765 } else {
1766 ✗ ys = y1;
1767 }
1768 ✗ fill_rectangle(s->xleft + x, ys, 1, y);
1769 ✗ i += channels;
1770 ✗ if (i >= SAMPLE_ARRAY_SIZE)
1771 ✗ i -= SAMPLE_ARRAY_SIZE;
1772 }
1773 }
1774
1775 ✗ SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
1776
1777 ✗ for (ch = 1; ch < nb_display_channels; ch++) {
1778 ✗ y = s->ytop + ch * h;
1779 ✗ fill_rectangle(s->xleft, y, s->width, 1);
1780 }
1781 } else {
1782 ✗ int err = 0;
1783 ✗ if (realloc_texture(&s->vis_texture, SDL_PIXELFORMAT_ARGB8888, s->width, s->height, SDL_BLENDMODE_NONE, 1) < 0)
1784 ✗ return;
1785
1786 ✗ if (s->xpos >= s->width)
1787 ✗ s->xpos = 0;
1788 ✗ nb_display_channels= FFMIN(nb_display_channels, 2);
1789 ✗ if (rdft_bits != s->rdft_bits) {
1790 ✗ const float rdft_scale = 1.0;
1791 ✗ av_tx_uninit(&s->rdft);
1792 ✗ av_freep(&s->real_data);
1793 ✗ av_freep(&s->rdft_data);
1794 ✗ s->rdft_bits = rdft_bits;
1795 ✗ s->real_data = av_malloc_array(nb_freq, 4 *sizeof(*s->real_data));
1796 ✗ s->rdft_data = av_malloc_array(nb_freq + 1, 2 *sizeof(*s->rdft_data));
1797 ✗ err = av_tx_init(&s->rdft, &s->rdft_fn, AV_TX_FLOAT_RDFT,
1798 0, 1 << rdft_bits, &rdft_scale, 0);
1799 }
1800 ✗ if (err < 0 || !s->rdft_data) {
1801 ✗ av_log(NULL, AV_LOG_ERROR, "Failed to allocate buffers for RDFT, switching to waves display\n");
1802 ✗ s->show_mode = SHOW_MODE_WAVES;
1803 } else {
1804 float *data_in[2];
1805 AVComplexFloat *data[2];
1806 ✗ SDL_Rect rect = {.x = s->xpos, .y = 0, .w = 1, .h = s->height};
1807 uint32_t *pixels;
1808 int pitch;
1809 ✗ for (ch = 0; ch < nb_display_channels; ch++) {
1810 ✗ data_in[ch] = s->real_data + 2 * nb_freq * ch;
1811 ✗ data[ch] = s->rdft_data + nb_freq * ch;
1812 ✗ i = i_start + ch;
1813 ✗ for (x = 0; x < 2 * nb_freq; x++) {
1814 ✗ double w = (x-nb_freq) * (1.0 / nb_freq);
1815 ✗ data_in[ch][x] = s->sample_array[i] * (1.0 - w * w);
1816 ✗ i += channels;
1817 ✗ if (i >= SAMPLE_ARRAY_SIZE)
1818 ✗ i -= SAMPLE_ARRAY_SIZE;
1819 }
1820 ✗ s->rdft_fn(s->rdft, data[ch], data_in[ch], sizeof(float));
1821 ✗ data[ch][0].im = data[ch][nb_freq].re;
1822 ✗ data[ch][nb_freq].re = 0;
1823 }
1824 /* Least efficient way to do this, we should of course
1825 * directly access it but it is more than fast enough. */
1826 ✗ if (!SDL_LockTexture(s->vis_texture, &rect, (void **)&pixels, &pitch)) {
1827 ✗ pitch >>= 2;
1828 ✗ pixels += pitch * s->height;
1829 ✗ for (y = 0; y < s->height; y++) {
1830 ✗ double w = 1 / sqrt(nb_freq);
1831 ✗ int a = sqrt(w * sqrt(data[0][y].re * data[0][y].re + data[0][y].im * data[0][y].im));
1832 ✗ int b = (nb_display_channels == 2 ) ? sqrt(w * hypot(data[1][y].re, data[1][y].im))
1833 : a;
1834 ✗ a = FFMIN(a, 255);
1835 ✗ b = FFMIN(b, 255);
1836 ✗ pixels -= pitch;
1837 ✗ *pixels = (a << 16) + (b << 8) + ((a+b) >> 1);
1838 }
1839 ✗ SDL_UnlockTexture(s->vis_texture);
1840 }
1841 ✗ SDL_RenderCopy(renderer, s->vis_texture, NULL, NULL);
1842 }
1843 ✗ if (!s->paused)
1844 ✗ s->xpos++;
1845 }
1846 }
1847
1848 ✗ static void uninit_bsf_graph(AVFormatContext *ic, int stream_index)
1849 {
1850 ✗ FormatContext *ici = ic->opaque;
1851 Stream *sti;
1852 StreamGroup *stgi;
1853
1854 ✗ if (stream_index >= ici->nb_streams)
1855 ✗ return;
1856
1857 ✗ sti = ici->streams[stream_index];
1858 ✗ sti->filter = NULL;
1859
1860 ✗ stgi = sti->group;
1861 ✗ if (stgi) {
1862 ✗ av_bsf_graph_free(&stgi->graph);
1863 ✗ stgi->sink = NULL;
1864
1865 ✗ for (int i = 0; i < stgi->stg->nb_streams; i++) {
1866 ✗ ici->streams[stgi->stg->streams[i]->index]->filter = NULL;
1867 ✗ ici->streams[stgi->stg->streams[i]->index]->group = NULL;
1868 }
1869 }
1870 ✗ sti->group = stgi;
1871 }
1872
1873 ✗ static void stream_component_close(VideoState *is, int stream_index)
1874 {
1875 ✗ AVFormatContext *ic = is->ic;
1876
1877 AVCodecParameters *codecpar;
1878
1879 ✗ if (stream_index < 0 || stream_index >= ic->nb_streams)
1880 ✗ return;
1881 ✗ codecpar = ic->streams[stream_index]->codecpar;
1882
1883 ✗ uninit_bsf_graph(ic, stream_index);
1884
1885 ✗ switch (codecpar->codec_type) {
1886 ✗ case AVMEDIA_TYPE_AUDIO:
1887 ✗ decoder_abort(&is->auddec, &is->sampq);
1888 ✗ SDL_CloseAudioDevice(audio_dev);
1889 ✗ decoder_destroy(&is->auddec);
1890 ✗ swr_free(&is->swr_ctx);
1891 ✗ av_freep(&is->audio_buf1);
1892 ✗ is->audio_buf1_size = 0;
1893 ✗ is->audio_buf = NULL;
1894
1895 ✗ if (is->rdft) {
1896 ✗ av_tx_uninit(&is->rdft);
1897 ✗ av_freep(&is->real_data);
1898 ✗ av_freep(&is->rdft_data);
1899 ✗ is->rdft = NULL;
1900 ✗ is->rdft_bits = 0;
1901 }
1902 ✗ break;
1903 ✗ case AVMEDIA_TYPE_VIDEO:
1904 ✗ decoder_abort(&is->viddec, &is->pictq);
1905 ✗ decoder_destroy(&is->viddec);
1906 ✗ break;
1907 ✗ case AVMEDIA_TYPE_SUBTITLE:
1908 ✗ decoder_abort(&is->subdec, &is->subpq);
1909 ✗ decoder_destroy(&is->subdec);
1910 ✗ break;
1911 ✗ default:
1912 ✗ break;
1913 }
1914
1915 ✗ ic->streams[stream_index]->discard = AVDISCARD_ALL;
1916 ✗ switch (codecpar->codec_type) {
1917 ✗ case AVMEDIA_TYPE_AUDIO:
1918 ✗ is->audio_st = NULL;
1919 ✗ is->audio_stream = -1;
1920 ✗ break;
1921 ✗ case AVMEDIA_TYPE_VIDEO:
1922 ✗ is->video_st = NULL;
1923 ✗ is->video_stream = -1;
1924 ✗ break;
1925 ✗ case AVMEDIA_TYPE_SUBTITLE:
1926 ✗ is->subtitle_st = NULL;
1927 ✗ is->subtitle_stream = -1;
1928 ✗ break;
1929 ✗ default:
1930 ✗ break;
1931 }
1932 }
1933
1934 ✗ static void stream_close(VideoState *is)
1935 {
1936 /* XXX: use a special url_shutdown call to abort parse cleanly */
1937 ✗ is->abort_request = 1;
1938 ✗ SDL_WaitThread(is->read_tid, NULL);
1939
1940 /* close each stream */
1941 ✗ if (is->audio_stream >= 0)
1942 ✗ stream_component_close(is, is->audio_stream);
1943 ✗ if (is->video_stream >= 0)
1944 ✗ stream_component_close(is, is->video_stream);
1945 ✗ if (is->subtitle_stream >= 0)
1946 ✗ stream_component_close(is, is->subtitle_stream);
1947
1948 ✗ if (is->ic) {
1949 ✗ FormatContext *ici = is->ic->opaque;
1950 ✗ for (int i = 0; i < ici->nb_streams; i++)
1951 ✗ av_freep(&ici->streams[i]);
1952 ✗ av_freep(&ici->streams);
1953 ✗ for (int i = 0; i < ici->nb_stream_groups; i++)
1954 ✗ av_freep(&ici->stream_groups[i]);
1955 ✗ av_freep(&ici->stream_groups);
1956 ✗ av_freep(&is->ic->opaque);
1957 }
1958
1959 ✗ avformat_close_input(&is->ic);
1960
1961 ✗ packet_queue_destroy(&is->videoq);
1962 ✗ packet_queue_destroy(&is->audioq);
1963 ✗ packet_queue_destroy(&is->subtitleq);
1964
1965 /* free all pictures */
1966 ✗ frame_queue_destroy(&is->pictq);
1967 ✗ frame_queue_destroy(&is->sampq);
1968 ✗ frame_queue_destroy(&is->subpq);
1969 ✗ SDL_DestroyCond(is->continue_read_thread);
1970 ✗ sws_freeContext(is->sub_convert_ctx);
1971 ✗ av_free(is->filename);
1972 ✗ if (is->vis_texture)
1973 ✗ SDL_DestroyTexture(is->vis_texture);
1974 ✗ if (is->vid_texture)
1975 ✗ SDL_DestroyTexture(is->vid_texture);
1976 ✗ if (is->sub_texture)
1977 ✗ SDL_DestroyTexture(is->sub_texture);
1978 ✗ if (is->chapter_texture)
1979 ✗ SDL_DestroyTexture(is->chapter_texture);
1980 ✗ avfilter_graph_free(&is->chapter_graph);
1981 ✗ av_freep(&is->chapter_rows);
1982 ✗ av_free(is);
1983 ✗ }
1984
1985 ✗ static void do_exit(VideoState *is)
1986 {
1987 ✗ if (is) {
1988 ✗ stream_close(is);
1989 }
1990 ✗ if (renderer)
1991 ✗ SDL_DestroyRenderer(renderer);
1992 ✗ if (vk_renderer)
1993 ✗ vk_renderer_destroy(vk_renderer);
1994 ✗ if (window)
1995 ✗ SDL_DestroyWindow(window);
1996 ✗ uninit_opts();
1997 ✗ for (int i = 0; i < nb_vfilters; i++)
1998 ✗ av_freep(&vfilters_list[i]);
1999 ✗ av_freep(&vfilters_list);
2000 ✗ av_freep(&video_codec_name);
2001 ✗ av_freep(&audio_codec_name);
2002 ✗ av_freep(&subtitle_codec_name);
2003 ✗ av_freep(&input_filename);
2004 ✗ avformat_network_deinit();
2005 ✗ if (show_status)
2006 ✗ printf("\n");
2007 ✗ SDL_Quit();
2008 ✗ av_log(NULL, AV_LOG_QUIET, "%s", "");
2009 ✗ exit(exit_status);
2010 }
2011
2012 ✗ static void sigterm_handler(int sig)
2013 {
2014 ✗ received_sigterm = sig;
2015 ✗ if (++received_nb_signals > 3)
2016 ✗ exit(123);
2017 ✗ }
2018
2019 ✗ static void set_default_window_size(int width, int height, AVRational sar)
2020 {
2021 SDL_Rect rect;
2022 ✗ int max_width = screen_width ? screen_width : INT_MAX;
2023 ✗ int max_height = screen_height ? screen_height : INT_MAX;
2024 ✗ if (max_width == INT_MAX && max_height == INT_MAX)
2025 ✗ max_height = height;
2026 ✗ calculate_display_rect(&rect, 0, 0, max_width, max_height, width, height, sar);
2027 ✗ default_width = rect.w;
2028 ✗ default_height = rect.h;
2029 ✗ }
2030
2031 ✗ static int video_open(VideoState *is)
2032 {
2033 int w,h;
2034
2035 ✗ w = screen_width ? screen_width : default_width;
2036 ✗ h = screen_height ? screen_height : default_height;
2037
2038 ✗ if (!window_title)
2039 ✗ window_title = input_filename;
2040 ✗ SDL_SetWindowTitle(window, window_title);
2041
2042 ✗ SDL_SetWindowSize(window, w, h);
2043 ✗ SDL_SetWindowPosition(window, screen_left, screen_top);
2044 ✗ if (is_full_screen)
2045 ✗ SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
2046 ✗ SDL_ShowWindow(window);
2047
2048 ✗ is->width = w;
2049 ✗ is->height = h;
2050
2051 ✗ return 0;
2052 }
2053
2054 /* display the current picture, if any */
2055 ✗ static void video_display(VideoState *is)
2056 {
2057 ✗ if (!is->width)
2058 ✗ video_open(is);
2059
2060 ✗ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
2061 ✗ SDL_RenderClear(renderer);
2062 ✗ if (is->audio_st && is->show_mode != SHOW_MODE_VIDEO)
2063 ✗ video_audio_display(is);
2064 ✗ else if (is->video_st)
2065 ✗ video_image_display(is);
2066 ✗ chapter_list_draw(is);
2067 ✗ SDL_RenderPresent(renderer);
2068 ✗ }
2069
2070 ✗ static double get_clock(Clock *c)
2071 {
2072 ✗ if (*c->queue_serial != c->serial)
2073 ✗ return NAN;
2074 ✗ if (c->paused) {
2075 ✗ return c->pts;
2076 } else {
2077 ✗ double time = av_gettime_relative() / 1000000.0;
2078 ✗ return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
2079 }
2080 }
2081
2082 ✗ static void set_clock_at(Clock *c, double pts, int serial, double time)
2083 {
2084 ✗ c->pts = pts;
2085 ✗ c->last_updated = time;
2086 ✗ c->pts_drift = c->pts - time;
2087 ✗ c->serial = serial;
2088 ✗ }
2089
2090 ✗ static void set_clock(Clock *c, double pts, int serial)
2091 {
2092 ✗ double time = av_gettime_relative() / 1000000.0;
2093 ✗ set_clock_at(c, pts, serial, time);
2094 ✗ }
2095
2096 ✗ static void set_clock_speed(Clock *c, double speed)
2097 {
2098 ✗ set_clock(c, get_clock(c), c->serial);
2099 ✗ c->speed = speed;
2100 ✗ }
2101
2102 ✗ static void init_clock(Clock *c, int *queue_serial)
2103 {
2104 ✗ c->speed = 1.0;
2105 ✗ c->paused = 0;
2106 ✗ c->queue_serial = queue_serial;
2107 ✗ set_clock(c, NAN, -1);
2108 ✗ }
2109
2110 ✗ static void sync_clock_to_slave(Clock *c, Clock *slave)
2111 {
2112 ✗ double clock = get_clock(c);
2113 ✗ double slave_clock = get_clock(slave);
2114 ✗ if (!isnan(slave_clock) && (isnan(clock) || fabs(clock - slave_clock) > AV_NOSYNC_THRESHOLD))
2115 ✗ set_clock(c, slave_clock, slave->serial);
2116 ✗ }
2117
2118 ✗ static int get_master_sync_type(VideoState *is) {
2119 ✗ if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
2120 ✗ if (is->video_st)
2121 ✗ return AV_SYNC_VIDEO_MASTER;
2122 else
2123 ✗ return AV_SYNC_AUDIO_MASTER;
2124 ✗ } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
2125 ✗ if (is->audio_st)
2126 ✗ return AV_SYNC_AUDIO_MASTER;
2127 else
2128 ✗ return AV_SYNC_EXTERNAL_CLOCK;
2129 } else {
2130 ✗ return AV_SYNC_EXTERNAL_CLOCK;
2131 }
2132 }
2133
2134 /* get the current master clock value */
2135 ✗ static double get_master_clock(VideoState *is)
2136 {
2137 double val;
2138
2139 ✗ switch (get_master_sync_type(is)) {
2140 ✗ case AV_SYNC_VIDEO_MASTER:
2141 ✗ val = get_clock(&is->vidclk);
2142 ✗ break;
2143 ✗ case AV_SYNC_AUDIO_MASTER:
2144 ✗ val = get_clock(&is->audclk);
2145 ✗ break;
2146 ✗ default:
2147 ✗ val = get_clock(&is->extclk);
2148 ✗ break;
2149 }
2150 ✗ return val;
2151 }
2152
2153 ✗ static void check_external_clock_speed(VideoState *is) {
2154 ✗ if (is->video_stream >= 0 && is->videoq.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES ||
2155 ✗ is->audio_stream >= 0 && is->audioq.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES) {
2156 ✗ set_clock_speed(&is->extclk, FFMAX(EXTERNAL_CLOCK_SPEED_MIN, is->extclk.speed - EXTERNAL_CLOCK_SPEED_STEP));
2157 ✗ } else if ((is->video_stream < 0 || is->videoq.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES) &&
2158 ✗ (is->audio_stream < 0 || is->audioq.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES)) {
2159 ✗ set_clock_speed(&is->extclk, FFMIN(EXTERNAL_CLOCK_SPEED_MAX, is->extclk.speed + EXTERNAL_CLOCK_SPEED_STEP));
2160 } else {
2161 ✗ double speed = is->extclk.speed;
2162 ✗ if (speed != 1.0)
2163 ✗ set_clock_speed(&is->extclk, speed + EXTERNAL_CLOCK_SPEED_STEP * (1.0 - speed) / fabs(1.0 - speed));
2164 }
2165 ✗ }
2166
2167 /* seek in the stream */
2168 ✗ static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int by_bytes)
2169 {
2170 ✗ if (!is->seek_req) {
2171 ✗ is->seek_pos = pos;
2172 ✗ is->seek_rel = rel;
2173 ✗ is->seek_flags &= ~AVSEEK_FLAG_BYTE;
2174 ✗ if (by_bytes)
2175 ✗ is->seek_flags |= AVSEEK_FLAG_BYTE;
2176 ✗ is->seek_req = 1;
2177 ✗ SDL_CondSignal(is->continue_read_thread);
2178 }
2179 ✗ }
2180
2181 /* pause or resume the video */
2182 ✗ static void stream_toggle_pause(VideoState *is)
2183 {
2184 ✗ if (is->paused) {
2185 ✗ is->frame_timer += av_gettime_relative() / 1000000.0 - is->vidclk.last_updated;
2186 ✗ if (is->read_pause_return != AVERROR(ENOSYS)) {
2187 ✗ is->vidclk.paused = 0;
2188 }
2189 ✗ set_clock(&is->vidclk, get_clock(&is->vidclk), is->vidclk.serial);
2190 }
2191 ✗ set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
2192 ✗ is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused = !is->paused;
2193 ✗ }
2194
2195 ✗ static void toggle_pause(VideoState *is)
2196 {
2197 ✗ stream_toggle_pause(is);
2198 ✗ is->step = 0;
2199 ✗ }
2200
2201 ✗ static void toggle_mute(VideoState *is)
2202 {
2203 ✗ is->muted = !is->muted;
2204 ✗ }
2205
2206 ✗ static void update_volume(VideoState *is, int sign, double step)
2207 {
2208 ✗ double volume_level = is->audio_volume ? (20 * log(is->audio_volume / (double)SDL_MIX_MAXVOLUME) / log(10)) : -1000.0;
2209 ✗ int new_volume = lrint(SDL_MIX_MAXVOLUME * pow(10.0, (volume_level + sign * step) / 20.0));
2210 ✗ is->audio_volume = av_clip(is->audio_volume == new_volume ? (is->audio_volume + sign) : new_volume, 0, SDL_MIX_MAXVOLUME);
2211 ✗ }
2212
2213 ✗ static void step_to_next_frame(VideoState *is)
2214 {
2215 /* if the stream is paused unpause it, then step */
2216 ✗ if (is->paused)
2217 ✗ stream_toggle_pause(is);
2218 ✗ is->step = 1;
2219 ✗ }
2220
2221 ✗ static double compute_target_delay(double delay, VideoState *is)
2222 {
2223 ✗ double sync_threshold, diff = 0;
2224
2225 /* update delay to follow master synchronisation source */
2226 ✗ if (get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER) {
2227 /* if video is slave, we try to correct big delays by
2228 duplicating or deleting a frame */
2229 ✗ diff = get_clock(&is->vidclk) - get_master_clock(is);
2230
2231 /* skip or repeat frame. We take into account the
2232 delay to compute the threshold. I still don't know
2233 if it is the best guess */
2234 ✗ sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
2235 ✗ if (!isnan(diff) && fabs(diff) < is->max_frame_duration) {
2236 ✗ if (diff <= -sync_threshold)
2237 ✗ delay = FFMAX(0, delay + diff);
2238 ✗ else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD)
2239 ✗ delay = delay + diff;
2240 ✗ else if (diff >= sync_threshold)
2241 ✗ delay = 2 * delay;
2242 }
2243 }
2244
2245 ✗ av_log(NULL, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n",
2246 delay, -diff);
2247
2248 ✗ return delay;
2249 }
2250
2251 ✗ static double vp_duration(VideoState *is, Frame *vp, Frame *nextvp) {
2252 ✗ if (vp->serial == nextvp->serial) {
2253 ✗ double duration = nextvp->pts - vp->pts;
2254 ✗ if (isnan(duration) || duration <= 0 || duration > is->max_frame_duration)
2255 ✗ return vp->duration;
2256 else
2257 ✗ return duration;
2258 } else {
2259 ✗ return 0.0;
2260 }
2261 }
2262
2263 ✗ static void update_video_pts(VideoState *is, double pts, int serial)
2264 {
2265 /* update current video pts */
2266 ✗ set_clock(&is->vidclk, pts, serial);
2267 ✗ sync_clock_to_slave(&is->extclk, &is->vidclk);
2268 ✗ }
2269
2270 /* called to display each frame */
2271 ✗ static void video_refresh(void *opaque, double *remaining_time)
2272 {
2273 ✗ VideoState *is = opaque;
2274 double time;
2275
2276 Frame *sp, *sp2;
2277
2278 ✗ if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)
2279 ✗ check_external_clock_speed(is);
2280
2281 ✗ if (!display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st) {
2282 ✗ time = av_gettime_relative() / 1000000.0;
2283 ✗ if (is->force_refresh || is->last_vis_time + rdftspeed < time) {
2284 ✗ video_display(is);
2285 ✗ is->last_vis_time = time;
2286 }
2287 ✗ *remaining_time = FFMIN(*remaining_time, is->last_vis_time + rdftspeed - time);
2288 }
2289
2290 ✗ if (is->video_st) {
2291 ✗ retry:
2292 ✗ if (frame_queue_nb_remaining(&is->pictq) == 0) {
2293 // nothing to do, no picture to display in the queue
2294 } else {
2295 double last_duration, duration, delay;
2296 Frame *vp, *lastvp;
2297
2298 /* dequeue the picture */
2299 ✗ lastvp = frame_queue_peek_last(&is->pictq);
2300 ✗ vp = frame_queue_peek(&is->pictq);
2301
2302 ✗ if (vp->serial != is->videoq.serial) {
2303 ✗ frame_queue_next(&is->pictq);
2304 ✗ goto retry;
2305 }
2306
2307 ✗ if (lastvp->serial != vp->serial)
2308 ✗ is->frame_timer = av_gettime_relative() / 1000000.0;
2309
2310 ✗ if (is->paused)
2311 ✗ goto display;
2312
2313 /* compute nominal last_duration */
2314 ✗ last_duration = vp_duration(is, lastvp, vp);
2315 ✗ delay = compute_target_delay(last_duration, is);
2316
2317 ✗ time= av_gettime_relative()/1000000.0;
2318 ✗ if (time < is->frame_timer + delay) {
2319 ✗ *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
2320 ✗ goto display;
2321 }
2322
2323 ✗ is->frame_timer += delay;
2324 ✗ if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)
2325 ✗ is->frame_timer = time;
2326
2327 ✗ SDL_LockMutex(is->pictq.mutex);
2328 ✗ if (!isnan(vp->pts))
2329 ✗ update_video_pts(is, vp->pts, vp->serial);
2330 ✗ SDL_UnlockMutex(is->pictq.mutex);
2331
2332 ✗ if (frame_queue_nb_remaining(&is->pictq) > 1) {
2333 ✗ Frame *nextvp = frame_queue_peek_next(&is->pictq);
2334 ✗ duration = vp_duration(is, vp, nextvp);
2335 ✗ if(!is->step && (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) && time > is->frame_timer + duration){
2336 ✗ is->frame_drops_late++;
2337 ✗ frame_queue_next(&is->pictq);
2338 ✗ goto retry;
2339 }
2340 }
2341
2342 ✗ if (is->subtitle_st) {
2343 ✗ while (frame_queue_nb_remaining(&is->subpq) > 0) {
2344 ✗ sp = frame_queue_peek(&is->subpq);
2345
2346 ✗ if (frame_queue_nb_remaining(&is->subpq) > 1)
2347 ✗ sp2 = frame_queue_peek_next(&is->subpq);
2348 else
2349 ✗ sp2 = NULL;
2350
2351 ✗ if (sp->serial != is->subtitleq.serial
2352 ✗ || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
2353 ✗ || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
2354 {
2355 ✗ if (sp->uploaded) {
2356 int i;
2357 ✗ for (i = 0; i < sp->sub.num_rects; i++) {
2358 ✗ AVSubtitleRect *sub_rect = sp->sub.rects[i];
2359 uint8_t *pixels;
2360 int pitch, j;
2361
2362 ✗ if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
2363 ✗ for (j = 0; j < sub_rect->h; j++, pixels += pitch)
2364 ✗ memset(pixels, 0, sub_rect->w << 2);
2365 ✗ SDL_UnlockTexture(is->sub_texture);
2366 }
2367 }
2368 }
2369 ✗ frame_queue_next(&is->subpq);
2370 } else {
2371 break;
2372 }
2373 }
2374 }
2375
2376 ✗ frame_queue_next(&is->pictq);
2377 ✗ is->force_refresh = 1;
2378
2379 ✗ if (is->step && !is->paused)
2380 ✗ stream_toggle_pause(is);
2381 }
2382 ✗ display:
2383 /* display picture */
2384 ✗ if (!display_disable && is->force_refresh && is->show_mode == SHOW_MODE_VIDEO && is->pictq.rindex_shown)
2385 ✗ video_display(is);
2386 }
2387 ✗ is->force_refresh = 0;
2388 ✗ if (show_status) {
2389 AVBPrint buf;
2390 static int64_t last_time;
2391 int64_t cur_time;
2392 int aqsize, vqsize, sqsize;
2393 double av_diff;
2394
2395 ✗ cur_time = av_gettime_relative();
2396 ✗ if (!last_time || (cur_time - last_time) >= 30000) {
2397 ✗ aqsize = 0;
2398 ✗ vqsize = 0;
2399 ✗ sqsize = 0;
2400 ✗ if (is->audio_st)
2401 ✗ aqsize = is->audioq.size;
2402 ✗ if (is->video_st)
2403 ✗ vqsize = is->videoq.size;
2404 ✗ if (is->subtitle_st)
2405 ✗ sqsize = is->subtitleq.size;
2406 ✗ av_diff = 0;
2407 ✗ if (is->audio_st && is->video_st)
2408 ✗ av_diff = get_clock(&is->audclk) - get_clock(&is->vidclk);
2409 ✗ else if (is->video_st)
2410 ✗ av_diff = get_master_clock(is) - get_clock(&is->vidclk);
2411 ✗ else if (is->audio_st)
2412 ✗ av_diff = get_master_clock(is) - get_clock(&is->audclk);
2413
2414 ✗ av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
2415 ✗ av_bprintf(&buf,
2416 "%7.2f %s:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB \r",
2417 get_master_clock(is),
2418 ✗ (is->audio_st && is->video_st) ? "A-V" : (is->video_st ? "M-V" : (is->audio_st ? "M-A" : " ")),
2419 av_diff,
2420 ✗ is->frame_drops_early + is->frame_drops_late,
2421 aqsize / 1024,
2422 vqsize / 1024,
2423 sqsize);
2424
2425 ✗ if (show_status == 1 && AV_LOG_INFO > av_log_get_level())
2426 ✗ fprintf(stderr, "%s", buf.str);
2427 else
2428 ✗ av_log(NULL, AV_LOG_INFO, "%s", buf.str);
2429
2430 ✗ fflush(stderr);
2431 ✗ av_bprint_finalize(&buf, NULL);
2432
2433 ✗ last_time = cur_time;
2434 }
2435 }
2436 ✗ }
2437
2438 ✗ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, double duration, int64_t pos, int serial)
2439 {
2440 Frame *vp;
2441
2442 #if defined(DEBUG_SYNC)
2443 printf("frame_type=%c pts=%0.3f\n",
2444 av_get_picture_type_char(src_frame->pict_type), pts);
2445 #endif
2446
2447 ✗ if (!(vp = frame_queue_peek_writable(&is->pictq)))
2448 ✗ return -1;
2449
2450 ✗ vp->sar = src_frame->sample_aspect_ratio;
2451 ✗ vp->uploaded = 0;
2452
2453 ✗ vp->width = src_frame->width;
2454 ✗ vp->height = src_frame->height;
2455 ✗ vp->format = src_frame->format;
2456
2457 ✗ vp->pts = pts;
2458 ✗ vp->duration = duration;
2459 ✗ vp->pos = pos;
2460 ✗ vp->serial = serial;
2461
2462 ✗ set_default_window_size(vp->width, vp->height, vp->sar);
2463
2464 ✗ av_frame_move_ref(vp->frame, src_frame);
2465 ✗ frame_queue_push(&is->pictq);
2466 ✗ return 0;
2467 }
2468
2469 ✗ static int get_video_frame(VideoState *is, AVFrame *frame)
2470 {
2471 int got_picture;
2472
2473 ✗ if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
2474 ✗ return -1;
2475
2476 ✗ if (got_picture) {
2477 ✗ double dpts = NAN;
2478
2479 ✗ if (frame->pts != AV_NOPTS_VALUE)
2480 ✗ dpts = av_q2d(is->video_st->time_base) * frame->pts;
2481
2482 ✗ frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, frame);
2483
2484 ✗ if (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) {
2485 ✗ if (frame->pts != AV_NOPTS_VALUE) {
2486 ✗ double diff = dpts - get_master_clock(is);
2487 ✗ if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD &&
2488 ✗ diff - is->frame_last_filter_delay < 0 &&
2489 ✗ is->viddec.pkt_serial == is->vidclk.serial &&
2490 ✗ is->videoq.nb_packets) {
2491 ✗ is->frame_drops_early++;
2492 ✗ av_frame_unref(frame);
2493 ✗ got_picture = 0;
2494 }
2495 }
2496 }
2497 }
2498
2499 ✗ return got_picture;
2500 }
2501
2502 ✗ static int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph,
2503 AVFilterContext *source_ctx, AVFilterContext *sink_ctx)
2504 {
2505 int ret, i;
2506 ✗ int nb_filters = graph->nb_filters;
2507 ✗ AVFilterInOut *outputs = NULL, *inputs = NULL;
2508
2509 ✗ if (filtergraph) {
2510 ✗ outputs = avfilter_inout_alloc();
2511 ✗ inputs = avfilter_inout_alloc();
2512 ✗ if (!outputs || !inputs) {
2513 ✗ ret = AVERROR(ENOMEM);
2514 ✗ goto fail;
2515 }
2516
2517 ✗ outputs->name = av_strdup("in");
2518 ✗ outputs->filter_ctx = source_ctx;
2519 ✗ outputs->pad_idx = 0;
2520 ✗ outputs->next = NULL;
2521
2522 ✗ inputs->name = av_strdup("out");
2523 ✗ inputs->filter_ctx = sink_ctx;
2524 ✗ inputs->pad_idx = 0;
2525 ✗ inputs->next = NULL;
2526
2527 ✗ if ((ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs, NULL)) < 0)
2528 ✗ goto fail;
2529 } else {
2530 ✗ if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) < 0)
2531 ✗ goto fail;
2532 }
2533
2534 /* Reorder the filters to ensure that inputs of the custom filters are merged first */
2535 ✗ for (i = 0; i < graph->nb_filters - nb_filters; i++)
2536 ✗ FFSWAP(AVFilterContext*, graph->filters[i], graph->filters[i + nb_filters]);
2537
2538 ✗ ret = avfilter_graph_config(graph, NULL);
2539 ✗ fail:
2540 ✗ avfilter_inout_free(&outputs);
2541 ✗ avfilter_inout_free(&inputs);
2542 ✗ return ret;
2543 }
2544
2545 ✗ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters, AVFrame *frame)
2546 {
2547 enum AVPixelFormat pix_fmts[FF_ARRAY_ELEMS(sdl_texture_format_map)];
2548 ✗ char sws_flags_str[512] = "";
2549 int ret;
2550 ✗ AVFilterContext *filt_src = NULL, *filt_out = NULL, *last_filter = NULL;
2551 ✗ AVCodecParameters *codecpar = is->video_st->codecpar;
2552 ✗ AVRational fr = av_guess_frame_rate(is->ic, is->video_st, NULL);
2553 ✗ const AVDictionaryEntry *e = NULL;
2554 ✗ int nb_pix_fmts = 0;
2555 int i, j;
2556 ✗ AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
2557
2558 ✗ if (!par)
2559 ✗ return AVERROR(ENOMEM);
2560
2561 ✗ for (i = 0; i < renderer_info.num_texture_formats; i++) {
2562 ✗ for (j = 0; j < FF_ARRAY_ELEMS(sdl_texture_format_map); j++) {
2563 ✗ if (renderer_info.texture_formats[i] == sdl_texture_format_map[j].texture_fmt) {
2564 ✗ pix_fmts[nb_pix_fmts++] = sdl_texture_format_map[j].format;
2565 ✗ break;
2566 }
2567 }
2568 }
2569
2570 ✗ while ((e = av_dict_iterate(sws_dict, e))) {
2571 ✗ if (!strcmp(e->key, "sws_flags")) {
2572 ✗ av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:", "flags", e->value);
2573 } else
2574 ✗ av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:", e->key, e->value);
2575 }
2576 ✗ if (strlen(sws_flags_str))
2577 ✗ sws_flags_str[strlen(sws_flags_str)-1] = '\0';
2578
2579 ✗ graph->scale_sws_opts = av_strdup(sws_flags_str);
2580
2581
2582 ✗ filt_src = avfilter_graph_alloc_filter(graph, avfilter_get_by_name("buffer"),
2583 "ffplay_buffer");
2584 ✗ if (!filt_src) {
2585 ✗ ret = AVERROR(ENOMEM);
2586 ✗ goto fail;
2587 }
2588
2589 ✗ par->format = frame->format;
2590 ✗ par->time_base = is->video_st->time_base;
2591 ✗ par->width = frame->width;
2592 ✗ par->height = frame->height;
2593 ✗ par->sample_aspect_ratio = codecpar->sample_aspect_ratio;
2594 ✗ par->color_space = frame->colorspace;
2595 ✗ par->color_range = frame->color_range;
2596 ✗ par->alpha_mode = frame->alpha_mode;
2597 ✗ par->frame_rate = fr;
2598 ✗ par->hw_frames_ctx = frame->hw_frames_ctx;
2599 ✗ ret = av_buffersrc_parameters_set(filt_src, par);
2600 ✗ if (ret < 0)
2601 ✗ goto fail;
2602
2603 ✗ ret = avfilter_init_dict(filt_src, NULL);
2604 ✗ if (ret < 0)
2605 ✗ goto fail;
2606
2607 ✗ filt_out = avfilter_graph_alloc_filter(graph, avfilter_get_by_name("buffersink"),
2608 "ffplay_buffersink");
2609 ✗ if (!filt_out) {
2610 ✗ ret = AVERROR(ENOMEM);
2611 ✗ goto fail;
2612 }
2613
2614 ✗ if ((ret = av_opt_set_array(filt_out, "pixel_formats", AV_OPT_SEARCH_CHILDREN,
2615 0, nb_pix_fmts, AV_OPT_TYPE_PIXEL_FMT, pix_fmts)) < 0)
2616 ✗ goto fail;
2617 ✗ if (!vk_renderer &&
2618 ✗ (ret = av_opt_set_array(filt_out, "colorspaces", AV_OPT_SEARCH_CHILDREN,
2619 0, FF_ARRAY_ELEMS(sdl_supported_color_spaces),
2620 AV_OPT_TYPE_INT, sdl_supported_color_spaces)) < 0)
2621 ✗ goto fail;
2622
2623 ✗ if ((ret = av_opt_set_array(filt_out, "alphamodes", AV_OPT_SEARCH_CHILDREN,
2624 0, FF_ARRAY_ELEMS(sdl_supported_alpha_modes),
2625 AV_OPT_TYPE_INT, sdl_supported_alpha_modes)) < 0)
2626 ✗ goto fail;
2627
2628 ✗ ret = avfilter_init_dict(filt_out, NULL);
2629 ✗ if (ret < 0)
2630 ✗ goto fail;
2631
2632 ✗ last_filter = filt_out;
2633
2634 /* Note: this macro adds a filter before the lastly added filter, so the
2635 * processing order of the filters is in reverse */
2636 #define INSERT_FILT(name, arg) do { \
2637 AVFilterContext *filt_ctx; \
2638 \
2639 ret = avfilter_graph_create_filter(&filt_ctx, \
2640 avfilter_get_by_name(name), \
2641 "ffplay_" name, arg, NULL, graph); \
2642 if (ret < 0) \
2643 goto fail; \
2644 \
2645 ret = avfilter_link(filt_ctx, 0, last_filter, 0); \
2646 if (ret < 0) \
2647 goto fail; \
2648 \
2649 last_filter = filt_ctx; \
2650 } while (0)
2651
2652 ✗ if (autorotate) {
2653 ✗ double theta = 0.0;
2654 ✗ int32_t *displaymatrix = NULL;
2655 ✗ AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX);
2656 ✗ if (sd)
2657 ✗ displaymatrix = (int32_t *)sd->data;
2658 ✗ if (!displaymatrix) {
2659 ✗ const AVPacketSideData *psd = av_packet_side_data_get(is->video_st->codecpar->coded_side_data,
2660 ✗ is->video_st->codecpar->nb_coded_side_data,
2661 AV_PKT_DATA_DISPLAYMATRIX);
2662 ✗ if (psd)
2663 ✗ displaymatrix = (int32_t *)psd->data;
2664 }
2665 ✗ theta = get_rotation(displaymatrix);
2666
2667 ✗ if (fabs(theta - 90) < 1.0) {
2668 ✗ INSERT_FILT("transpose", displaymatrix[3] > 0 ? "cclock_flip" : "clock");
2669 ✗ } else if (fabs(theta - 180) < 1.0) {
2670 ✗ if (displaymatrix[0] < 0)
2671 ✗ INSERT_FILT("hflip", NULL);
2672 ✗ if (displaymatrix[4] < 0)
2673 ✗ INSERT_FILT("vflip", NULL);
2674 ✗ } else if (fabs(theta - 270) < 1.0) {
2675 ✗ INSERT_FILT("transpose", displaymatrix[3] < 0 ? "clock_flip" : "cclock");
2676 ✗ } else if (fabs(theta) > 1.0) {
2677 char rotate_buf[64];
2678 ✗ snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);
2679 ✗ INSERT_FILT("rotate", rotate_buf);
2680 } else {
2681 ✗ if (displaymatrix && displaymatrix[4] < 0)
2682 ✗ INSERT_FILT("vflip", NULL);
2683 }
2684 }
2685
2686 ✗ if ((ret = configure_filtergraph(graph, vfilters, filt_src, last_filter)) < 0)
2687 ✗ goto fail;
2688
2689 ✗ is->in_video_filter = filt_src;
2690 ✗ is->out_video_filter = filt_out;
2691
2692 ✗ fail:
2693 ✗ av_freep(&par);
2694 ✗ return ret;
2695 }
2696
2697 ✗ static int configure_audio_filters(VideoState *is, const char *afilters, int force_output_format)
2698 {
2699 ✗ AVFilterContext *filt_asrc = NULL, *filt_asink = NULL;
2700 ✗ char aresample_swr_opts[512] = "";
2701 ✗ const AVDictionaryEntry *e = NULL;
2702 AVBPrint bp;
2703 char asrc_args[256];
2704 int ret;
2705
2706 ✗ avfilter_graph_free(&is->agraph);
2707 ✗ if (!(is->agraph = avfilter_graph_alloc()))
2708 ✗ return AVERROR(ENOMEM);
2709 ✗ is->agraph->nb_threads = filter_nbthreads;
2710
2711 ✗ av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
2712
2713 ✗ while ((e = av_dict_iterate(swr_opts, e)))
2714 ✗ av_strlcatf(aresample_swr_opts, sizeof(aresample_swr_opts), "%s=%s:", e->key, e->value);
2715 ✗ if (strlen(aresample_swr_opts))
2716 ✗ aresample_swr_opts[strlen(aresample_swr_opts)-1] = '\0';
2717 ✗ av_opt_set(is->agraph, "aresample_swr_opts", aresample_swr_opts, 0);
2718
2719 ✗ av_channel_layout_describe_bprint(&is->audio_filter_src.ch_layout, &bp);
2720
2721 ✗ ret = snprintf(asrc_args, sizeof(asrc_args),
2722 "sample_rate=%d:sample_fmt=%s:time_base=%d/%d:channel_layout=%s",
2723 is->audio_filter_src.freq, av_get_sample_fmt_name(is->audio_filter_src.fmt),
2724 1, is->audio_filter_src.freq, bp.str);
2725
2726 ✗ ret = avfilter_graph_create_filter(&filt_asrc,
2727 avfilter_get_by_name("abuffer"), "ffplay_abuffer",
2728 asrc_args, NULL, is->agraph);
2729 ✗ if (ret < 0)
2730 ✗ goto end;
2731
2732 ✗ filt_asink = avfilter_graph_alloc_filter(is->agraph, avfilter_get_by_name("abuffersink"),
2733 "ffplay_abuffersink");
2734 ✗ if (!filt_asink) {
2735 ✗ ret = AVERROR(ENOMEM);
2736 ✗ goto end;
2737 }
2738
2739 ✗ if ((ret = av_opt_set(filt_asink, "sample_formats", "s16", AV_OPT_SEARCH_CHILDREN)) < 0)
2740 ✗ goto end;
2741
2742 ✗ if (force_output_format) {
2743 ✗ if ((ret = av_opt_set_array(filt_asink, "channel_layouts", AV_OPT_SEARCH_CHILDREN,
2744 ✗ 0, 1, AV_OPT_TYPE_CHLAYOUT, &is->audio_tgt.ch_layout)) < 0)
2745 ✗ goto end;
2746 ✗ if ((ret = av_opt_set_array(filt_asink, "samplerates", AV_OPT_SEARCH_CHILDREN,
2747 ✗ 0, 1, AV_OPT_TYPE_INT, &is->audio_tgt.freq)) < 0)
2748 ✗ goto end;
2749 }
2750
2751 ✗ ret = avfilter_init_dict(filt_asink, NULL);
2752 ✗ if (ret < 0)
2753 ✗ goto end;
2754
2755 ✗ if ((ret = configure_filtergraph(is->agraph, afilters, filt_asrc, filt_asink)) < 0)
2756 ✗ goto end;
2757
2758 ✗ is->in_audio_filter = filt_asrc;
2759 ✗ is->out_audio_filter = filt_asink;
2760
2761 ✗ end:
2762 ✗ if (ret < 0)
2763 ✗ avfilter_graph_free(&is->agraph);
2764 ✗ av_bprint_finalize(&bp, NULL);
2765
2766 ✗ return ret;
2767 }
2768
2769 ✗ static int audio_thread(void *arg)
2770 {
2771 ✗ VideoState *is = arg;
2772 ✗ AVFrame *frame = av_frame_alloc();
2773 Frame *af;
2774 ✗ int last_serial = -1;
2775 int reconfigure;
2776 ✗ int got_frame = 0;
2777 AVRational tb;
2778 ✗ int ret = 0;
2779
2780 ✗ if (!frame)
2781 ✗ return AVERROR(ENOMEM);
2782
2783 do {
2784 ✗ if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0)
2785 ✗ goto the_end;
2786
2787 ✗ if (got_frame) {
2788 ✗ tb = (AVRational){1, frame->sample_rate};
2789
2790 ✗ reconfigure =
2791 ✗ cmp_audio_fmts(is->audio_filter_src.fmt, is->audio_filter_src.ch_layout.nb_channels,
2792 ✗ frame->format, frame->ch_layout.nb_channels) ||
2793 ✗ av_channel_layout_compare(&is->audio_filter_src.ch_layout, &frame->ch_layout) ||
2794 ✗ is->audio_filter_src.freq != frame->sample_rate ||
2795 ✗ is->auddec.pkt_serial != last_serial;
2796
2797 ✗ if (reconfigure) {
2798 char buf1[1024], buf2[1024];
2799 ✗ av_channel_layout_describe(&is->audio_filter_src.ch_layout, buf1, sizeof(buf1));
2800 ✗ av_channel_layout_describe(&frame->ch_layout, buf2, sizeof(buf2));
2801 ✗ av_log(NULL, AV_LOG_DEBUG,
2802 "Audio frame changed from rate:%d ch:%d fmt:%s layout:%s serial:%d to rate:%d ch:%d fmt:%s layout:%s serial:%d\n",
2803 is->audio_filter_src.freq, is->audio_filter_src.ch_layout.nb_channels, av_get_sample_fmt_name(is->audio_filter_src.fmt), buf1, last_serial,
2804 ✗ frame->sample_rate, frame->ch_layout.nb_channels, av_get_sample_fmt_name(frame->format), buf2, is->auddec.pkt_serial);
2805
2806 ✗ is->audio_filter_src.fmt = frame->format;
2807 ✗ ret = av_channel_layout_copy(&is->audio_filter_src.ch_layout, &frame->ch_layout);
2808 ✗ if (ret < 0)
2809 ✗ goto the_end;
2810 ✗ is->audio_filter_src.freq = frame->sample_rate;
2811 ✗ last_serial = is->auddec.pkt_serial;
2812
2813 ✗ if ((ret = configure_audio_filters(is, afilters, 1)) < 0)
2814 ✗ goto the_end;
2815 }
2816
2817 ✗ if ((ret = av_buffersrc_add_frame(is->in_audio_filter, frame)) < 0)
2818 ✗ goto the_end;
2819
2820 ✗ while ((ret = av_buffersink_get_frame_flags(is->out_audio_filter, frame, 0)) >= 0) {
2821 ✗ FrameData *fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL;
2822 ✗ tb = av_buffersink_get_time_base(is->out_audio_filter);
2823 ✗ if (!(af = frame_queue_peek_writable(&is->sampq)))
2824 ✗ goto the_end;
2825
2826 ✗ af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
2827 ✗ af->pos = fd ? fd->pkt_pos : -1;
2828 ✗ af->serial = is->auddec.pkt_serial;
2829 ✗ af->duration = av_q2d((AVRational){frame->nb_samples, frame->sample_rate});
2830
2831 ✗ av_frame_move_ref(af->frame, frame);
2832 ✗ frame_queue_push(&is->sampq);
2833
2834 ✗ if (is->audioq.serial != is->auddec.pkt_serial)
2835 ✗ break;
2836 }
2837 ✗ if (ret == AVERROR_EOF)
2838 ✗ is->auddec.finished = is->auddec.pkt_serial;
2839 }
2840 ✗ } while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF);
2841 ✗ the_end:
2842 ✗ avfilter_graph_free(&is->agraph);
2843 ✗ av_frame_free(&frame);
2844 ✗ return ret;
2845 }
2846
2847 ✗ static int decoder_start(Decoder *d, int (*fn)(void *), const char *thread_name, void* arg)
2848 {
2849 ✗ packet_queue_start(d->queue);
2850 ✗ d->decoder_tid = SDL_CreateThread(fn, thread_name, arg);
2851 ✗ if (!d->decoder_tid) {
2852 ✗ av_log(NULL, AV_LOG_ERROR, "SDL_CreateThread(): %s\n", SDL_GetError());
2853 ✗ return AVERROR(ENOMEM);
2854 }
2855 ✗ return 0;
2856 }
2857
2858 ✗ static int video_thread(void *arg)
2859 {
2860 ✗ VideoState *is = arg;
2861 ✗ AVFrame *frame = av_frame_alloc();
2862 double pts;
2863 double duration;
2864 int ret;
2865 ✗ AVRational tb = is->video_st->time_base;
2866 ✗ AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL);
2867
2868 ✗ AVFilterGraph *graph = NULL;
2869 ✗ AVFilterContext *filt_out = NULL, *filt_in = NULL;
2870 ✗ int last_w = 0;
2871 ✗ int last_h = 0;
2872 ✗ enum AVPixelFormat last_format = -2;
2873 ✗ int last_serial = -1;
2874 ✗ int last_vfilter_idx = 0;
2875
2876 ✗ if (!frame)
2877 ✗ return AVERROR(ENOMEM);
2878
2879 for (;;) {
2880 ✗ ret = get_video_frame(is, frame);
2881 ✗ if (ret < 0)
2882 ✗ goto the_end;
2883 ✗ if (!ret)
2884 ✗ continue;
2885
2886 ✗ if ( last_w != frame->width
2887 ✗ || last_h != frame->height
2888 ✗ || last_format != frame->format
2889 ✗ || last_serial != is->viddec.pkt_serial
2890 ✗ || last_vfilter_idx != is->vfilter_idx) {
2891 ✗ av_log(NULL, AV_LOG_DEBUG,
2892 "Video frame changed from size:%dx%d format:%s serial:%d to size:%dx%d format:%s serial:%d\n",
2893 last_w, last_h,
2894 ✗ (const char *)av_x_if_null(av_get_pix_fmt_name(last_format), "none"), last_serial,
2895 ✗ frame->width, frame->height,
2896 ✗ (const char *)av_x_if_null(av_get_pix_fmt_name(frame->format), "none"), is->viddec.pkt_serial);
2897 ✗ avfilter_graph_free(&graph);
2898 ✗ graph = avfilter_graph_alloc();
2899 ✗ if (!graph) {
2900 ✗ ret = AVERROR(ENOMEM);
2901 ✗ goto the_end;
2902 }
2903 ✗ graph->nb_threads = filter_nbthreads;
2904 ✗ if ((ret = configure_video_filters(graph, is, vfilters_list ? vfilters_list[is->vfilter_idx] : NULL, frame)) < 0) {
2905 SDL_Event event;
2906 ✗ event.type = FF_QUIT_EVENT;
2907 ✗ event.user.data1 = is;
2908 ✗ SDL_PushEvent(&event);
2909 ✗ goto the_end;
2910 }
2911 ✗ filt_in = is->in_video_filter;
2912 ✗ filt_out = is->out_video_filter;
2913 ✗ last_w = frame->width;
2914 ✗ last_h = frame->height;
2915 ✗ last_format = frame->format;
2916 ✗ last_serial = is->viddec.pkt_serial;
2917 ✗ last_vfilter_idx = is->vfilter_idx;
2918 ✗ frame_rate = av_buffersink_get_frame_rate(filt_out);
2919 }
2920
2921 ✗ ret = av_buffersrc_add_frame(filt_in, frame);
2922 ✗ if (ret < 0)
2923 ✗ goto the_end;
2924
2925 ✗ while (ret >= 0) {
2926 FrameData *fd;
2927
2928 ✗ is->frame_last_returned_time = av_gettime_relative() / 1000000.0;
2929
2930 ✗ ret = av_buffersink_get_frame_flags(filt_out, frame, 0);
2931 ✗ if (ret < 0) {
2932 ✗ if (ret == AVERROR_EOF)
2933 ✗ is->viddec.finished = is->viddec.pkt_serial;
2934 ✗ ret = 0;
2935 ✗ break;
2936 }
2937
2938 ✗ fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL;
2939
2940 ✗ is->frame_last_filter_delay = av_gettime_relative() / 1000000.0 - is->frame_last_returned_time;
2941 ✗ if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0)
2942 ✗ is->frame_last_filter_delay = 0;
2943 ✗ tb = av_buffersink_get_time_base(filt_out);
2944 ✗ duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0);
2945 ✗ pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
2946 ✗ ret = queue_picture(is, frame, pts, duration, fd ? fd->pkt_pos : -1, is->viddec.pkt_serial);
2947 ✗ av_frame_unref(frame);
2948 ✗ if (is->videoq.serial != is->viddec.pkt_serial)
2949 ✗ break;
2950 }
2951
2952 ✗ if (ret < 0)
2953 ✗ goto the_end;
2954 }
2955 ✗ the_end:
2956 ✗ avfilter_graph_free(&graph);
2957 ✗ av_frame_free(&frame);
2958 ✗ return 0;
2959 }
2960
2961 ✗ static int subtitle_thread(void *arg)
2962 {
2963 ✗ VideoState *is = arg;
2964 Frame *sp;
2965 int got_subtitle;
2966 double pts;
2967
2968 for (;;) {
2969 ✗ if (!(sp = frame_queue_peek_writable(&is->subpq)))
2970 ✗ return 0;
2971
2972 ✗ if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0)
2973 ✗ break;
2974
2975 ✗ pts = 0;
2976
2977 ✗ if (got_subtitle && sp->sub.format == 0) {
2978 ✗ if (sp->sub.pts != AV_NOPTS_VALUE)
2979 ✗ pts = sp->sub.pts / (double)AV_TIME_BASE;
2980 ✗ sp->pts = pts;
2981 ✗ sp->serial = is->subdec.pkt_serial;
2982 ✗ sp->width = is->subdec.avctx->width;
2983 ✗ sp->height = is->subdec.avctx->height;
2984 ✗ sp->uploaded = 0;
2985
2986 /* now we can update the picture count */
2987 ✗ frame_queue_push(&is->subpq);
2988 ✗ } else if (got_subtitle) {
2989 ✗ avsubtitle_free(&sp->sub);
2990 }
2991 }
2992 ✗ return 0;
2993 }
2994
2995 /* copy samples for viewing in editor window */
2996 ✗ static void update_sample_display(VideoState *is, short *samples, int samples_size)
2997 {
2998 int size, len;
2999
3000 ✗ size = samples_size / sizeof(short);
3001 ✗ while (size > 0) {
3002 ✗ len = SAMPLE_ARRAY_SIZE - is->sample_array_index;
3003 ✗ if (len > size)
3004 ✗ len = size;
3005 ✗ memcpy(is->sample_array + is->sample_array_index, samples, len * sizeof(short));
3006 ✗ samples += len;
3007 ✗ is->sample_array_index += len;
3008 ✗ if (is->sample_array_index >= SAMPLE_ARRAY_SIZE)
3009 ✗ is->sample_array_index = 0;
3010 ✗ size -= len;
3011 }
3012 ✗ }
3013
3014 /* return the wanted number of samples to get better sync if sync_type is video
3015 * or external master clock */
3016 ✗ static int synchronize_audio(VideoState *is, int nb_samples)
3017 {
3018 ✗ int wanted_nb_samples = nb_samples;
3019
3020 /* if not master, then we try to remove or add samples to correct the clock */
3021 ✗ if (get_master_sync_type(is) != AV_SYNC_AUDIO_MASTER) {
3022 double diff, avg_diff;
3023 int min_nb_samples, max_nb_samples;
3024
3025 ✗ diff = get_clock(&is->audclk) - get_master_clock(is);
3026
3027 ✗ if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD) {
3028 ✗ is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum;
3029 ✗ if (is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) {
3030 /* not enough measures to have a correct estimate */
3031 ✗ is->audio_diff_avg_count++;
3032 } else {
3033 /* estimate the A-V difference */
3034 ✗ avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef);
3035
3036 ✗ if (fabs(avg_diff) >= is->audio_diff_threshold) {
3037 ✗ wanted_nb_samples = nb_samples + (int)(diff * is->audio_src.freq);
3038 ✗ min_nb_samples = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100));
3039 ✗ max_nb_samples = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100));
3040 ✗ wanted_nb_samples = av_clip(wanted_nb_samples, min_nb_samples, max_nb_samples);
3041 }
3042 ✗ av_log(NULL, AV_LOG_TRACE, "diff=%f adiff=%f sample_diff=%d apts=%0.3f %f\n",
3043 diff, avg_diff, wanted_nb_samples - nb_samples,
3044 is->audio_clock, is->audio_diff_threshold);
3045 }
3046 } else {
3047 /* too big difference : may be initial PTS errors, so
3048 reset A-V filter */
3049 ✗ is->audio_diff_avg_count = 0;
3050 ✗ is->audio_diff_cum = 0;
3051 }
3052 }
3053
3054 ✗ return wanted_nb_samples;
3055 }
3056
3057 /**
3058 * Decode one audio frame and return its uncompressed size.
3059 *
3060 * The processed audio frame is decoded, converted if required, and
3061 * stored in is->audio_buf, with size in bytes given by the return
3062 * value.
3063 */
3064 ✗ static int audio_decode_frame(VideoState *is)
3065 {
3066 int data_size, resampled_data_size;
3067 av_unused double audio_clock0;
3068 int wanted_nb_samples;
3069 Frame *af;
3070
3071 ✗ if (is->paused)
3072 ✗ return -1;
3073
3074 do {
3075 #if defined(_WIN32)
3076 while (frame_queue_nb_remaining(&is->sampq) == 0) {
3077 if ((av_gettime_relative() - audio_callback_time) > 1000000LL * is->audio_hw_buf_size / is->audio_tgt.bytes_per_sec / 2)
3078 return -1;
3079 av_usleep (1000);
3080 }
3081 #endif
3082 ✗ if (!(af = frame_queue_peek_readable(&is->sampq)))
3083 ✗ return -1;
3084 ✗ frame_queue_next(&is->sampq);
3085 ✗ } while (af->serial != is->audioq.serial);
3086
3087 ✗ data_size = av_samples_get_buffer_size(NULL, af->frame->ch_layout.nb_channels,
3088 ✗ af->frame->nb_samples,
3089 ✗ af->frame->format, 1);
3090
3091 ✗ wanted_nb_samples = synchronize_audio(is, af->frame->nb_samples);
3092
3093 ✗ if (af->frame->format != is->audio_src.fmt ||
3094 ✗ av_channel_layout_compare(&af->frame->ch_layout, &is->audio_src.ch_layout) ||
3095 ✗ af->frame->sample_rate != is->audio_src.freq ||
3096 ✗ (wanted_nb_samples != af->frame->nb_samples && !is->swr_ctx)) {
3097 int ret;
3098 ✗ swr_free(&is->swr_ctx);
3099 ✗ ret = swr_alloc_set_opts2(&is->swr_ctx,
3100 ✗ &is->audio_tgt.ch_layout, is->audio_tgt.fmt, is->audio_tgt.freq,
3101 ✗ &af->frame->ch_layout, af->frame->format, af->frame->sample_rate,
3102 0, NULL);
3103 ✗ if (ret < 0 || swr_init(is->swr_ctx) < 0) {
3104 ✗ av_log(NULL, AV_LOG_ERROR,
3105 "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
3106 ✗ af->frame->sample_rate, av_get_sample_fmt_name(af->frame->format), af->frame->ch_layout.nb_channels,
3107 is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.ch_layout.nb_channels);
3108 ✗ swr_free(&is->swr_ctx);
3109 ✗ return -1;
3110 }
3111 ✗ if (av_channel_layout_copy(&is->audio_src.ch_layout, &af->frame->ch_layout) < 0)
3112 ✗ return -1;
3113 ✗ is->audio_src.freq = af->frame->sample_rate;
3114 ✗ is->audio_src.fmt = af->frame->format;
3115 }
3116
3117 ✗ if (is->swr_ctx) {
3118 ✗ const uint8_t **in = (const uint8_t **)af->frame->extended_data;
3119 ✗ uint8_t **out = &is->audio_buf1;
3120 ✗ int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate + 256;
3121 ✗ int out_size = av_samples_get_buffer_size(NULL, is->audio_tgt.ch_layout.nb_channels, out_count, is->audio_tgt.fmt, 0);
3122 int len2;
3123 ✗ if (out_size < 0) {
3124 ✗ av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n");
3125 ✗ return -1;
3126 }
3127 ✗ if (wanted_nb_samples != af->frame->nb_samples) {
3128 ✗ if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - af->frame->nb_samples) * is->audio_tgt.freq / af->frame->sample_rate,
3129 ✗ wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate) < 0) {
3130 ✗ av_log(NULL, AV_LOG_ERROR, "swr_set_compensation() failed\n");
3131 ✗ return -1;
3132 }
3133 }
3134 ✗ av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size);
3135 ✗ if (!is->audio_buf1)
3136 ✗ return AVERROR(ENOMEM);
3137 ✗ len2 = swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples);
3138 ✗ if (len2 < 0) {
3139 ✗ av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n");
3140 ✗ return -1;
3141 }
3142 ✗ if (len2 == out_count) {
3143 ✗ av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n");
3144 ✗ if (swr_init(is->swr_ctx) < 0)
3145 ✗ swr_free(&is->swr_ctx);
3146 }
3147 ✗ is->audio_buf = is->audio_buf1;
3148 ✗ resampled_data_size = len2 * is->audio_tgt.ch_layout.nb_channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
3149 } else {
3150 ✗ is->audio_buf = af->frame->data[0];
3151 ✗ resampled_data_size = data_size;
3152 }
3153
3154 ✗ audio_clock0 = is->audio_clock;
3155 /* update the audio clock with the pts */
3156 ✗ if (!isnan(af->pts))
3157 ✗ is->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate;
3158 else
3159 ✗ is->audio_clock = NAN;
3160 ✗ is->audio_clock_serial = af->serial;
3161 #ifdef DEBUG
3162 {
3163 static double last_clock;
3164 printf("audio: delay=%0.3f clock=%0.3f clock0=%0.3f\n",
3165 is->audio_clock - last_clock,
3166 is->audio_clock, audio_clock0);
3167 last_clock = is->audio_clock;
3168 }
3169 #endif
3170 ✗ return resampled_data_size;
3171 }
3172
3173 /* prepare a new audio buffer */
3174 ✗ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
3175 {
3176 ✗ VideoState *is = opaque;
3177 int audio_size, len1;
3178
3179 ✗ audio_callback_time = av_gettime_relative();
3180
3181 ✗ while (len > 0) {
3182 ✗ if (is->audio_buf_index >= is->audio_buf_size) {
3183 ✗ audio_size = audio_decode_frame(is);
3184 ✗ if (audio_size < 0) {
3185 /* if error, just output silence */
3186 ✗ is->audio_buf = NULL;
3187 ✗ is->audio_buf_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size * is->audio_tgt.frame_size;
3188 } else {
3189 ✗ if (is->show_mode != SHOW_MODE_VIDEO)
3190 ✗ update_sample_display(is, (int16_t *)is->audio_buf, audio_size);
3191 ✗ is->audio_buf_size = audio_size;
3192 }
3193 ✗ is->audio_buf_index = 0;
3194 }
3195 ✗ len1 = is->audio_buf_size - is->audio_buf_index;
3196 ✗ if (len1 > len)
3197 ✗ len1 = len;
3198 ✗ if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)
3199 ✗ memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
3200 else {
3201 ✗ memset(stream, 0, len1);
3202 ✗ if (!is->muted && is->audio_buf)
3203 ✗ SDL_MixAudioFormat(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, is->audio_volume);
3204 }
3205 ✗ len -= len1;
3206 ✗ stream += len1;
3207 ✗ is->audio_buf_index += len1;
3208 }
3209 ✗ is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index;
3210 /* Let's assume the audio driver that is used by SDL has two periods. */
3211 ✗ if (!isnan(is->audio_clock)) {
3212 ✗ set_clock_at(&is->audclk, is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec, is->audio_clock_serial, audio_callback_time / 1000000.0);
3213 ✗ sync_clock_to_slave(&is->extclk, &is->audclk);
3214 }
3215 ✗ }
3216
3217 ✗ static int audio_open(void *opaque, AVChannelLayout *wanted_channel_layout, int wanted_sample_rate, struct AudioParams *audio_hw_params)
3218 {
3219 SDL_AudioSpec wanted_spec, spec;
3220 const char *env;
3221 static const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6};
3222 static const int next_sample_rates[] = {0, 44100, 48000, 96000, 192000};
3223 ✗ int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;
3224 ✗ int wanted_nb_channels = wanted_channel_layout->nb_channels;
3225
3226 ✗ env = SDL_getenv("SDL_AUDIO_CHANNELS");
3227 ✗ if (env) {
3228 ✗ wanted_nb_channels = atoi(env);
3229 ✗ av_channel_layout_uninit(wanted_channel_layout);
3230 ✗ av_channel_layout_default(wanted_channel_layout, wanted_nb_channels);
3231 }
3232 ✗ if (wanted_channel_layout->order != AV_CHANNEL_ORDER_NATIVE) {
3233 ✗ av_channel_layout_uninit(wanted_channel_layout);
3234 ✗ av_channel_layout_default(wanted_channel_layout, wanted_nb_channels);
3235 }
3236 ✗ wanted_nb_channels = wanted_channel_layout->nb_channels;
3237 ✗ wanted_spec.channels = wanted_nb_channels;
3238 ✗ wanted_spec.freq = wanted_sample_rate;
3239 ✗ if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
3240 ✗ av_log(NULL, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
3241 ✗ return -1;
3242 }
3243 ✗ while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq)
3244 ✗ next_sample_rate_idx--;
3245 ✗ wanted_spec.format = AUDIO_S16SYS;
3246 ✗ wanted_spec.silence = 0;
3247 ✗ wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC));
3248 ✗ wanted_spec.callback = sdl_audio_callback;
3249 ✗ wanted_spec.userdata = opaque;
3250 ✗ while (!(audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE))) {
3251 ✗ av_log(NULL, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
3252 ✗ wanted_spec.channels, wanted_spec.freq, SDL_GetError());
3253 ✗ wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
3254 ✗ if (!wanted_spec.channels) {
3255 ✗ wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
3256 ✗ wanted_spec.channels = wanted_nb_channels;
3257 ✗ if (!wanted_spec.freq) {
3258 ✗ av_log(NULL, AV_LOG_ERROR,
3259 "No more combinations to try, audio open failed\n");
3260 ✗ return -1;
3261 }
3262 }
3263 ✗ av_channel_layout_default(wanted_channel_layout, wanted_spec.channels);
3264 }
3265 ✗ if (spec.format != AUDIO_S16SYS) {
3266 ✗ av_log(NULL, AV_LOG_ERROR,
3267 ✗ "SDL advised audio format %d is not supported!\n", spec.format);
3268 ✗ return -1;
3269 }
3270 ✗ if (spec.channels != wanted_spec.channels) {
3271 ✗ av_channel_layout_uninit(wanted_channel_layout);
3272 ✗ av_channel_layout_default(wanted_channel_layout, spec.channels);
3273 ✗ if (wanted_channel_layout->order != AV_CHANNEL_ORDER_NATIVE) {
3274 ✗ av_log(NULL, AV_LOG_ERROR,
3275 ✗ "SDL advised channel count %d is not supported!\n", spec.channels);
3276 ✗ return -1;
3277 }
3278 }
3279
3280 ✗ audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
3281 ✗ audio_hw_params->freq = spec.freq;
3282 ✗ if (av_channel_layout_copy(&audio_hw_params->ch_layout, wanted_channel_layout) < 0)
3283 ✗ return -1;
3284 ✗ audio_hw_params->frame_size = av_samples_get_buffer_size(NULL, audio_hw_params->ch_layout.nb_channels, 1, audio_hw_params->fmt, 1);
3285 ✗ audio_hw_params->bytes_per_sec = av_samples_get_buffer_size(NULL, audio_hw_params->ch_layout.nb_channels, audio_hw_params->freq, audio_hw_params->fmt, 1);
3286 ✗ if (audio_hw_params->bytes_per_sec <= 0 || audio_hw_params->frame_size <= 0) {
3287 ✗ av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
3288 ✗ return -1;
3289 }
3290 ✗ return spec.size;
3291 }
3292
3293 ✗ static int create_hwaccel(AVBufferRef **device_ctx)
3294 {
3295 enum AVHWDeviceType type;
3296 int ret;
3297 AVBufferRef *vk_dev;
3298
3299 ✗ *device_ctx = NULL;
3300
3301 ✗ if (!hwaccel)
3302 ✗ return 0;
3303
3304 ✗ type = av_hwdevice_find_type_by_name(hwaccel);
3305 ✗ if (type == AV_HWDEVICE_TYPE_NONE)
3306 ✗ return AVERROR(ENOTSUP);
3307
3308 ✗ if (!vk_renderer) {
3309 ✗ av_log(NULL, AV_LOG_ERROR, "Vulkan renderer is not available\n");
3310 ✗ return AVERROR(ENOTSUP);
3311 }
3312
3313 ✗ ret = vk_renderer_get_hw_dev(vk_renderer, &vk_dev);
3314 ✗ if (ret < 0)
3315 ✗ return ret;
3316
3317 ✗ ret = av_hwdevice_ctx_create_derived(device_ctx, type, vk_dev, 0);
3318 ✗ if (!ret)
3319 ✗ return 0;
3320
3321 ✗ if (ret != AVERROR(ENOSYS))
3322 ✗ return ret;
3323
3324 ✗ av_log(NULL, AV_LOG_WARNING, "Derive %s from vulkan not supported.\n", hwaccel);
3325 ✗ ret = av_hwdevice_ctx_create(device_ctx, type, NULL, NULL, 0);
3326 ✗ return ret;
3327 }
3328
3329 ✗ static int init_lcevc_graph(AVFormatContext *ic, int stream_index)
3330 {
3331 ✗ FormatContext *ici = ic->opaque;
3332 ✗ StreamGroup *stgi = ici->streams[stream_index]->group;
3333 ✗ AVStreamGroup *stg = stgi->stg;
3334 ✗ const AVBitStreamFilter *filter, *lcevc_filter = av_bsf_get_by_name("lcevc_merge");
3335 AVBitStreamFilterContext *lcevc_merge;
3336 int ret;
3337
3338 ✗ stgi->graph = av_bsf_graph_alloc();
3339 ✗ if (!stgi->graph)
3340 ✗ return AVERROR(ENOMEM);
3341
3342 ✗ const AVStreamGroupLayeredVideo *lcevc = stg->params.layered_video;
3343 ✗ AVStream *base_st = ic->streams[stream_index];
3344 ✗ AVStream *lcevc_st = stg->streams[lcevc->el_index];
3345 ✗ Stream *lcevc_sti = ici->streams[lcevc_st->index];
3346 ✗ Stream *base_sti = ici->streams[stream_index];
3347
3348 ✗ filter = av_bsf_get_by_name("source");
3349 ✗ ret = av_bsf_graph_alloc_filter(&base_sti->filter, filter, "lcevc_merge_base", stgi->graph);
3350 ✗ if (ret < 0)
3351 ✗ return ret;
3352 ✗ av_opt_set_q(base_sti->filter->priv_data, "time_base", base_st->time_base, 0);
3353 ✗ ret = av_bsf_source_parameters_set(base_sti->filter, base_st->codecpar);
3354 ✗ if (ret < 0)
3355 ✗ return ret;
3356
3357 ✗ ret = av_bsf_graph_alloc_filter(&lcevc_sti->filter, filter, "lcevc_merge_enhancement", stgi->graph);
3358 ✗ if (ret < 0)
3359 ✗ return ret;
3360 ✗ av_opt_set_q(lcevc_sti->filter->priv_data, "time_base", lcevc_st->time_base, 0);
3361 ✗ ret = av_bsf_source_parameters_set(lcevc_sti->filter, lcevc_st->codecpar);
3362 ✗ if (ret < 0)
3363 ✗ return ret;
3364
3365 ✗ ret = av_bsf_graph_alloc_filter(&lcevc_merge, lcevc_filter, "lcevc_merge", stgi->graph);
3366 ✗ if (ret < 0)
3367 ✗ return ret;
3368
3369 ✗ filter = av_bsf_get_by_name("sink");
3370 ✗ ret = av_bsf_graph_alloc_filter(&stgi->sink, filter, "lcevc_merge_sink", stgi->graph);
3371 ✗ if (ret < 0)
3372 ✗ return ret;
3373
3374 ✗ ret = av_bsf_init_dict(base_sti->filter, NULL);
3375 ✗ if (ret < 0)
3376 ✗ return ret;
3377 ✗ ret = av_bsf_init_dict(lcevc_sti->filter, NULL);
3378 ✗ if (ret < 0)
3379 ✗ return ret;
3380 ✗ ret = av_bsf_init_dict(lcevc_merge, NULL);
3381 ✗ if (ret < 0)
3382 ✗ return ret;
3383 ✗ ret = av_bsf_init_dict(stgi->sink, NULL);
3384 ✗ if (ret < 0)
3385 ✗ return ret;
3386
3387 ✗ ret = av_bsf_link(base_sti->filter, 0, lcevc_merge, 0);
3388 ✗ if (ret < 0)
3389 ✗ return ret;
3390 ✗ ret = av_bsf_link(lcevc_sti->filter, 0, lcevc_merge, 1);
3391 ✗ if (ret < 0)
3392 ✗ return ret;
3393 ✗ ret = av_bsf_link(lcevc_merge, 0, stgi->sink, 0);
3394 ✗ if (ret < 0)
3395 ✗ return ret;
3396
3397 ✗ ret = av_bsf_graph_config(stgi->graph, NULL);
3398 ✗ if (ret < 0)
3399 ✗ return ret;
3400
3401 ✗ lcevc_st->discard = AVDISCARD_DEFAULT;
3402 ✗ lcevc_sti->group = stgi;
3403
3404 ✗ return 0;
3405 }
3406
3407 /* open a given stream. Return 0 if OK */
3408 ✗ static int stream_component_open(VideoState *is, int stream_index)
3409 {
3410 ✗ AVFormatContext *ic = is->ic;
3411 ✗ FormatContext *ici = ic->opaque;
3412 AVCodecContext *avctx;
3413 const AVCodec *codec;
3414 ✗ const char *forced_codec_name = NULL;
3415 ✗ AVDictionary *opts = NULL;
3416 int sample_rate;
3417 ✗ AVChannelLayout ch_layout = { 0 };
3418 ✗ int ret = 0;
3419 ✗ int stream_lowres = lowres;
3420
3421 ✗ if (stream_index < 0 || stream_index >= ic->nb_streams)
3422 ✗ return -1;
3423
3424 ✗ avctx = avcodec_alloc_context3(NULL);
3425 ✗ if (!avctx)
3426 ✗ return AVERROR(ENOMEM);
3427
3428 ✗ ret = avcodec_parameters_to_context(avctx, ic->streams[stream_index]->codecpar);
3429 ✗ if (ret < 0)
3430 ✗ goto fail;
3431 ✗ avctx->pkt_timebase = ic->streams[stream_index]->time_base;
3432
3433 ✗ codec = avcodec_find_decoder(avctx->codec_id);
3434
3435 ✗ switch(avctx->codec_type){
3436 ✗ case AVMEDIA_TYPE_AUDIO : is->last_audio_stream = stream_index; forced_codec_name = audio_codec_name; break;
3437 ✗ case AVMEDIA_TYPE_SUBTITLE: is->last_subtitle_stream = stream_index; forced_codec_name = subtitle_codec_name; break;
3438 ✗ case AVMEDIA_TYPE_VIDEO : is->last_video_stream = stream_index; forced_codec_name = video_codec_name; break;
3439 }
3440 ✗ if (forced_codec_name)
3441 ✗ codec = avcodec_find_decoder_by_name(forced_codec_name);
3442 ✗ if (!codec) {
3443 ✗ if (forced_codec_name) av_log(NULL, AV_LOG_WARNING,
3444 "No codec could be found with name '%s'\n", forced_codec_name);
3445 ✗ else av_log(NULL, AV_LOG_WARNING,
3446 ✗ "No decoder could be found for codec %s\n", avcodec_get_name(avctx->codec_id));
3447 ✗ ret = AVERROR(EINVAL);
3448 ✗ goto fail;
3449 }
3450
3451 ✗ avctx->codec_id = codec->id;
3452 ✗ if (stream_lowres > codec->max_lowres) {
3453 ✗ av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",
3454 ✗ codec->max_lowres);
3455 ✗ stream_lowres = codec->max_lowres;
3456 }
3457 ✗ avctx->lowres = stream_lowres;
3458
3459 ✗ if (fast)
3460 ✗ avctx->flags2 |= AV_CODEC_FLAG2_FAST;
3461
3462 ✗ ret = filter_codec_opts(codec_opts, avctx->codec_id, ic,
3463 ✗ ic->streams[stream_index], codec, &opts, NULL);
3464 ✗ if (ret < 0)
3465 ✗ goto fail;
3466
3467 ✗ if (!av_dict_get(opts, "threads", NULL, 0))
3468 ✗ av_dict_set(&opts, "threads", "auto", 0);
3469 ✗ if (stream_lowres)
3470 ✗ av_dict_set_int(&opts, "lowres", stream_lowres, 0);
3471
3472 ✗ av_dict_set(&opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY);
3473
3474 ✗ if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
3475 ✗ ret = create_hwaccel(&avctx->hw_device_ctx);
3476 ✗ if (ret < 0)
3477 ✗ goto fail;
3478 }
3479
3480 ✗ if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {
3481 ✗ goto fail;
3482 }
3483 ✗ ret = check_avoptions(opts);
3484 ✗ if (ret < 0)
3485 ✗ goto fail;
3486
3487 ✗ is->eof = 0;
3488 ✗ ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
3489 ✗ switch (avctx->codec_type) {
3490 ✗ case AVMEDIA_TYPE_AUDIO:
3491 {
3492 AVFilterContext *sink;
3493
3494 ✗ is->audio_filter_src.freq = avctx->sample_rate;
3495 ✗ ret = av_channel_layout_copy(&is->audio_filter_src.ch_layout, &avctx->ch_layout);
3496 ✗ if (ret < 0)
3497 ✗ goto fail;
3498 ✗ is->audio_filter_src.fmt = avctx->sample_fmt;
3499 ✗ if ((ret = configure_audio_filters(is, afilters, 0)) < 0)
3500 ✗ goto fail;
3501 ✗ sink = is->out_audio_filter;
3502 ✗ sample_rate = av_buffersink_get_sample_rate(sink);
3503 ✗ ret = av_buffersink_get_ch_layout(sink, &ch_layout);
3504 ✗ if (ret < 0)
3505 ✗ goto fail;
3506 }
3507
3508 /* prepare audio output */
3509 ✗ if ((ret = audio_open(is, &ch_layout, sample_rate, &is->audio_tgt)) < 0)
3510 ✗ goto fail;
3511 ✗ is->audio_hw_buf_size = ret;
3512 ✗ is->audio_src = is->audio_tgt;
3513 ✗ is->audio_buf_size = 0;
3514 ✗ is->audio_buf_index = 0;
3515
3516 /* init averaging filter */
3517 ✗ is->audio_diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB);
3518 ✗ is->audio_diff_avg_count = 0;
3519 /* since we do not have a precise anough audio FIFO fullness,
3520 we correct audio sync only if larger than this threshold */
3521 ✗ is->audio_diff_threshold = (double)(is->audio_hw_buf_size) / is->audio_tgt.bytes_per_sec;
3522
3523 ✗ is->audio_stream = stream_index;
3524 ✗ is->audio_st = ic->streams[stream_index];
3525
3526 ✗ if ((ret = decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread)) < 0)
3527 ✗ goto fail;
3528 ✗ if (is->ic->iformat->flags & AVFMT_NOTIMESTAMPS) {
3529 ✗ is->auddec.start_pts = is->audio_st->start_time;
3530 ✗ is->auddec.start_pts_tb = is->audio_st->time_base;
3531 }
3532 ✗ if ((ret = decoder_start(&is->auddec, audio_thread, "audio_decoder", is)) < 0)
3533 ✗ goto out;
3534 ✗ SDL_PauseAudioDevice(audio_dev, 0);
3535 ✗ break;
3536 ✗ case AVMEDIA_TYPE_VIDEO:
3537 ✗ is->video_stream = stream_index;
3538 ✗ is->video_st = ic->streams[stream_index];
3539
3540 ✗ if (ici->streams[stream_index]->group) {
3541 ✗ if ((ret = init_lcevc_graph(ic, stream_index)) < 0)
3542 ✗ goto fail;
3543 }
3544 ✗ if ((ret = decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread)) < 0)
3545 ✗ goto fail;
3546 ✗ if ((ret = decoder_start(&is->viddec, video_thread, "video_decoder", is)) < 0)
3547 ✗ goto out;
3548 ✗ is->queue_attachments_req = 1;
3549 ✗ break;
3550 ✗ case AVMEDIA_TYPE_SUBTITLE:
3551 ✗ is->subtitle_stream = stream_index;
3552 ✗ is->subtitle_st = ic->streams[stream_index];
3553
3554 ✗ if ((ret = decoder_init(&is->subdec, avctx, &is->subtitleq, is->continue_read_thread)) < 0)
3555 ✗ goto fail;
3556 ✗ if ((ret = decoder_start(&is->subdec, subtitle_thread, "subtitle_decoder", is)) < 0)
3557 ✗ goto out;
3558 ✗ break;
3559 ✗ default:
3560 ✗ break;
3561 }
3562 ✗ goto out;
3563
3564 ✗ fail:
3565 ✗ avcodec_free_context(&avctx);
3566 ✗ out:
3567 ✗ av_channel_layout_uninit(&ch_layout);
3568 ✗ av_dict_free(&opts);
3569
3570 ✗ return ret;
3571 }
3572
3573 ✗ static int decode_interrupt_cb(void *ctx)
3574 {
3575 ✗ VideoState *is = ctx;
3576 ✗ return is->abort_request;
3577 }
3578
3579 ✗ static int stream_has_enough_packets(AVStream *st, int stream_id, PacketQueue *queue) {
3580 ✗ return stream_id < 0 ||
3581 ✗ queue->abort_request ||
3582 ✗ (st->disposition & AV_DISPOSITION_ATTACHED_PIC) ||
3583 ✗ queue->nb_packets > MIN_FRAMES && (!queue->duration || av_q2d(st->time_base) * queue->duration > 1.0);
3584 }
3585
3586 ✗ static int is_realtime(AVFormatContext *s)
3587 {
3588 ✗ if( !strcmp(s->iformat->name, "rtp")
3589 ✗ || !strcmp(s->iformat->name, "rtsp")
3590 ✗ || !strcmp(s->iformat->name, "sdp")
3591 )
3592 ✗ return 1;
3593
3594 ✗ if(s->pb && ( !strncmp(s->url, "rtp:", 4)
3595 ✗ || !strncmp(s->url, "udp:", 4)
3596 )
3597 )
3598 ✗ return 1;
3599 ✗ return 0;
3600 }
3601
3602 ✗ static int do_bsf_graph(AVFormatContext *ic, VideoState *is,
3603 Stream *sti, AVPacket *pkt)
3604 {
3605 ✗ StreamGroup *stgi = sti->group;
3606 ✗ AVBitStreamFilterContext *source = sti->filter;
3607 int ret;
3608
3609 ✗ ret = av_bsf_source_add_packet(source, pkt, AV_BSF_SOURCE_FLAG_PUSH);
3610 ✗ if (ret < 0) {
3611 ✗ if (pkt)
3612 ✗ av_packet_unref(pkt);
3613 ✗ av_log(NULL, AV_LOG_ERROR, "Error submitting a packet for filtering: %s\n",
3614 ✗ av_err2str(ret));
3615 ✗ return ret;
3616 }
3617
3618 while (1) {
3619 ✗ ret = av_bsf_sink_get_packet(stgi->sink, pkt, 0);
3620 ✗ if (ret == AVERROR(EAGAIN))
3621 ✗ return 0;
3622 ✗ else if (ret < 0) {
3623 ✗ if (ret != AVERROR_EOF)
3624 ✗ av_log(NULL, AV_LOG_ERROR,
3625 "Error applying bitstream filters to a packet: %s\n",
3626 ✗ av_err2str(ret));
3627 ✗ return ret;
3628 }
3629 ✗ pkt->time_base = av_bsf_sink_get_time_base(stgi->sink);
3630 ✗ packet_queue_put(&is->videoq, pkt);
3631 }
3632
3633 return 0;
3634 }
3635
3636 ✗ static int do_bsf_flush(AVFormatContext *ic, VideoState *is)
3637 {
3638 ✗ FormatContext *ici = ic->opaque;
3639 int ret;
3640
3641 ✗ for (unsigned i = 0; i < ic->nb_streams; i++) {
3642 ✗ Stream *sti = ici->streams[i];
3643 ✗ StreamGroup *stgi = sti->group;
3644
3645 ✗ if (!stgi || !stgi->graph)
3646 ✗ continue;
3647
3648 ✗ ret = do_bsf_graph(ic, is, sti, NULL);
3649 ✗ ret = (ret == AVERROR_EOF) ? 0 : (ret < 0) ? ret : AVERROR_BUG;
3650 ✗ if (ret < 0) {
3651 ✗ av_log(NULL, AV_LOG_ERROR, "Error flushing BSFs: %s\n",
3652 ✗ av_err2str(ret));
3653 ✗ return ret;
3654 }
3655 }
3656
3657 ✗ return 0;
3658 }
3659
3660 /* this thread gets the stream from the disk or the network */
3661 ✗ static int read_thread(void *arg)
3662 {
3663 ✗ VideoState *is = arg;
3664 ✗ AVFormatContext *ic = NULL;
3665 ✗ FormatContext *ici = NULL;
3666 int err, i, ret;
3667 int st_index[AVMEDIA_TYPE_NB];
3668 ✗ AVPacket *pkt = NULL;
3669 int64_t stream_start_time;
3670 char metadata_description[96];
3671 ✗ int pkt_in_play_range = 0;
3672 const AVDictionaryEntry *t;
3673 ✗ SDL_mutex *wait_mutex = SDL_CreateMutex();
3674 ✗ int scan_all_pmts_set = 0;
3675 int64_t pkt_ts;
3676
3677 ✗ if (!wait_mutex) {
3678 ✗ av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
3679 ✗ ret = AVERROR(ENOMEM);
3680 ✗ goto fail;
3681 }
3682
3683 ✗ memset(st_index, -1, sizeof(st_index));
3684 ✗ is->eof = 0;
3685
3686 ✗ pkt = av_packet_alloc();
3687 ✗ if (!pkt) {
3688 ✗ av_log(NULL, AV_LOG_FATAL, "Could not allocate packet.\n");
3689 ✗ ret = AVERROR(ENOMEM);
3690 ✗ goto fail;
3691 }
3692 ✗ ic = avformat_alloc_context();
3693 ✗ if (!ic) {
3694 ✗ av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");
3695 ✗ ret = AVERROR(ENOMEM);
3696 ✗ goto fail;
3697 }
3698 ✗ ic->opaque = av_mallocz(sizeof(FormatContext));
3699 ✗ if (!ic->opaque) {
3700 ✗ av_log(NULL, AV_LOG_FATAL, "Could not allocate internal context.\n");
3701 ✗ ret = AVERROR(ENOMEM);
3702 ✗ goto fail;
3703 }
3704 ✗ ic->interrupt_callback.callback = decode_interrupt_cb;
3705 ✗ ic->interrupt_callback.opaque = is;
3706 ✗ if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
3707 ✗ av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
3708 ✗ scan_all_pmts_set = 1;
3709 }
3710 ✗ err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
3711 ✗ if (err < 0) {
3712 ✗ print_error(is->filename, err);
3713 ✗ ret = -1;
3714 ✗ goto fail;
3715 }
3716 ✗ if (scan_all_pmts_set)
3717 ✗ av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
3718 ✗ remove_avoptions(&format_opts, codec_opts);
3719
3720 ✗ ret = check_avoptions(format_opts);
3721 ✗ if (ret < 0)
3722 ✗ goto fail;
3723 ✗ is->ic = ic;
3724
3725 ✗ if (genpts)
3726 ✗ ic->flags |= AVFMT_FLAG_GENPTS;
3727
3728 ✗ if (find_stream_info) {
3729 AVDictionary **opts;
3730 ✗ int orig_nb_streams = ic->nb_streams;
3731
3732 ✗ err = setup_find_stream_info_opts(ic, codec_opts, &opts);
3733 ✗ if (err < 0) {
3734 ✗ av_log(NULL, AV_LOG_ERROR,
3735 "Error setting up avformat_find_stream_info() options\n");
3736 ✗ ret = err;
3737 ✗ goto fail;
3738 }
3739
3740 ✗ err = avformat_find_stream_info(ic, opts);
3741
3742 ✗ for (i = 0; i < orig_nb_streams; i++)
3743 ✗ av_dict_free(&opts[i]);
3744 ✗ av_freep(&opts);
3745
3746 ✗ if (err < 0) {
3747 ✗ av_log(NULL, AV_LOG_WARNING,
3748 "%s: could not find codec parameters\n", is->filename);
3749 ✗ ret = -1;
3750 ✗ goto fail;
3751 }
3752 }
3753
3754 ✗ if (ic->pb)
3755 ✗ ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use avio_feof() to test for the end
3756
3757 ✗ if (seek_by_bytes < 0)
3758 ✗ seek_by_bytes = !(ic->iformat->flags & AVFMT_NO_BYTE_SEEK) &&
3759 ✗ !!(ic->iformat->flags & AVFMT_TS_DISCONT) &&
3760 ✗ strcmp("ogg", ic->iformat->name);
3761
3762 ✗ is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0;
3763
3764 ✗ if (!window_title && (t = av_dict_get(ic->metadata, "title", NULL, 0)))
3765 ✗ window_title = av_asprintf("%s - %s", t->value, input_filename);
3766
3767 /* if seeking requested, we execute it */
3768 ✗ if (start_time != AV_NOPTS_VALUE) {
3769 int64_t timestamp;
3770
3771 ✗ timestamp = start_time;
3772 /* add the stream start time */
3773 ✗ if (ic->start_time != AV_NOPTS_VALUE)
3774 ✗ timestamp += ic->start_time;
3775 ✗ ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);
3776 ✗ if (ret < 0) {
3777 ✗ av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",
3778 ✗ is->filename, (double)timestamp / AV_TIME_BASE);
3779 }
3780 }
3781
3782 ✗ is->realtime = is_realtime(ic);
3783
3784 ✗ if (show_status) {
3785 ✗ fprintf(stderr, "\x1b[2K\r");
3786 ✗ av_dump_format(ic, 0, is->filename, 0);
3787 }
3788
3789 ✗ ici = ic->opaque;
3790 ✗ ici->streams = av_calloc(ic->nb_streams, sizeof(*ici->streams));
3791 ✗ if (!ici->streams) {
3792 ✗ av_log(NULL, AV_LOG_FATAL, "Could not allocate internal streams context.\n");
3793 ✗ ret = AVERROR(ENOMEM);
3794 ✗ goto fail;
3795 }
3796 ✗ ici->nb_streams = ic->nb_streams;
3797 ✗ for (i = 0; i < ic->nb_streams; i++) {
3798 ✗ AVStream *st = ic->streams[i];
3799 ✗ enum AVMediaType type = st->codecpar->codec_type;
3800 ✗ ici->streams[i] = av_mallocz(sizeof(Stream));
3801 ✗ if (!ici->streams[i]) {
3802 ✗ av_log(NULL, AV_LOG_FATAL, "Could not allocate internal stream context.\n");
3803 ✗ ret = AVERROR(ENOMEM);
3804 ✗ goto fail;
3805 }
3806 ✗ st->discard = AVDISCARD_ALL;
3807 ✗ if (type >= 0 && wanted_stream_spec[type] && st_index[type] == -1)
3808 ✗ if (avformat_match_stream_specifier(ic, st, wanted_stream_spec[type]) > 0)
3809 ✗ st_index[type] = i;
3810 // Clear all pre-existing metadata update flags to avoid printing
3811 // initial metadata as update.
3812 ✗ st->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
3813 }
3814 ✗ ici->stream_groups = av_calloc(ic->nb_stream_groups, sizeof(*ici->stream_groups));
3815 ✗ if (!ici->stream_groups) {
3816 ✗ av_log(NULL, AV_LOG_FATAL, "Could not allocate internal stream groups context.\n");
3817 ✗ ret = AVERROR(ENOMEM);
3818 ✗ goto fail;
3819 }
3820 ✗ ici->nb_stream_groups = ic->nb_stream_groups;
3821 ✗ for (i = 0; i < ic->nb_stream_groups; i++) {
3822 ✗ AVStreamGroup *stg = ic->stream_groups[i];
3823
3824 ✗ ici->stream_groups[i] = av_mallocz(sizeof(*ici->stream_groups[i]));
3825 ✗ if (!ici->stream_groups[i]) {
3826 ✗ av_log(NULL, AV_LOG_FATAL, "Could not allocate internal stream group.\n");
3827 ✗ ret = AVERROR(ENOMEM);
3828 ✗ goto fail;
3829 }
3830 ✗ ici->stream_groups[i]->stg = stg;
3831
3832 ✗ if (stg->type != AV_STREAM_GROUP_PARAMS_LCEVC)
3833 ✗ continue;
3834 ✗ if (!av_bsf_get_by_name("lcevc_merge") || stg->nb_streams != 2)
3835 ✗ continue;
3836
3837 ✗ const AVStreamGroupLayeredVideo *lcevc = stg->params.layered_video;
3838 ✗ const AVStream *base_st = ic->streams[stg->streams[!lcevc->el_index]->index];
3839 ✗ Stream *base_sti = ici->streams[base_st->index];
3840
3841 ✗ base_sti->group = ici->stream_groups[i];
3842 }
3843 ✗ ic->event_flags &= ~AVFMT_EVENT_FLAG_METADATA_UPDATED;
3844 ✗ for (i = 0; i < AVMEDIA_TYPE_NB; i++) {
3845 ✗ if (wanted_stream_spec[i] && st_index[i] == -1) {
3846 ✗ av_log(NULL, AV_LOG_ERROR, "Stream specifier %s does not match any %s stream\n", wanted_stream_spec[i], av_get_media_type_string(i));
3847 ✗ st_index[i] = INT_MAX;
3848 }
3849 }
3850
3851 ✗ if (!video_disable)
3852 ✗ st_index[AVMEDIA_TYPE_VIDEO] =
3853 ✗ av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
3854 st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
3855 ✗ if (!audio_disable)
3856 ✗ st_index[AVMEDIA_TYPE_AUDIO] =
3857 ✗ av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,
3858 st_index[AVMEDIA_TYPE_AUDIO],
3859 st_index[AVMEDIA_TYPE_VIDEO],
3860 NULL, 0);
3861 ✗ if (!video_disable && !subtitle_disable)
3862 ✗ st_index[AVMEDIA_TYPE_SUBTITLE] =
3863 ✗ av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE,
3864 st_index[AVMEDIA_TYPE_SUBTITLE],
3865 ✗ (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ?
3866 st_index[AVMEDIA_TYPE_AUDIO] :
3867 st_index[AVMEDIA_TYPE_VIDEO]),
3868 NULL, 0);
3869
3870 ✗ is->show_mode = show_mode;
3871 ✗ if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
3872 ✗ AVStream *st = ic->streams[st_index[AVMEDIA_TYPE_VIDEO]];
3873 ✗ AVCodecParameters *codecpar = st->codecpar;
3874 ✗ AVRational sar = av_guess_sample_aspect_ratio(ic, st, NULL);
3875 ✗ if (codecpar->width)
3876 ✗ set_default_window_size(codecpar->width, codecpar->height, sar);
3877 }
3878
3879 /* open the streams */
3880 ✗ if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
3881 ✗ stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
3882 }
3883
3884 ✗ ret = -1;
3885 ✗ if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
3886 ✗ ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
3887 }
3888 ✗ if (is->show_mode == SHOW_MODE_NONE)
3889 ✗ is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT;
3890
3891 ✗ if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
3892 ✗ stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);
3893 }
3894
3895 ✗ if (is->video_stream < 0 && is->audio_stream < 0) {
3896 ✗ av_log(NULL, AV_LOG_FATAL, "Failed to open file '%s' or configure filtergraph\n",
3897 is->filename);
3898 ✗ ret = -1;
3899 ✗ goto fail;
3900 }
3901
3902 ✗ if (infinite_buffer < 0 && is->realtime)
3903 ✗ infinite_buffer = 1;
3904
3905 for (;;) {
3906 ✗ if (is->abort_request)
3907 ✗ break;
3908 ✗ if (is->paused != is->last_paused) {
3909 ✗ is->last_paused = is->paused;
3910 ✗ if (is->paused)
3911 ✗ is->read_pause_return = av_read_pause(ic);
3912 else
3913 ✗ av_read_play(ic);
3914 }
3915 #if CONFIG_RTSP_DEMUXER || CONFIG_MMSH_PROTOCOL
3916 ✗ if (is->paused &&
3917 ✗ (!strcmp(ic->iformat->name, "rtsp") ||
3918 ✗ (ic->pb && !strncmp(input_filename, "mmsh:", 5)))) {
3919 /* wait 10 ms to avoid trying to get another packet */
3920 /* XXX: horrible */
3921 ✗ SDL_Delay(10);
3922 ✗ continue;
3923 }
3924 #endif
3925 ✗ if (is->seek_req) {
3926 ✗ int64_t seek_target = is->seek_pos;
3927 ✗ int64_t seek_min = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;
3928 ✗ int64_t seek_max = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX;
3929 // FIXME the +-2 is due to rounding being not done in the correct direction in generation
3930 // of the seek_pos/seek_rel variables
3931
3932 ✗ ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
3933 ✗ if (ret < 0) {
3934 ✗ av_log(NULL, AV_LOG_ERROR,
3935 ✗ "%s: error while seeking\n", is->ic->url);
3936 } else {
3937 ✗ if (is->audio_stream >= 0)
3938 ✗ packet_queue_flush(&is->audioq);
3939 ✗ if (is->subtitle_stream >= 0)
3940 ✗ packet_queue_flush(&is->subtitleq);
3941 ✗ if (is->video_stream >= 0) {
3942 ✗ packet_queue_flush(&is->videoq);
3943 ✗ uninit_bsf_graph(is->ic, is->video_stream);
3944 ✗ if (ici->streams[is->video_stream]->group) {
3945 ✗ if ((ret = init_lcevc_graph(is->ic, is->video_stream)) < 0)
3946 ✗ goto fail;
3947 }
3948 }
3949 ✗ if (is->seek_flags & AVSEEK_FLAG_BYTE) {
3950 ✗ set_clock(&is->extclk, NAN, 0);
3951 } else {
3952 ✗ set_clock(&is->extclk, seek_target / (double)AV_TIME_BASE, 0);
3953 }
3954 }
3955 ✗ is->seek_req = 0;
3956 ✗ is->queue_attachments_req = 1;
3957 ✗ is->eof = 0;
3958 ✗ if (is->paused)
3959 ✗ step_to_next_frame(is);
3960 }
3961 ✗ if (is->queue_attachments_req) {
3962 ✗ if (is->video_st && is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC) {
3963 ✗ if ((ret = av_packet_ref(pkt, &is->video_st->attached_pic)) < 0)
3964 ✗ goto fail;
3965 ✗ packet_queue_put(&is->videoq, pkt);
3966 ✗ packet_queue_put_nullpacket(&is->videoq, pkt, is->video_stream);
3967 }
3968 ✗ is->queue_attachments_req = 0;
3969 }
3970
3971 /* if the queue are full, no need to read more */
3972 ✗ if (infinite_buffer<1 &&
3973 ✗ (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE
3974 ✗ || (stream_has_enough_packets(is->audio_st, is->audio_stream, &is->audioq) &&
3975 ✗ stream_has_enough_packets(is->video_st, is->video_stream, &is->videoq) &&
3976 ✗ stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq)))) {
3977 /* wait 10 ms */
3978 ✗ SDL_LockMutex(wait_mutex);
3979 ✗ SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
3980 ✗ SDL_UnlockMutex(wait_mutex);
3981 ✗ continue;
3982 }
3983 ✗ if (!is->paused &&
3984 ✗ (!is->audio_st || (is->auddec.finished == is->audioq.serial && frame_queue_nb_remaining(&is->sampq) == 0)) &&
3985 ✗ (!is->video_st || (is->viddec.finished == is->videoq.serial && frame_queue_nb_remaining(&is->pictq) == 0))) {
3986 ✗ if (loop != 1 && (!loop || --loop)) {
3987 ✗ stream_seek(is, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0);
3988 ✗ } else if (autoexit) {
3989 ✗ ret = AVERROR_EOF;
3990 ✗ goto fail;
3991 }
3992 }
3993 ✗ ret = av_read_frame(ic, pkt);
3994 ✗ if (ret < 0) {
3995 ✗ if ((ret == AVERROR_EOF || avio_feof(ic->pb)) && !is->eof) {
3996 ✗ if (is->video_stream >= 0) {
3997 ✗ ret = do_bsf_flush(ic, is);
3998 ✗ if (ret < 0)
3999 ✗ goto fail;
4000 ✗ packet_queue_put_nullpacket(&is->videoq, pkt, is->video_stream);
4001 }
4002 ✗ if (is->audio_stream >= 0)
4003 ✗ packet_queue_put_nullpacket(&is->audioq, pkt, is->audio_stream);
4004 ✗ if (is->subtitle_stream >= 0)
4005 ✗ packet_queue_put_nullpacket(&is->subtitleq, pkt, is->subtitle_stream);
4006 ✗ is->eof = 1;
4007 }
4008 ✗ if (ic->pb && ic->pb->error) {
4009 ✗ if (autoexit)
4010 ✗ goto fail;
4011 else
4012 ✗ break;
4013 }
4014 ✗ SDL_LockMutex(wait_mutex);
4015 ✗ SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
4016 ✗ SDL_UnlockMutex(wait_mutex);
4017 ✗ continue;
4018 } else {
4019 ✗ is->eof = 0;
4020 }
4021
4022 ✗ if (show_status) {
4023 ✗ if (ic->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
4024 ✗ fprintf(stderr, "\x1b[2K\r");
4025 ✗ dump_dictionary(NULL, ic->metadata,
4026 "\r New metadata", " ", AV_LOG_INFO);
4027 }
4028 ✗ if (ic->streams[pkt->stream_index]->event_flags &
4029 AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
4030 ✗ fprintf(stderr, "\x1b[2K\r");
4031 ✗ snprintf(metadata_description,
4032 sizeof(metadata_description),
4033 "\r New metadata for stream %d",
4034 ✗ pkt->stream_index);
4035 ✗ dump_dictionary(NULL, ic->streams[pkt->stream_index]->metadata,
4036 metadata_description, " ", AV_LOG_INFO);
4037 }
4038 }
4039 ✗ ic->event_flags &= ~AVFMT_EVENT_FLAG_METADATA_UPDATED;
4040 ✗ ic->streams[pkt->stream_index]->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
4041
4042 /* check if packet is in play range specified by user, then queue, otherwise discard */
4043 ✗ stream_start_time = ic->streams[pkt->stream_index]->start_time;
4044 ✗ pkt_ts = pkt->pts == AV_NOPTS_VALUE ? pkt->dts : pkt->pts;
4045 ✗ pkt_in_play_range = duration == AV_NOPTS_VALUE ||
4046 ✗ (pkt_ts - (stream_start_time != AV_NOPTS_VALUE ? stream_start_time : 0)) *
4047 ✗ av_q2d(ic->streams[pkt->stream_index]->time_base) -
4048 ✗ (double)(start_time != AV_NOPTS_VALUE ? start_time : 0) / 1000000
4049 ✗ <= ((double)duration / 1000000);
4050 ✗ if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
4051 ✗ packet_queue_put(&is->audioq, pkt);
4052 ✗ } else if (pkt->stream_index == is->video_stream && pkt_in_play_range
4053 ✗ && !(is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
4054 ✗ Stream *sti = ici->streams[is->video_stream];
4055 ✗ StreamGroup *stgi = sti->group;
4056 ✗ if (stgi && stgi->graph) {
4057 ✗ ret = do_bsf_graph(ic, is, sti, pkt);
4058 ✗ if (ret < 0)
4059 ✗ goto fail;
4060 } else
4061 ✗ packet_queue_put(&is->videoq, pkt);
4062 ✗ } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
4063 ✗ packet_queue_put(&is->subtitleq, pkt);
4064 } else {
4065 ✗ Stream *sti = ici->streams[pkt->stream_index];
4066 ✗ StreamGroup *stgi = sti->group;
4067 ✗ if (stgi && stgi->graph) {
4068 ✗ ret = do_bsf_graph(ic, is, sti, pkt);
4069 ✗ if (ret < 0)
4070 ✗ goto fail;
4071 }
4072 ✗ av_packet_unref(pkt);
4073 }
4074 }
4075
4076 ✗ ret = 0;
4077 ✗ fail:
4078 ✗ if (ic && !is->ic) {
4079 ✗ av_freep(&ic->opaque);
4080 ✗ avformat_close_input(&ic);
4081 }
4082
4083 ✗ av_packet_free(&pkt);
4084 ✗ if (ret != 0) {
4085 SDL_Event event;
4086
4087 ✗ event.type = FF_QUIT_EVENT;
4088 ✗ event.user.data1 = is;
4089 ✗ SDL_PushEvent(&event);
4090 }
4091 ✗ SDL_DestroyMutex(wait_mutex);
4092 ✗ return 0;
4093 }
4094
4095 ✗ static VideoState *stream_open(const char *filename,
4096 const AVInputFormat *iformat)
4097 {
4098 VideoState *is;
4099
4100 ✗ is = av_mallocz(sizeof(VideoState));
4101 ✗ if (!is)
4102 ✗ return NULL;
4103 ✗ is->last_video_stream = is->video_stream = -1;
4104 ✗ is->last_audio_stream = is->audio_stream = -1;
4105 ✗ is->last_subtitle_stream = is->subtitle_stream = -1;
4106 ✗ is->filename = av_strdup(filename);
4107 ✗ if (!is->filename)
4108 ✗ goto fail;
4109 ✗ is->iformat = iformat;
4110 ✗ is->ytop = 0;
4111 ✗ is->xleft = 0;
4112
4113 /* start video display */
4114 ✗ if (frame_queue_init(&is->pictq, &is->videoq, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0)
4115 ✗ goto fail;
4116 ✗ if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)
4117 ✗ goto fail;
4118 ✗ if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)
4119 ✗ goto fail;
4120
4121 ✗ if (packet_queue_init(&is->videoq) < 0 ||
4122 ✗ packet_queue_init(&is->audioq) < 0 ||
4123 ✗ packet_queue_init(&is->subtitleq) < 0)
4124 ✗ goto fail;
4125
4126 ✗ if (!(is->continue_read_thread = SDL_CreateCond())) {
4127 ✗ av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
4128 ✗ goto fail;
4129 }
4130
4131 ✗ init_clock(&is->vidclk, &is->videoq.serial);
4132 ✗ init_clock(&is->audclk, &is->audioq.serial);
4133 ✗ init_clock(&is->extclk, &is->extclk.serial);
4134 ✗ is->audio_clock_serial = -1;
4135 ✗ if (startup_volume < 0)
4136 ✗ av_log(NULL, AV_LOG_WARNING, "-volume=%d < 0, setting to 0\n", startup_volume);
4137 ✗ if (startup_volume > 100)
4138 ✗ av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", startup_volume);
4139 ✗ if (video_background) {
4140 ✗ if (!strcmp(video_background, "none")) {
4141 ✗ is->render_params.video_background_type = VIDEO_BACKGROUND_NONE;
4142 ✗ } else if (strcmp(video_background, "tiles")) {
4143 ✗ if (av_parse_color(is->render_params.video_background_color, video_background, -1, NULL) >= 0)
4144 ✗ is->render_params.video_background_type = VIDEO_BACKGROUND_COLOR;
4145 else
4146 ✗ goto fail;
4147 }
4148 }
4149 ✗ startup_volume = av_clip(startup_volume, 0, 100);
4150 ✗ startup_volume = av_clip(SDL_MIX_MAXVOLUME * startup_volume / 100, 0, SDL_MIX_MAXVOLUME);
4151 ✗ is->audio_volume = startup_volume;
4152 ✗ is->muted = 0;
4153 ✗ is->av_sync_type = av_sync_type;
4154 ✗ is->read_tid = SDL_CreateThread(read_thread, "read_thread", is);
4155 ✗ if (!is->read_tid) {
4156 ✗ av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %s\n", SDL_GetError());
4157 ✗ fail:
4158 ✗ stream_close(is);
4159 ✗ return NULL;
4160 }
4161 ✗ return is;
4162 }
4163
4164 ✗ static void stream_cycle_channel(VideoState *is, int codec_type)
4165 {
4166 ✗ AVFormatContext *ic = is->ic;
4167 int start_index, stream_index;
4168 int old_index;
4169 AVStream *st;
4170 ✗ AVProgram *p = NULL;
4171 ✗ int nb_streams = is->ic->nb_streams;
4172
4173 ✗ if (codec_type == AVMEDIA_TYPE_VIDEO) {
4174 ✗ start_index = is->last_video_stream;
4175 ✗ old_index = is->video_stream;
4176 ✗ } else if (codec_type == AVMEDIA_TYPE_AUDIO) {
4177 ✗ start_index = is->last_audio_stream;
4178 ✗ old_index = is->audio_stream;
4179 } else {
4180 ✗ start_index = is->last_subtitle_stream;
4181 ✗ old_index = is->subtitle_stream;
4182 }
4183 ✗ stream_index = start_index;
4184
4185 ✗ if (codec_type != AVMEDIA_TYPE_VIDEO && is->video_stream != -1) {
4186 ✗ p = av_find_program_from_stream(ic, NULL, is->video_stream);
4187 ✗ if (p) {
4188 ✗ nb_streams = p->nb_stream_indexes;
4189 ✗ for (start_index = 0; start_index < nb_streams; start_index++)
4190 ✗ if (p->stream_index[start_index] == stream_index)
4191 ✗ break;
4192 ✗ if (start_index == nb_streams)
4193 ✗ start_index = -1;
4194 ✗ stream_index = start_index;
4195 }
4196 }
4197
4198 for (;;) {
4199 ✗ if (++stream_index >= nb_streams)
4200 {
4201 ✗ if (codec_type == AVMEDIA_TYPE_SUBTITLE)
4202 {
4203 ✗ stream_index = -1;
4204 ✗ is->last_subtitle_stream = -1;
4205 ✗ goto the_end;
4206 }
4207 ✗ if (start_index == -1)
4208 ✗ return;
4209 ✗ stream_index = 0;
4210 }
4211 ✗ if (stream_index == start_index)
4212 ✗ return;
4213 ✗ st = is->ic->streams[p ? p->stream_index[stream_index] : stream_index];
4214 ✗ if (st->codecpar->codec_type == codec_type) {
4215 /* check that parameters are OK */
4216 ✗ switch (codec_type) {
4217 ✗ case AVMEDIA_TYPE_AUDIO:
4218 ✗ if (st->codecpar->sample_rate != 0 &&
4219 ✗ st->codecpar->ch_layout.nb_channels != 0)
4220 ✗ goto the_end;
4221 ✗ break;
4222 ✗ case AVMEDIA_TYPE_VIDEO:
4223 case AVMEDIA_TYPE_SUBTITLE:
4224 ✗ goto the_end;
4225 ✗ default:
4226 ✗ break;
4227 }
4228 }
4229 }
4230 ✗ the_end:
4231 ✗ if (p && stream_index != -1)
4232 ✗ stream_index = p->stream_index[stream_index];
4233 ✗ av_log(NULL, AV_LOG_INFO, "Switch %s stream from #%d to #%d\n",
4234 av_get_media_type_string(codec_type),
4235 old_index,
4236 stream_index);
4237
4238 ✗ stream_component_close(is, old_index);
4239 ✗ stream_component_open(is, stream_index);
4240 }
4241
4242
4243 ✗ static void toggle_full_screen(VideoState *is)
4244 {
4245 ✗ is_full_screen = !is_full_screen;
4246 ✗ SDL_SetWindowFullscreen(window, is_full_screen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
4247 ✗ }
4248
4249 ✗ static void toggle_audio_display(VideoState *is)
4250 {
4251 ✗ int next = is->show_mode;
4252 do {
4253 ✗ next = (next + 1) % SHOW_MODE_NB;
4254 ✗ } while (next != is->show_mode && (next == SHOW_MODE_VIDEO && !is->video_st || next != SHOW_MODE_VIDEO && !is->audio_st));
4255 ✗ if (is->show_mode != next) {
4256 ✗ is->force_refresh = 1;
4257 ✗ is->show_mode = next;
4258 }
4259 ✗ }
4260
4261 ✗ static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) {
4262 ✗ double remaining_time = 0.0;
4263 ✗ SDL_PumpEvents();
4264 ✗ while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
4265 ✗ if (received_sigterm) {
4266 ✗ exit_status = 123;
4267 ✗ do_exit(is);
4268 }
4269 ✗ if (!cursor_hidden && av_gettime_relative() - cursor_last_shown > CURSOR_HIDE_DELAY) {
4270 ✗ SDL_ShowCursor(0);
4271 ✗ cursor_hidden = 1;
4272 }
4273 ✗ if (remaining_time > 0.0)
4274 ✗ av_usleep((int64_t)(remaining_time * 1000000.0));
4275 ✗ remaining_time = REFRESH_RATE;
4276 ✗ if (is->chapter_render_pending || (chapter_list_visible(is) && current_chapter(is) != is->chapter_playing))
4277 ✗ chapter_list_render(is);
4278 ✗ if (chapter_list_alpha(is, av_gettime_relative()) != is->chapter_drawn_alpha)
4279 ✗ is->force_refresh = 1;
4280 ✗ if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh))
4281 ✗ video_refresh(is, &remaining_time);
4282 ✗ SDL_PumpEvents();
4283 }
4284 ✗ }
4285
4286 /* index of the chapter containing the current playback position, -1 before the first one */
4287 ✗ static int current_chapter(VideoState *is)
4288 {
4289 ✗ int64_t pos = get_master_clock(is) * AV_TIME_BASE;
4290 int i;
4291
4292 ✗ for (i = 0; i < is->ic->nb_chapters; i++) {
4293 ✗ AVChapter *ch = is->ic->chapters[i];
4294 ✗ if (av_compare_ts(pos, AV_TIME_BASE_Q, ch->start, ch->time_base) < 0)
4295 ✗ break;
4296 }
4297 ✗ return i - 1;
4298 }
4299
4300 ✗ static void seek_chapter(VideoState *is, int i)
4301 {
4302 ✗ i = FFMAX(i, 0);
4303 ✗ chapter_list_show(is, FFMIN(i, is->ic->nb_chapters - 1));
4304 ✗ if (i >= is->ic->nb_chapters)
4305 ✗ return;
4306
4307 ✗ av_log(NULL, AV_LOG_VERBOSE, "Seeking to chapter %d.\n", i);
4308 ✗ stream_seek(is, av_rescale_q(is->ic->chapters[i]->start, is->ic->chapters[i]->time_base,
4309 ✗ AV_TIME_BASE_Q), 0, 0);
4310 }
4311
4312 /* handle an event sent by the GUI */
4313 ✗ static void event_loop(VideoState *cur_stream)
4314 {
4315 SDL_Event event;
4316 double incr, pos, frac;
4317
4318 ✗ for (;;) {
4319 double x;
4320 ✗ refresh_loop_wait_event(cur_stream, &event);
4321 ✗ switch (event.type) {
4322 ✗ case SDL_KEYDOWN:
4323 ✗ if (exit_on_keydown || (!cur_stream->chapter_pinned &&
4324 ✗ (event.key.keysym.sym == SDLK_ESCAPE || event.key.keysym.sym == SDLK_q))) {
4325 ✗ do_exit(cur_stream);
4326 ✗ break;
4327 }
4328 // If we don't yet have a window, skip all key events, because read_thread might still be initializing...
4329 ✗ if (!cur_stream->width)
4330 ✗ continue;
4331 ✗ if (cur_stream->chapter_pinned) {
4332 ✗ chapter_list_key(cur_stream, event.key.keysym.sym);
4333 ✗ break;
4334 }
4335 ✗ switch (event.key.keysym.sym) {
4336 ✗ case SDLK_f:
4337 ✗ toggle_full_screen(cur_stream);
4338 ✗ cur_stream->force_refresh = 1;
4339 ✗ break;
4340 ✗ case SDLK_p:
4341 case SDLK_SPACE:
4342 ✗ toggle_pause(cur_stream);
4343 ✗ break;
4344 ✗ case SDLK_m:
4345 ✗ toggle_mute(cur_stream);
4346 ✗ break;
4347 ✗ case SDLK_KP_MULTIPLY:
4348 case SDLK_0:
4349 ✗ update_volume(cur_stream, 1, SDL_VOLUME_STEP);
4350 ✗ break;
4351 ✗ case SDLK_KP_DIVIDE:
4352 case SDLK_9:
4353 ✗ update_volume(cur_stream, -1, SDL_VOLUME_STEP);
4354 ✗ break;
4355 ✗ case SDLK_s: // S: Step to next frame
4356 ✗ step_to_next_frame(cur_stream);
4357 ✗ break;
4358 ✗ case SDLK_a:
4359 ✗ stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);
4360 ✗ break;
4361 ✗ case SDLK_v:
4362 ✗ stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);
4363 ✗ break;
4364 ✗ case SDLK_c:
4365 ✗ stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);
4366 ✗ stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);
4367 ✗ stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
4368 ✗ break;
4369 ✗ case SDLK_t:
4370 ✗ stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
4371 ✗ break;
4372 ✗ case SDLK_l:
4373 ✗ cur_stream->chapter_pin_requested = cur_stream->ic->nb_chapters > 0;
4374 ✗ break;
4375 ✗ case SDLK_RETURN:
4376 case SDLK_KP_ENTER:
4377 ✗ if (chapter_list_visible(cur_stream) && cur_stream->nb_chapter_rows)
4378 ✗ seek_chapter(cur_stream, cur_stream->chapter_rows[cur_stream->chapter_selected].index);
4379 ✗ break;
4380 ✗ case SDLK_w:
4381 ✗ if (cur_stream->show_mode == SHOW_MODE_VIDEO && cur_stream->vfilter_idx < nb_vfilters - 1) {
4382 ✗ if (++cur_stream->vfilter_idx >= nb_vfilters)
4383 ✗ cur_stream->vfilter_idx = 0;
4384 } else {
4385 ✗ cur_stream->vfilter_idx = 0;
4386 ✗ toggle_audio_display(cur_stream);
4387 }
4388 ✗ break;
4389 ✗ case SDLK_PAGEUP:
4390 ✗ if (cur_stream->ic->nb_chapters <= 1) {
4391 ✗ incr = 600.0;
4392 ✗ goto do_seek;
4393 }
4394 ✗ seek_chapter(cur_stream, current_chapter(cur_stream) + 1);
4395 ✗ break;
4396 ✗ case SDLK_PAGEDOWN:
4397 ✗ if (cur_stream->ic->nb_chapters <= 1) {
4398 ✗ incr = -600.0;
4399 ✗ goto do_seek;
4400 }
4401 ✗ seek_chapter(cur_stream, current_chapter(cur_stream) - 1);
4402 ✗ break;
4403 ✗ case SDLK_LEFT:
4404 ✗ incr = seek_interval ? -seek_interval : -10.0;
4405 ✗ goto do_seek;
4406 ✗ case SDLK_RIGHT:
4407 ✗ incr = seek_interval ? seek_interval : 10.0;
4408 ✗ goto do_seek;
4409 ✗ case SDLK_UP:
4410 ✗ incr = 60.0;
4411 ✗ goto do_seek;
4412 ✗ case SDLK_DOWN:
4413 ✗ incr = -60.0;
4414 ✗ do_seek:
4415 ✗ if (seek_by_bytes) {
4416 ✗ pos = -1;
4417 ✗ if (pos < 0 && cur_stream->video_stream >= 0)
4418 ✗ pos = frame_queue_last_pos(&cur_stream->pictq);
4419 ✗ if (pos < 0 && cur_stream->audio_stream >= 0)
4420 ✗ pos = frame_queue_last_pos(&cur_stream->sampq);
4421 ✗ if (pos < 0)
4422 ✗ pos = avio_tell(cur_stream->ic->pb);
4423 ✗ if (cur_stream->ic->bit_rate)
4424 ✗ incr *= cur_stream->ic->bit_rate / 8.0;
4425 else
4426 ✗ incr *= 180000.0;
4427 ✗ pos += incr;
4428 ✗ stream_seek(cur_stream, pos, incr, 1);
4429 } else {
4430 ✗ pos = get_master_clock(cur_stream);
4431 ✗ if (isnan(pos))
4432 ✗ pos = (double)cur_stream->seek_pos / AV_TIME_BASE;
4433 ✗ pos += incr;
4434 ✗ if (cur_stream->ic->start_time != AV_NOPTS_VALUE && pos < cur_stream->ic->start_time / (double)AV_TIME_BASE)
4435 ✗ pos = cur_stream->ic->start_time / (double)AV_TIME_BASE;
4436 ✗ stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0);
4437 }
4438 ✗ break;
4439 ✗ default:
4440 ✗ break;
4441 }
4442 ✗ break;
4443 ✗ case SDL_KEYUP:
4444 /* on the release of a press made while unpinned, so that the key's own text input does not start the search */
4445 ✗ if (event.key.keysym.sym == SDLK_l && cur_stream->chapter_pin_requested)
4446 ✗ chapter_list_pin(cur_stream, 1);
4447 ✗ cur_stream->chapter_pin_requested = 0;
4448 ✗ break;
4449 ✗ case SDL_TEXTINPUT:
4450 ✗ if (cur_stream->chapter_pinned)
4451 ✗ chapter_list_search(cur_stream, event.text.text);
4452 ✗ break;
4453 ✗ case SDL_MOUSEWHEEL:
4454 ✗ if (chapter_list_visible(cur_stream))
4455 ✗ chapter_list_move(cur_stream, -event.wheel.y);
4456 ✗ break;
4457 ✗ case SDL_MOUSEBUTTONDOWN:
4458 ✗ if (exit_on_mousedown) {
4459 ✗ do_exit(cur_stream);
4460 ✗ break;
4461 }
4462 ✗ if (event.button.button == SDL_BUTTON_LEFT && chapter_list_visible(cur_stream) &&
4463 ✗ chapter_list_click(cur_stream, event.button.x, event.button.y))
4464 ✗ break;
4465 ✗ if (event.button.button == SDL_BUTTON_LEFT) {
4466 static int64_t last_mouse_left_click = 0;
4467 ✗ if (av_gettime_relative() - last_mouse_left_click <= 500000) {
4468 ✗ toggle_full_screen(cur_stream);
4469 ✗ cur_stream->force_refresh = 1;
4470 ✗ last_mouse_left_click = 0;
4471 } else {
4472 ✗ last_mouse_left_click = av_gettime_relative();
4473 }
4474 }
4475 av_fallthrough;
4476 case SDL_MOUSEMOTION:
4477 ✗ if (cursor_hidden) {
4478 ✗ SDL_ShowCursor(1);
4479 ✗ cursor_hidden = 0;
4480 }
4481 ✗ cursor_last_shown = av_gettime_relative();
4482 ✗ if (event.type == SDL_MOUSEMOTION)
4483 ✗ chapter_list_hover(cur_stream, event.motion.x, event.motion.y);
4484 ✗ if (event.type == SDL_MOUSEBUTTONDOWN) {
4485 ✗ if (event.button.button != SDL_BUTTON_RIGHT)
4486 ✗ break;
4487 ✗ x = event.button.x;
4488 } else {
4489 ✗ if (!(event.motion.state & SDL_BUTTON_RMASK))
4490 ✗ break;
4491 ✗ x = event.motion.x;
4492 }
4493 ✗ if (seek_by_bytes || cur_stream->ic->duration <= 0) {
4494 ✗ uint64_t size = avio_size(cur_stream->ic->pb);
4495 ✗ stream_seek(cur_stream, size*x/cur_stream->width, 0, 1);
4496 } else {
4497 int64_t ts;
4498 int ns, hh, mm, ss;
4499 int tns, thh, tmm, tss;
4500 ✗ tns = cur_stream->ic->duration / 1000000LL;
4501 ✗ thh = tns / 3600;
4502 ✗ tmm = (tns % 3600) / 60;
4503 ✗ tss = (tns % 60);
4504 ✗ frac = x / cur_stream->width;
4505 ✗ ns = frac * tns;
4506 ✗ hh = ns / 3600;
4507 ✗ mm = (ns % 3600) / 60;
4508 ✗ ss = (ns % 60);
4509 ✗ av_log(NULL, AV_LOG_INFO,
4510 "Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d) \n", frac*100,
4511 hh, mm, ss, thh, tmm, tss);
4512 ✗ ts = frac * cur_stream->ic->duration;
4513 ✗ if (cur_stream->ic->start_time != AV_NOPTS_VALUE)
4514 ✗ ts += cur_stream->ic->start_time;
4515 ✗ stream_seek(cur_stream, ts, 0, 0);
4516 }
4517 ✗ break;
4518 ✗ case SDL_WINDOWEVENT:
4519 ✗ switch (event.window.event) {
4520 ✗ case SDL_WINDOWEVENT_LEAVE:
4521 ✗ chapter_list_hover(cur_stream, -1, -1);
4522 ✗ break;
4523 ✗ case SDL_WINDOWEVENT_SIZE_CHANGED:
4524 ✗ screen_width = cur_stream->width = event.window.data1;
4525 ✗ screen_height = cur_stream->height = event.window.data2;
4526 ✗ if (cur_stream->vis_texture) {
4527 ✗ SDL_DestroyTexture(cur_stream->vis_texture);
4528 ✗ cur_stream->vis_texture = NULL;
4529 }
4530 ✗ avfilter_graph_free(&cur_stream->chapter_graph);
4531 ✗ if (cur_stream->chapter_texture)
4532 ✗ chapter_list_render(cur_stream);
4533 ✗ if (vk_renderer)
4534 ✗ vk_renderer_resize(vk_renderer, screen_width, screen_height);
4535 av_fallthrough;
4536 case SDL_WINDOWEVENT_EXPOSED:
4537 ✗ cur_stream->force_refresh = 1;
4538 }
4539 ✗ break;
4540 ✗ case SDL_QUIT:
4541 case FF_QUIT_EVENT:
4542 ✗ do_exit(cur_stream);
4543 ✗ break;
4544 ✗ default:
4545 ✗ break;
4546 }
4547 }
4548 }
4549
4550 ✗ static int opt_width(void *optctx, const char *opt, const char *arg)
4551 {
4552 double num;
4553 ✗ int ret = parse_number(opt, arg, OPT_TYPE_INT64, 1, INT_MAX, &num);
4554 ✗ if (ret < 0)
4555 ✗ return ret;
4556
4557 ✗ screen_width = num;
4558 ✗ return 0;
4559 }
4560
4561 ✗ static int opt_height(void *optctx, const char *opt, const char *arg)
4562 {
4563 double num;
4564 ✗ int ret = parse_number(opt, arg, OPT_TYPE_INT64, 1, INT_MAX, &num);
4565 ✗ if (ret < 0)
4566 ✗ return ret;
4567
4568 ✗ screen_height = num;
4569 ✗ return 0;
4570 }
4571
4572 ✗ static int opt_format(void *optctx, const char *opt, const char *arg)
4573 {
4574 ✗ file_iformat = av_find_input_format(arg);
4575 ✗ if (!file_iformat) {
4576 ✗ av_log(NULL, AV_LOG_FATAL, "Unknown input format: %s\n", arg);
4577 ✗ return AVERROR(EINVAL);
4578 }
4579 ✗ return 0;
4580 }
4581
4582 ✗ static int opt_sync(void *optctx, const char *opt, const char *arg)
4583 {
4584 ✗ if (!strcmp(arg, "audio"))
4585 ✗ av_sync_type = AV_SYNC_AUDIO_MASTER;
4586 ✗ else if (!strcmp(arg, "video"))
4587 ✗ av_sync_type = AV_SYNC_VIDEO_MASTER;
4588 ✗ else if (!strcmp(arg, "ext"))
4589 ✗ av_sync_type = AV_SYNC_EXTERNAL_CLOCK;
4590 else {
4591 ✗ av_log(NULL, AV_LOG_ERROR, "Unknown value for %s: %s\n", opt, arg);
4592 ✗ exit(1);
4593 }
4594 ✗ return 0;
4595 }
4596
4597 ✗ static int opt_show_mode(void *optctx, const char *opt, const char *arg)
4598 {
4599 ✗ show_mode = !strcmp(arg, "video") ? SHOW_MODE_VIDEO :
4600 ✗ !strcmp(arg, "waves") ? SHOW_MODE_WAVES :
4601 ✗ !strcmp(arg, "rdft" ) ? SHOW_MODE_RDFT : SHOW_MODE_NONE;
4602
4603 ✗ if (show_mode == SHOW_MODE_NONE) {
4604 double num;
4605 ✗ int ret = parse_number(opt, arg, OPT_TYPE_INT, 0, SHOW_MODE_NB-1, &num);
4606 ✗ if (ret < 0)
4607 ✗ return ret;
4608 ✗ show_mode = num;
4609 }
4610 ✗ return 0;
4611 }
4612
4613 ✗ static int opt_input_file(void *optctx, const char *filename)
4614 {
4615 ✗ if (input_filename) {
4616 ✗ av_log(NULL, AV_LOG_FATAL,
4617 "Argument '%s' provided as input filename, but '%s' was already specified.\n",
4618 filename, input_filename);
4619 ✗ return AVERROR(EINVAL);
4620 }
4621 ✗ if (!strcmp(filename, "-"))
4622 ✗ filename = "fd:";
4623 ✗ input_filename = av_strdup(filename);
4624 ✗ if (!input_filename)
4625 ✗ return AVERROR(ENOMEM);
4626
4627 ✗ return 0;
4628 }
4629
4630 ✗ static int opt_codec(void *optctx, const char *opt, const char *arg)
4631 {
4632 ✗ const char *spec = strchr(opt, ':');
4633 const char **name;
4634 ✗ if (!spec) {
4635 ✗ av_log(NULL, AV_LOG_ERROR,
4636 "No media specifier was specified in '%s' in option '%s'\n",
4637 arg, opt);
4638 ✗ return AVERROR(EINVAL);
4639 }
4640 ✗ spec++;
4641
4642 ✗ switch (spec[0]) {
4643 ✗ case 'a' : name = &audio_codec_name; break;
4644 ✗ case 's' : name = &subtitle_codec_name; break;
4645 ✗ case 'v' : name = &video_codec_name; break;
4646 ✗ default:
4647 ✗ av_log(NULL, AV_LOG_ERROR,
4648 "Invalid media specifier '%s' in option '%s'\n", spec, opt);
4649 ✗ return AVERROR(EINVAL);
4650 }
4651
4652 ✗ av_freep(name);
4653 ✗ *name = av_strdup(arg);
4654 ✗ return *name ? 0 : AVERROR(ENOMEM);
4655 }
4656
4657 static int dummy;
4658
4659 static const OptionDef options[] = {
4660 CMDUTILS_COMMON_OPTIONS
4661 { "x", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_width }, "force displayed width", "width" },
4662 { "y", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_height }, "force displayed height", "height" },
4663 { "fs", OPT_TYPE_BOOL, 0, { &is_full_screen }, "force full screen" },
4664 { "an", OPT_TYPE_BOOL, 0, { &audio_disable }, "disable audio" },
4665 { "vn", OPT_TYPE_BOOL, 0, { &video_disable }, "disable video" },
4666 { "sn", OPT_TYPE_BOOL, 0, { &subtitle_disable }, "disable subtitling" },
4667 { "ast", OPT_TYPE_STRING, OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_specifier" },
4668 { "vst", OPT_TYPE_STRING, OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_specifier" },
4669 { "sst", OPT_TYPE_STRING, OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_specifier" },
4670 { "ss", OPT_TYPE_TIME, 0, { &start_time }, "seek to a given position in seconds", "pos" },
4671 { "t", OPT_TYPE_TIME, 0, { &duration }, "play \"duration\" seconds of audio/video", "duration" },
4672 { "bytes", OPT_TYPE_INT, 0, { &seek_by_bytes }, "seek by bytes 0=off 1=on -1=auto", "val" },
4673 { "seek_interval", OPT_TYPE_FLOAT, 0, { &seek_interval }, "set seek interval for left/right keys, in seconds", "seconds" },
4674 { "nodisp", OPT_TYPE_BOOL, 0, { &display_disable }, "disable graphical display" },
4675 { "noborder", OPT_TYPE_BOOL, 0, { &borderless }, "borderless window" },
4676 { "alwaysontop", OPT_TYPE_BOOL, 0, { &alwaysontop }, "window always on top" },
4677 { "volume", OPT_TYPE_INT, 0, { &startup_volume}, "set startup volume 0=min 100=max", "volume" },
4678 { "f", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_format }, "force format", "fmt" },
4679 { "stats", OPT_TYPE_BOOL, OPT_EXPERT, { &show_status }, "show status", "" },
4680 { "fast", OPT_TYPE_BOOL, OPT_EXPERT, { &fast }, "non spec compliant optimizations", "" },
4681 { "genpts", OPT_TYPE_BOOL, OPT_EXPERT, { &genpts }, "generate pts", "" },
4682 { "drp", OPT_TYPE_INT, OPT_EXPERT, { &decoder_reorder_pts }, "let decoder reorder pts 0=off 1=on -1=auto", ""},
4683 { "lowres", OPT_TYPE_INT, OPT_EXPERT, { &lowres }, "", "" },
4684 { "sync", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = opt_sync }, "set audio-video sync. type (type=audio/video/ext)", "type" },
4685 { "autoexit", OPT_TYPE_BOOL, OPT_EXPERT, { &autoexit }, "exit at the end", "" },
4686 { "exitonkeydown", OPT_TYPE_BOOL, OPT_EXPERT, { &exit_on_keydown }, "exit on key down", "" },
4687 { "exitonmousedown", OPT_TYPE_BOOL, OPT_EXPERT, { &exit_on_mousedown }, "exit on mouse down", "" },
4688 { "loop", OPT_TYPE_INT, OPT_EXPERT, { &loop }, "set number of times the playback shall be looped", "loop count" },
4689 { "framedrop", OPT_TYPE_BOOL, OPT_EXPERT, { &framedrop }, "drop frames when cpu is too slow", "" },
4690 { "infbuf", OPT_TYPE_BOOL, OPT_EXPERT, { &infinite_buffer }, "don't limit the input buffer size (useful with realtime streams)", "" },
4691 { "window_title", OPT_TYPE_STRING, 0, { &window_title }, "set window title", "window title" },
4692 { "left", OPT_TYPE_INT, OPT_EXPERT, { &screen_left }, "set the x position for the left of the window", "x pos" },
4693 { "top", OPT_TYPE_INT, OPT_EXPERT, { &screen_top }, "set the y position for the top of the window", "y pos" },
4694 { "vf", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = opt_add_vfilter }, "set video filters", "filter_graph" },
4695 { "af", OPT_TYPE_STRING, 0, { &afilters }, "set audio filters", "filter_graph" },
4696 { "rdftspeed", OPT_TYPE_INT, OPT_AUDIO | OPT_EXPERT, { &rdftspeed }, "rdft speed", "msecs" },
4697 { "showmode", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_show_mode}, "select show mode (0 = video, 1 = waves, 2 = RDFT)", "mode" },
4698 { "i", OPT_TYPE_BOOL, 0, { &dummy}, "read specified file", "input_file"},
4699 { "codec", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_codec}, "force decoder", "decoder_name" },
4700 { "acodec", OPT_TYPE_STRING, OPT_EXPERT, { &audio_codec_name }, "force audio decoder", "decoder_name" },
4701 { "scodec", OPT_TYPE_STRING, OPT_EXPERT, { &subtitle_codec_name }, "force subtitle decoder", "decoder_name" },
4702 { "vcodec", OPT_TYPE_STRING, OPT_EXPERT, { &video_codec_name }, "force video decoder", "decoder_name" },
4703 { "autorotate", OPT_TYPE_BOOL, 0, { &autorotate }, "automatically rotate video", "" },
4704 { "find_stream_info", OPT_TYPE_BOOL, OPT_INPUT | OPT_EXPERT, { &find_stream_info },
4705 "read and decode the streams to fill missing information with heuristics" },
4706 { "filter_threads", OPT_TYPE_INT, OPT_EXPERT, { &filter_nbthreads }, "number of filter threads per graph" },
4707 { "enable_vulkan", OPT_TYPE_BOOL, 0, { &enable_vulkan }, "enable vulkan renderer" },
4708 { "vulkan_params", OPT_TYPE_STRING, OPT_EXPERT, { &vulkan_params }, "vulkan configuration using a list of key=value pairs separated by ':'" },
4709 { "video_bg", OPT_TYPE_STRING, OPT_EXPERT, { &video_background }, "set video background for transparent videos" },
4710 { "hwaccel", OPT_TYPE_STRING, OPT_EXPERT, { &hwaccel }, "use HW accelerated decoding" },
4711 { NULL, },
4712 };
4713
4714 ✗ static void show_usage(void)
4715 {
4716 ✗ av_log(NULL, AV_LOG_INFO, "Simple media player\n");
4717 ✗ av_log(NULL, AV_LOG_INFO, "usage: %s [options] input_file\n", program_name);
4718 ✗ av_log(NULL, AV_LOG_INFO, "\n");
4719 ✗ }
4720
4721 ✗ void show_help_default(const char *opt, const char *arg)
4722 {
4723 ✗ av_log_set_callback(log_callback_help);
4724 ✗ show_usage();
4725 ✗ show_help_options(options, "Main options:", 0, OPT_EXPERT);
4726 ✗ show_help_options(options, "Advanced options:", OPT_EXPERT, 0);
4727 ✗ printf("\n");
4728 ✗ show_help_children(avcodec_get_class(), AV_OPT_FLAG_DECODING_PARAM);
4729 ✗ show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);
4730 ✗ show_help_children(avfilter_get_class(), AV_OPT_FLAG_FILTERING_PARAM);
4731 ✗ printf("\nWhile playing:\n"
4732 "q, ESC quit\n"
4733 "f toggle full screen\n"
4734 "p, SPC pause\n"
4735 "m toggle mute\n"
4736 "9, 0 decrease and increase volume respectively\n"
4737 "/, * decrease and increase volume respectively\n"
4738 "a cycle audio channel in the current program\n"
4739 "v cycle video channel\n"
4740 "t cycle subtitle channel in the current program\n"
4741 "c cycle program\n"
4742 "w cycle video filters or show modes\n"
4743 "s activate frame-step mode\n"
4744 "left/right seek backward/forward by 10 seconds or a custom interval if -seek_interval is set\n"
4745 "down/up seek backward/forward 1 minute\n"
4746 "page down/page up seek to previous/next chapter or backward/forward 10 minutes if no chapters\n"
4747 "l keep the chapter list on screen; typing then filters it, down/up move its selection and escape closes it\n"
4748 "enter seek to the chapter selected in the chapter list while it is shown\n"
4749 "mouse wheel move the chapter list selection while it is shown\n"
4750 "left click seek to the clicked chapter list entry, or sort the list by the clicked column, again to flip it;\n"
4751 " the left half of the shuffle button lowers its seed, the right half raises it\n"
4752 "mouse over (i) show the metadata of that chapter list entry\n"
4753 "right mouse click seek to percentage in file corresponding to fraction of width\n"
4754 "left double-click toggle full screen\n"
4755 );
4756 ✗ }
4757
4758 /* Called from the main */
4759 ✗ int main(int argc, char **argv)
4760 {
4761 int flags, ret;
4762 VideoState *is;
4763
4764 ✗ init_dynload();
4765
4766 ✗ av_log_set_flags(AV_LOG_SKIP_REPEATED);
4767 ✗ parse_loglevel(argc, argv, options);
4768
4769 /* register all codecs, demux and protocols */
4770 #if CONFIG_AVDEVICE
4771 ✗ avdevice_register_all();
4772 #endif
4773 ✗ avformat_network_init();
4774
4775 ✗ signal(SIGINT , sigterm_handler); /* Interrupt (ANSI). */
4776 ✗ signal(SIGTERM, sigterm_handler); /* Termination (ANSI). */
4777
4778 ✗ show_banner(argc, argv, options);
4779
4780 ✗ ret = parse_options(NULL, argc, argv, options, opt_input_file);
4781 ✗ if (ret < 0)
4782 ✗ exit(ret == AVERROR_EXIT ? 0 : 1);
4783
4784 ✗ if (!input_filename) {
4785 ✗ show_usage();
4786 ✗ av_log(NULL, AV_LOG_FATAL, "An input file must be specified\n");
4787 ✗ av_log(NULL, AV_LOG_FATAL,
4788 "Use -h to get full help or, even better, run 'man %s'\n", program_name);
4789 ✗ exit(1);
4790 }
4791
4792 ✗ if (display_disable) {
4793 ✗ video_disable = 1;
4794 }
4795 ✗ flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
4796 ✗ if (audio_disable)
4797 ✗ flags &= ~SDL_INIT_AUDIO;
4798 ✗ if (display_disable)
4799 ✗ flags &= ~SDL_INIT_VIDEO;
4800 ✗ if (SDL_Init (flags)) {
4801 ✗ av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());
4802 ✗ av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");
4803 ✗ exit(1);
4804 }
4805
4806 ✗ SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
4807 ✗ SDL_EventState(SDL_USEREVENT, SDL_IGNORE);
4808
4809 ✗ if (!display_disable) {
4810 ✗ int flags = SDL_WINDOW_HIDDEN;
4811 ✗ if (alwaysontop)
4812 #if SDL_VERSION_ATLEAST(2,0,5)
4813 ✗ flags |= SDL_WINDOW_ALWAYS_ON_TOP;
4814 #else
4815 av_log(NULL, AV_LOG_WARNING, "Your SDL version doesn't support SDL_WINDOW_ALWAYS_ON_TOP. Feature will be inactive.\n");
4816 #endif
4817 ✗ if (borderless)
4818 ✗ flags |= SDL_WINDOW_BORDERLESS;
4819 else
4820 ✗ flags |= SDL_WINDOW_RESIZABLE;
4821
4822 #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
4823 ✗ SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
4824 #endif
4825 ✗ if (hwaccel && !enable_vulkan) {
4826 ✗ av_log(NULL, AV_LOG_INFO, "Enable vulkan renderer to support hwaccel %s\n", hwaccel);
4827 ✗ enable_vulkan = 1;
4828 }
4829 ✗ if (enable_vulkan) {
4830 ✗ vk_renderer = vk_get_renderer();
4831 ✗ if (vk_renderer) {
4832 #if SDL_VERSION_ATLEAST(2, 0, 6)
4833 ✗ flags |= SDL_WINDOW_VULKAN;
4834 #endif
4835 } else {
4836 ✗ av_log(NULL, AV_LOG_WARNING, "Doesn't support vulkan renderer, fallback to SDL renderer\n");
4837 ✗ enable_vulkan = 0;
4838 }
4839 }
4840 ✗ window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);
4841 ✗ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
4842 ✗ if (!window) {
4843 ✗ av_log(NULL, AV_LOG_FATAL, "Failed to create window: %s", SDL_GetError());
4844 ✗ do_exit(NULL);
4845 }
4846
4847 ✗ if (vk_renderer) {
4848 ✗ AVDictionary *dict = NULL;
4849
4850 ✗ if (vulkan_params) {
4851 ✗ int ret = av_dict_parse_string(&dict, vulkan_params, "=", ":", 0);
4852 ✗ if (ret < 0) {
4853 ✗ av_log(NULL, AV_LOG_FATAL, "Failed to parse, %s\n", vulkan_params);
4854 ✗ do_exit(NULL);
4855 }
4856 }
4857 ✗ ret = vk_renderer_create(vk_renderer, window, dict);
4858 ✗ av_dict_free(&dict);
4859 ✗ if (ret < 0) {
4860 ✗ av_log(NULL, AV_LOG_FATAL, "Failed to create vulkan renderer, %s\n", av_err2str(ret));
4861 ✗ do_exit(NULL);
4862 }
4863 } else {
4864 ✗ renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
4865 ✗ if (!renderer) {
4866 ✗ av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());
4867 ✗ renderer = SDL_CreateRenderer(window, -1, 0);
4868 }
4869 ✗ if (renderer) {
4870 ✗ if (!SDL_GetRendererInfo(renderer, &renderer_info))
4871 ✗ av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", renderer_info.name);
4872 }
4873 ✗ if (!renderer || !renderer_info.num_texture_formats) {
4874 ✗ av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError());
4875 ✗ do_exit(NULL);
4876 }
4877 }
4878 }
4879
4880 ✗ is = stream_open(input_filename, file_iformat);
4881 ✗ if (!is) {
4882 ✗ av_log(NULL, AV_LOG_FATAL, "Failed to initialize VideoState!\n");
4883 ✗ do_exit(NULL);
4884 }
4885
4886 ✗ event_loop(is);
4887
4888 /* never returns */
4889
4890 ✗ return 0;
4891 }
4892