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