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 <inttypes.h>
21 #include <math.h>
22 #include <assert.h>
23 
24 #include "config.h"
25 #include "mpv_talloc.h"
26 
27 #include "common/msg.h"
28 #include "options/options.h"
29 #include "common/common.h"
30 #include "common/global.h"
31 
32 #include "stream/stream.h"
33 #include "sub/dec_sub.h"
34 #include "demux/demux.h"
35 #include "video/mp_image.h"
36 
37 #include "core.h"
38 
39 // 0: primary sub, 1: secondary sub, -1: not selected
get_order(struct MPContext * mpctx,struct track * track)40 static int get_order(struct MPContext *mpctx, struct track *track)
41 {
42     for (int n = 0; n < num_ptracks[STREAM_SUB]; n++) {
43         if (mpctx->current_track[n][STREAM_SUB] == track)
44             return n;
45     }
46     return -1;
47 }
48 
reset_subtitles(struct MPContext * mpctx,struct track * track)49 static void reset_subtitles(struct MPContext *mpctx, struct track *track)
50 {
51     if (track->d_sub) {
52         sub_reset(track->d_sub);
53         sub_set_play_dir(track->d_sub, mpctx->play_dir);
54     }
55     term_osd_set_subs(mpctx, NULL);
56 }
57 
reset_subtitle_state(struct MPContext * mpctx)58 void reset_subtitle_state(struct MPContext *mpctx)
59 {
60     for (int n = 0; n < mpctx->num_tracks; n++)
61         reset_subtitles(mpctx, mpctx->tracks[n]);
62     term_osd_set_subs(mpctx, NULL);
63 }
64 
uninit_sub(struct MPContext * mpctx,struct track * track)65 void uninit_sub(struct MPContext *mpctx, struct track *track)
66 {
67     if (track && track->d_sub) {
68         reset_subtitles(mpctx, track);
69         sub_select(track->d_sub, false);
70         int order = get_order(mpctx, track);
71         osd_set_sub(mpctx->osd, order, NULL);
72         sub_destroy(track->d_sub);
73         track->d_sub = NULL;
74     }
75 }
76 
uninit_sub_all(struct MPContext * mpctx)77 void uninit_sub_all(struct MPContext *mpctx)
78 {
79     for (int n = 0; n < mpctx->num_tracks; n++)
80         uninit_sub(mpctx, mpctx->tracks[n]);
81 }
82 
update_subtitle(struct MPContext * mpctx,double video_pts,struct track * track)83 static bool update_subtitle(struct MPContext *mpctx, double video_pts,
84                             struct track *track)
85 {
86     struct dec_sub *dec_sub = track ? track->d_sub : NULL;
87 
88     if (!dec_sub || video_pts == MP_NOPTS_VALUE)
89         return true;
90 
91     if (mpctx->vo_chain) {
92         struct mp_image_params params = mpctx->vo_chain->filter->input_params;
93         if (params.imgfmt)
94             sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, &params);
95     }
96 
97     if (track->demuxer->fully_read && sub_can_preload(dec_sub)) {
98         // Assume fully_read implies no interleaved audio/video streams.
99         // (Reading packets will change the demuxer position.)
100         demux_seek(track->demuxer, 0, 0);
101         sub_preload(dec_sub);
102     }
103 
104     if (!sub_read_packets(dec_sub, video_pts))
105         return false;
106 
107     // Handle displaying subtitles on terminal; never done for secondary subs
108     if (mpctx->current_track[0][STREAM_SUB] == track && !mpctx->video_out) {
109         char *text = sub_get_text(dec_sub, video_pts, SD_TEXT_TYPE_PLAIN);
110         term_osd_set_subs(mpctx, text);
111         talloc_free(text);
112     }
113 
114     // Handle displaying subtitles on VO with no video being played. This is
115     // quite different, because normally subtitles are redrawn on new video
116     // frames, using the video frames' timestamps.
117     if (mpctx->video_out && mpctx->video_status == STATUS_EOF &&
118         (mpctx->opts->subs_rend->sub_past_video_end ||
119          !mpctx->current_track[0][STREAM_VIDEO] ||
120          mpctx->current_track[0][STREAM_VIDEO]->attached_picture)) {
121         if (osd_get_force_video_pts(mpctx->osd) != video_pts) {
122             osd_set_force_video_pts(mpctx->osd, video_pts);
123             osd_query_and_reset_want_redraw(mpctx->osd);
124             vo_redraw(mpctx->video_out);
125             // Force an arbitrary minimum FPS
126             mp_set_timeout(mpctx, 0.1);
127         }
128     }
129 
130     return true;
131 }
132 
133 // Return true if the subtitles for the given PTS are ready; false if the player
134 // should wait for new demuxer data, and then should retry.
update_subtitles(struct MPContext * mpctx,double video_pts)135 bool update_subtitles(struct MPContext *mpctx, double video_pts)
136 {
137     bool ok = true;
138     for (int n = 0; n < num_ptracks[STREAM_SUB]; n++)
139         ok &= update_subtitle(mpctx, video_pts, mpctx->current_track[n][STREAM_SUB]);
140     return ok;
141 }
142 
get_all_attachments(struct MPContext * mpctx)143 static struct attachment_list *get_all_attachments(struct MPContext *mpctx)
144 {
145     struct attachment_list *list = talloc_zero(NULL, struct attachment_list);
146     struct demuxer *prev_demuxer = NULL;
147     for (int n = 0; n < mpctx->num_tracks; n++) {
148         struct track *t = mpctx->tracks[n];
149         if (!t->demuxer || prev_demuxer == t->demuxer)
150             continue;
151         prev_demuxer = t->demuxer;
152         for (int i = 0; i < t->demuxer->num_attachments; i++) {
153             struct demux_attachment *att = &t->demuxer->attachments[i];
154             struct demux_attachment copy = {
155                 .name = talloc_strdup(list, att->name),
156                 .type = talloc_strdup(list, att->type),
157                 .data = talloc_memdup(list, att->data, att->data_size),
158                 .data_size = att->data_size,
159             };
160             MP_TARRAY_APPEND(list, list->entries, list->num_entries, copy);
161         }
162     }
163     return list;
164 }
165 
init_subdec(struct MPContext * mpctx,struct track * track)166 static bool init_subdec(struct MPContext *mpctx, struct track *track)
167 {
168     assert(!track->d_sub);
169 
170     if (!track->demuxer || !track->stream)
171         return false;
172 
173     track->d_sub = sub_create(mpctx->global, track->stream,
174                               get_all_attachments(mpctx),
175                               get_order(mpctx, track));
176     if (!track->d_sub)
177         return false;
178 
179     struct track *vtrack = mpctx->current_track[0][STREAM_VIDEO];
180     struct mp_codec_params *v_c =
181         vtrack && vtrack->stream ? vtrack->stream->codec : NULL;
182     double fps = v_c ? v_c->fps : 25;
183     sub_control(track->d_sub, SD_CTRL_SET_VIDEO_DEF_FPS, &fps);
184 
185     return true;
186 }
187 
reinit_sub(struct MPContext * mpctx,struct track * track)188 void reinit_sub(struct MPContext *mpctx, struct track *track)
189 {
190     if (!track || !track->stream || track->stream->type != STREAM_SUB)
191         return;
192 
193     assert(!track->d_sub);
194 
195     if (!init_subdec(mpctx, track)) {
196         error_on_track(mpctx, track);
197         return;
198     }
199 
200     sub_select(track->d_sub, true);
201     int order = get_order(mpctx, track);
202     osd_set_sub(mpctx->osd, order, track->d_sub);
203     sub_control(track->d_sub, SD_CTRL_SET_TOP, &order);
204 
205     if (mpctx->playback_initialized)
206         update_subtitles(mpctx, mpctx->playback_pts);
207 }
208 
reinit_sub_all(struct MPContext * mpctx)209 void reinit_sub_all(struct MPContext *mpctx)
210 {
211     for (int n = 0; n < num_ptracks[STREAM_SUB]; n++)
212         reinit_sub(mpctx, mpctx->current_track[n][STREAM_SUB]);
213 }
214