1 /*
2  * This file is part of mpv.
3  *
4  * mpv is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * mpv is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stddef.h>
19 #include <stdbool.h>
20 #include <errno.h>
21 #include <assert.h>
22 
23 #include "config.h"
24 #include "mpv_talloc.h"
25 
26 #include "osdep/io.h"
27 #include "osdep/timer.h"
28 #include "osdep/threads.h"
29 
30 #include "common/msg.h"
31 #include "options/options.h"
32 #include "options/m_property.h"
33 #include "options/m_config.h"
34 #include "common/common.h"
35 #include "common/global.h"
36 #include "common/encode.h"
37 #include "common/playlist.h"
38 #include "input/input.h"
39 
40 #include "audio/out/ao.h"
41 #include "demux/demux.h"
42 #include "stream/stream.h"
43 #include "video/out/vo.h"
44 
45 #include "core.h"
46 #include "command.h"
47 
48 const int num_ptracks[STREAM_TYPE_COUNT] = {
49     [STREAM_VIDEO] = 1,
50     [STREAM_AUDIO] = 1,
51     [STREAM_SUB] = 2,
52 };
53 
rel_time_to_abs(struct MPContext * mpctx,struct m_rel_time t)54 double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t)
55 {
56     double length = get_time_length(mpctx);
57     // Relative times are an offset to the start of the file.
58     double start = 0;
59     if (mpctx->demuxer && !mpctx->opts->rebase_start_time)
60         start = mpctx->demuxer->start_time;
61 
62     switch (t.type) {
63     case REL_TIME_ABSOLUTE:
64         return t.pos;
65     case REL_TIME_RELATIVE:
66         if (t.pos >= 0) {
67             return start + t.pos;
68         } else {
69             if (length >= 0)
70                 return start + MPMAX(length + t.pos, 0.0);
71         }
72         break;
73     case REL_TIME_PERCENT:
74         if (length >= 0)
75             return start + length * (t.pos / 100.0);
76         break;
77     case REL_TIME_CHAPTER:
78         return chapter_start_time(mpctx, t.pos); // already absolute time
79     }
80 
81     return MP_NOPTS_VALUE;
82 }
83 
get_play_end_pts_setting(struct MPContext * mpctx)84 static double get_play_end_pts_setting(struct MPContext *mpctx)
85 {
86     struct MPOpts *opts = mpctx->opts;
87     double end = rel_time_to_abs(mpctx, opts->play_end);
88     double length = rel_time_to_abs(mpctx, opts->play_length);
89     if (length != MP_NOPTS_VALUE) {
90         double start = get_play_start_pts(mpctx);
91         if (end == MP_NOPTS_VALUE || start + length < end)
92             end = start + length;
93     }
94     return end;
95 }
96 
97 // Return absolute timestamp against which currently playing media should be
98 // clipped. Returns MP_NOPTS_VALUE if no clipping should happen.
get_play_end_pts(struct MPContext * mpctx)99 double get_play_end_pts(struct MPContext *mpctx)
100 {
101     double end = get_play_end_pts_setting(mpctx);
102     double ab[2];
103     if (mpctx->ab_loop_clip && get_ab_loop_times(mpctx, ab)) {
104         if (end == MP_NOPTS_VALUE || end > ab[1])
105             end = ab[1];
106     }
107     return end;
108 }
109 
110 // Get the absolute PTS at which playback should start.
111 // Never returns MP_NOPTS_VALUE.
get_play_start_pts(struct MPContext * mpctx)112 double get_play_start_pts(struct MPContext *mpctx)
113 {
114     struct MPOpts *opts = mpctx->opts;
115     double res = rel_time_to_abs(mpctx, opts->play_start);
116     if (res == MP_NOPTS_VALUE)
117         res = get_start_time(mpctx, mpctx->play_dir);
118     return res;
119 }
120 
121 // Get timestamps to use for AB-loop. Returns false iff any of the timestamps
122 // are invalid and/or AB-loops are currently disabled, and set t[] to either
123 // the user options or NOPTS on best effort basis.
get_ab_loop_times(struct MPContext * mpctx,double t[2])124 bool get_ab_loop_times(struct MPContext *mpctx, double t[2])
125 {
126     struct MPOpts *opts = mpctx->opts;
127     int dir = mpctx->play_dir;
128 
129     t[0] = opts->ab_loop[0];
130     t[1] = opts->ab_loop[1];
131 
132     if (!opts->ab_loop_count)
133         return false;
134 
135     if (t[0] == MP_NOPTS_VALUE || t[1] == MP_NOPTS_VALUE || t[0] == t[1])
136         return false;
137 
138     if (t[0] * dir > t[1] * dir)
139         MPSWAP(double, t[0], t[1]);
140 
141     return true;
142 }
143 
get_track_seek_offset(struct MPContext * mpctx,struct track * track)144 double get_track_seek_offset(struct MPContext *mpctx, struct track *track)
145 {
146     struct MPOpts *opts = mpctx->opts;
147     if (track->selected) {
148         if (track->type == STREAM_AUDIO)
149             return -opts->audio_delay;
150         if (track->type == STREAM_SUB)
151             return -opts->subs_rend->sub_delay;
152     }
153     return 0;
154 }
155 
issue_refresh_seek(struct MPContext * mpctx,enum seek_precision min_prec)156 void issue_refresh_seek(struct MPContext *mpctx, enum seek_precision min_prec)
157 {
158     // let queued seeks execute at a slightly later point
159     if (mpctx->seek.type) {
160         mp_wakeup_core(mpctx);
161         return;
162     }
163     // repeat currently ongoing seeks
164     if (mpctx->current_seek.type) {
165         mpctx->seek = mpctx->current_seek;
166         mp_wakeup_core(mpctx);
167         return;
168     }
169     queue_seek(mpctx, MPSEEK_ABSOLUTE, get_current_time(mpctx), min_prec, 0);
170 }
171 
update_vo_playback_state(struct MPContext * mpctx)172 void update_vo_playback_state(struct MPContext *mpctx)
173 {
174     if (mpctx->video_out && mpctx->video_out->config_ok) {
175         struct voctrl_playback_state oldstate = mpctx->vo_playback_state;
176         struct voctrl_playback_state newstate = {
177             .taskbar_progress = mpctx->opts->vo->taskbar_progress,
178             .playing = mpctx->playing,
179             .paused = mpctx->paused,
180             .percent_pos = get_percent_pos(mpctx),
181         };
182 
183         if (oldstate.taskbar_progress != newstate.taskbar_progress ||
184             oldstate.playing != newstate.playing ||
185             oldstate.paused != newstate.paused ||
186             oldstate.percent_pos != newstate.percent_pos)
187         {
188             // Don't update progress bar if it was and still is hidden
189             if ((oldstate.playing && oldstate.taskbar_progress) ||
190                 (newstate.playing && newstate.taskbar_progress))
191             {
192                 vo_control_async(mpctx->video_out,
193                                  VOCTRL_UPDATE_PLAYBACK_STATE, &newstate);
194             }
195             mpctx->vo_playback_state = newstate;
196         }
197     } else {
198         mpctx->vo_playback_state = (struct voctrl_playback_state){ 0 };
199     }
200 }
201 
update_window_title(struct MPContext * mpctx,bool force)202 void update_window_title(struct MPContext *mpctx, bool force)
203 {
204     if (!mpctx->video_out && !mpctx->ao) {
205         talloc_free(mpctx->last_window_title);
206         mpctx->last_window_title = NULL;
207         return;
208     }
209     char *title = mp_property_expand_string(mpctx, mpctx->opts->wintitle);
210     if (!mpctx->last_window_title || force ||
211         strcmp(title, mpctx->last_window_title) != 0)
212     {
213         talloc_free(mpctx->last_window_title);
214         mpctx->last_window_title = talloc_steal(mpctx, title);
215 
216         if (mpctx->video_out)
217             vo_control(mpctx->video_out, VOCTRL_UPDATE_WINDOW_TITLE, title);
218 
219         if (mpctx->ao) {
220             ao_control(mpctx->ao, AOCONTROL_UPDATE_STREAM_TITLE, title);
221         }
222     } else {
223         talloc_free(title);
224     }
225 }
226 
error_on_track(struct MPContext * mpctx,struct track * track)227 void error_on_track(struct MPContext *mpctx, struct track *track)
228 {
229     if (!track || !track->selected)
230         return;
231     mp_deselect_track(mpctx, track);
232     if (track->type == STREAM_AUDIO)
233         MP_INFO(mpctx, "Audio: no audio\n");
234     if (track->type == STREAM_VIDEO)
235         MP_INFO(mpctx, "Video: no video\n");
236     if (mpctx->opts->stop_playback_on_init_failure ||
237         !(mpctx->vo_chain || mpctx->ao_chain))
238     {
239         if (!mpctx->stop_play)
240             mpctx->stop_play = PT_ERROR;
241         if (mpctx->error_playing >= 0)
242             mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY;
243     }
244     mp_wakeup_core(mpctx);
245 }
246 
stream_dump(struct MPContext * mpctx,const char * source_filename)247 int stream_dump(struct MPContext *mpctx, const char *source_filename)
248 {
249     struct MPOpts *opts = mpctx->opts;
250     stream_t *stream = stream_create(source_filename,
251                                      STREAM_ORIGIN_DIRECT | STREAM_READ,
252                                      mpctx->playback_abort, mpctx->global);
253     if (!stream)
254         return -1;
255 
256     int64_t size = stream_get_size(stream);
257 
258     FILE *dest = fopen(opts->stream_dump, "wb");
259     if (!dest) {
260         MP_ERR(mpctx, "Error opening dump file: %s\n", mp_strerror(errno));
261         return -1;
262     }
263 
264     bool ok = true;
265 
266     while (mpctx->stop_play == KEEP_PLAYING && ok) {
267         if (!opts->quiet && ((stream->pos / (1024 * 1024)) % 2) == 1) {
268             uint64_t pos = stream->pos;
269             MP_MSG(mpctx, MSGL_STATUS, "Dumping %lld/%lld...",
270                    (long long int)pos, (long long int)size);
271         }
272         uint8_t buf[4096];
273         int len = stream_read(stream, buf, sizeof(buf));
274         if (!len) {
275             ok &= stream->eof;
276             break;
277         }
278         ok &= fwrite(buf, len, 1, dest) == 1;
279         mp_wakeup_core(mpctx); // don't actually sleep
280         mp_idle(mpctx); // but process input
281     }
282 
283     ok &= fclose(dest) == 0;
284     free_stream(stream);
285     return ok ? 0 : -1;
286 }
287 
merge_playlist_files(struct playlist * pl)288 void merge_playlist_files(struct playlist *pl)
289 {
290     if (!pl->num_entries)
291         return;
292     char *edl = talloc_strdup(NULL, "edl://");
293     for (int n = 0; n < pl->num_entries; n++) {
294         struct playlist_entry *e = pl->entries[n];
295         if (n)
296             edl = talloc_strdup_append_buffer(edl, ";");
297         // Escape if needed
298         if (e->filename[strcspn(e->filename, "=%,;\n")] ||
299             bstr_strip(bstr0(e->filename)).len != strlen(e->filename))
300         {
301             // %length%
302             edl = talloc_asprintf_append_buffer(edl, "%%%zd%%", strlen(e->filename));
303         }
304         edl = talloc_strdup_append_buffer(edl, e->filename);
305     }
306     playlist_clear(pl);
307     playlist_add_file(pl, edl);
308     talloc_free(edl);
309 }
310 
mp_status_str(enum playback_status st)311 const char *mp_status_str(enum playback_status st)
312 {
313     switch (st) {
314     case STATUS_SYNCING:    return "syncing";
315     case STATUS_READY:      return "ready";
316     case STATUS_PLAYING:    return "playing";
317     case STATUS_DRAINING:   return "draining";
318     case STATUS_EOF:        return "eof";
319     default:                return "bug";
320     }
321 }
322