1 /*
2 * Copyright (C) 2000-2018 the xine project
3 * May 2003 - Miguel Freitas
4 * This plugin was sponsored by 1Control
5 *
6 * This file is part of xine, a free video player.
7 *
8 * xine is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * xine is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
21 */
22
23 /*
24 * demuxer for slave "protocol"
25 * master xine must be started with XINE_PARAM_BROADCASTER_PORT set, that is,
26 * 'xine --broadcast-port <port_number>'
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <string.h>
38
39 #define LOG_MODULE "demux_slave"
40 #define LOG_VERBOSE
41 /*
42 #define LOG
43 */
44
45 #include <xine/xine_internal.h>
46 #include <xine/xineutils.h>
47 #include <xine/compat.h>
48 #include <xine/demux.h>
49
50 #define SCRATCH_SIZE 1024
51 #define CHECK_VPTS_INTERVAL 2*90000
52 #define NETWORK_PREBUFFER 90000
53
54 typedef struct {
55 demux_plugin_t demux_plugin;
56
57 xine_stream_t *stream;
58 fifo_buffer_t *video_fifo;
59 fifo_buffer_t *audio_fifo;
60 input_plugin_t *input;
61 int status;
62
63 int64_t last_vpts;
64 int send_newpts;
65
66 /* additional decoder flags and other dec-spec. stuff */
67 uint32_t decoder_info[BUF_NUM_DEC_INFO];
68 /* pointers to dec-spec. stuff */
69 void *decoder_info_ptr[BUF_NUM_DEC_INFO];
70 xine_list_t *dec_infos; /* dec-spec. stuff */
71
72 uint8_t scratch[SCRATCH_SIZE+1];
73 int scratch_used;
74 } demux_slave_t ;
75
76
77 #define MAX_COMMAND_SIZE 20
78
demux_slave_next(demux_slave_t * this)79 static int demux_slave_next (demux_slave_t *this) {
80 buf_element_t *buf;
81 int n, i;
82 char fifo_name[11];
83 uint8_t *p, *s;
84 int64_t curvpts;
85
86 /* fill the scratch buffer */
87 n = this->input->read(this->input, &this->scratch[this->scratch_used],
88 SCRATCH_SIZE - this->scratch_used);
89 if (n <= 0) {
90 lprintf("connection closed\n");
91 this->status = DEMUX_FINISHED;
92 return 0;
93 }
94
95 this->scratch_used += n;
96 this->scratch[this->scratch_used] = '\0';
97
98 p = strchr(this->scratch,'\n');
99 s = strchr(this->scratch,' ');
100
101 if( !s || s > p )
102 s = p;
103
104 if( !p || !s || (s-this->scratch+1) > MAX_COMMAND_SIZE ) {
105 lprintf("protocol error\n");
106 this->status = DEMUX_FINISHED;
107 return 0;
108 }
109
110 *s++ = '\0';
111 p++;
112
113 if( !strcmp(this->scratch,"buffer") ) {
114 int32_t size ; /* size of _content_ */
115 uint32_t type;
116 int64_t pts; /* presentation time stamp, used for a/v sync */
117 int64_t disc_off; /* discontinuity offset */
118 uint32_t decoder_flags; /* stuff like keyframe, is_header ... see below */
119
120 if( sscanf(s,"fifo=%10s size=%" SCNd32 " type=%" SCNu32 " pts=%" SCNd64 " disc=%" SCNd64 " flags=%" SCNu32,
121 fifo_name, &size, &type, &pts, &disc_off, &decoder_flags) != 6 ) {
122 lprintf("'buffer' command error\n");
123 this->status = DEMUX_FINISHED;
124 return 0;
125 }
126
127 if( type == BUF_CONTROL_NEWPTS ) {
128 this->send_newpts = 0;
129 this->last_vpts = 0;
130 }
131
132 /* if we join an already existing broadcaster we must take care
133 * of the initial pts.
134 */
135 if( pts && this->send_newpts ) {
136 _x_demux_control_newpts( this->stream, pts, 0 );
137 this->send_newpts = 0;
138 }
139
140 /* check if we are not late on playback.
141 * that might happen if user hits "pause" on the master, for example.
142 */
143 if( pts &&
144 (curvpts = this->stream->xine->clock->get_current_time(this->stream->xine->clock)) >
145 (this->last_vpts + CHECK_VPTS_INTERVAL) ) {
146 if( this->last_vpts &&
147 pts - (NETWORK_PREBUFFER/2) +
148 this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET) <
149 curvpts ) {
150 xprintf(this->stream->xine, XINE_VERBOSITY_LOG, "we are running late, forcing newpts.\n");
151 _x_demux_control_newpts( this->stream, pts - NETWORK_PREBUFFER, 0 );
152 }
153 this->last_vpts = curvpts;
154 }
155
156
157 if( !strcmp(fifo_name,"video") || !this->audio_fifo )
158 buf = this->video_fifo->buffer_pool_alloc(this->video_fifo);
159 else
160 buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo);
161
162 /* copy data to buf, either from stratch or network */
163 n = this->scratch_used - (p-this->scratch);
164 if( n > size )
165 n = size;
166 if( n )
167 memcpy(buf->content, p, n);
168 if( n < size ) {
169 if (this->input->read(this->input, &buf->content[n], size-n) != (size-n)) {
170 buf->free_buffer(buf);
171 this->status = DEMUX_FINISHED;
172 return 0;
173 }
174 }
175
176 p += n;
177 n = this->scratch_used - (p-this->scratch);
178 if( n )
179 memmove(this->scratch, p, n);
180 this->scratch_used = n;
181
182 /* populate our buf */
183 buf->size = size;
184 buf->type = type;
185 buf->pts = pts;
186 buf->disc_off = disc_off;
187 buf->decoder_flags = decoder_flags;
188
189 /* set decoder info */
190 memcpy(buf->decoder_info, this->decoder_info, sizeof(this->decoder_info));
191 memcpy(buf->decoder_info_ptr, this->decoder_info_ptr, sizeof(this->decoder_info));
192 memset(this->decoder_info, 0, sizeof(this->decoder_info));
193 memset(this->decoder_info_ptr, 0, sizeof(this->decoder_info_ptr));
194
195 if( !strcmp(fifo_name,"video") )
196 this->video_fifo->put(this->video_fifo, buf);
197 else if (this->audio_fifo)
198 this->audio_fifo->put(this->audio_fifo, buf);
199 else
200 buf->free_buffer(buf);
201
202 } else if( !strcmp(this->scratch,"decoder_info") ) {
203
204 uint32_t decoder_info;
205 int has_data;
206 int size;
207
208 if( sscanf(s,"index=%d decoder_info=%u has_data=%d",
209 &i, &decoder_info, &has_data) != 3 ||
210 i < 0 || i >= BUF_NUM_DEC_INFO) {
211 lprintf("'decoder_info' command error\n");
212 this->status = DEMUX_FINISHED;
213 return 0;
214 }
215
216 this->decoder_info[i] = decoder_info;
217
218 size = (has_data) ? decoder_info : 0;
219
220 if( size ) {
221 this->decoder_info_ptr[i] = malloc(size);
222 xine_list_push_back(this->dec_infos, this->decoder_info_ptr[i]);
223 }
224
225 n = this->scratch_used - (p-this->scratch);
226 if( n > size )
227 n = size;
228 if( n )
229 memcpy(this->decoder_info_ptr[i], p, n);
230 if( n < size ) {
231 if (this->input->read(this->input, (char *)this->decoder_info_ptr[i]+n, size-n) != (size-n)) {
232 this->status = DEMUX_FINISHED;
233 return 0;
234 }
235 }
236
237 p += n;
238 n = this->scratch_used - (p-this->scratch);
239 if( n )
240 memmove(this->scratch, p, n);
241 this->scratch_used = n;
242
243
244 } else if( !strcmp(this->scratch,"flush_engine") ) {
245
246 _x_demux_flush_engine( this->stream );
247 n = this->scratch_used - (p-this->scratch);
248 if( n )
249 memmove(this->scratch, p, n);
250 this->scratch_used = n;
251
252 } else {
253 lprintf("unknown command '%s'\n", this->scratch);
254 n = this->scratch_used - (p-this->scratch);
255 if( n )
256 memmove(this->scratch, p, n);
257 this->scratch_used = n;
258 }
259
260 return 1;
261 }
262
demux_slave_send_chunk(demux_plugin_t * this_gen)263 static int demux_slave_send_chunk (demux_plugin_t *this_gen) {
264 demux_slave_t *this = (demux_slave_t *) this_gen;
265
266 demux_slave_next(this);
267
268 return this->status;
269 }
270
demux_slave_get_status(demux_plugin_t * this_gen)271 static int demux_slave_get_status (demux_plugin_t *this_gen) {
272 demux_slave_t *this = (demux_slave_t *) this_gen;
273
274 return this->status;
275 }
276
277
demux_slave_send_headers(demux_plugin_t * this_gen)278 static void demux_slave_send_headers (demux_plugin_t *this_gen) {
279 demux_slave_t *this = (demux_slave_t *) this_gen;
280
281 this->video_fifo = this->stream->video_fifo;
282 this->audio_fifo = this->stream->audio_fifo;
283
284 _x_demux_control_start(this->stream);
285
286 this->status = DEMUX_OK;
287
288 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 1);
289 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 1);
290
291 this->last_vpts = 0;
292 this->send_newpts = 1;
293 }
294
demux_slave_seek(demux_plugin_t * this_gen,off_t start_pos,int start_time,int playing)295 static int demux_slave_seek (demux_plugin_t *this_gen, off_t start_pos, int start_time, int playing) {
296 demux_slave_t *this = (demux_slave_t *) this_gen;
297
298 (void)this_gen;
299 (void)start_pos;
300 (void)start_time;
301 (void)playing;
302 return this->status;
303 }
304
demux_slave_dispose(demux_plugin_t * this_gen)305 static void demux_slave_dispose (demux_plugin_t *this_gen) {
306 demux_slave_t *this = (demux_slave_t *) this_gen;
307 void *data;
308 xine_list_iterator_t ite;
309
310 /* free all decoder information */
311 ite = NULL;
312 while ((data = xine_list_next_value (this->dec_infos, &ite)))
313 free(data);
314 xine_list_delete(this->dec_infos);
315
316 free (this);
317 }
318
demux_slave_get_stream_length(demux_plugin_t * this_gen)319 static int demux_slave_get_stream_length(demux_plugin_t *this_gen) {
320 (void)this_gen;
321 return 0 ; /*FIXME: implement */
322 }
323
demux_slave_get_capabilities(demux_plugin_t * this_gen)324 static uint32_t demux_slave_get_capabilities(demux_plugin_t *this_gen) {
325 (void)this_gen;
326 return DEMUX_CAP_NOCAP;
327 }
328
demux_slave_get_optional_data(demux_plugin_t * this_gen,void * data,int data_type)329 static int demux_slave_get_optional_data(demux_plugin_t *this_gen,
330 void *data, int data_type) {
331 (void)this_gen;
332 (void)data;
333 (void)data_type;
334 return DEMUX_OPTIONAL_UNSUPPORTED;
335 }
336
337
open_plugin(demux_class_t * class_gen,xine_stream_t * stream,input_plugin_t * input)338 static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream,
339 input_plugin_t *input) {
340
341 static const char slave_id_str[] = "master xine v1\n";
342 const size_t slave_id_str_len = strlen(slave_id_str);
343 demux_slave_t *this;
344 char scratch[sizeof(slave_id_str)];
345
346 switch (stream->content_detection_method) {
347
348 case METHOD_BY_CONTENT:
349 if (_x_demux_read_header (input, scratch, slave_id_str_len) != (int)slave_id_str_len)
350 return NULL;
351 if (memcmp(scratch, slave_id_str, slave_id_str_len))
352 return NULL;
353 break;
354
355 case METHOD_BY_MRL:
356 case METHOD_EXPLICIT:
357 break;
358
359 default:
360 return NULL;
361 }
362
363 if (input->seek (input, slave_id_str_len, SEEK_SET) != (int)slave_id_str_len)
364 return NULL;
365
366 this = calloc(1, sizeof(demux_slave_t));
367 if (!this)
368 return NULL;
369
370 this->stream = stream;
371 this->input = input;
372 this->dec_infos = xine_list_new();
373
374 this->demux_plugin.send_headers = demux_slave_send_headers;
375 this->demux_plugin.send_chunk = demux_slave_send_chunk;
376 this->demux_plugin.seek = demux_slave_seek;
377 this->demux_plugin.dispose = demux_slave_dispose;
378 this->demux_plugin.get_status = demux_slave_get_status;
379 this->demux_plugin.get_stream_length = demux_slave_get_stream_length;
380 this->demux_plugin.get_capabilities = demux_slave_get_capabilities;
381 this->demux_plugin.get_optional_data = demux_slave_get_optional_data;
382 this->demux_plugin.demux_class = class_gen;
383
384 this->status = DEMUX_FINISHED;
385
386 this->scratch_used = 0;
387
388 memset(this->decoder_info, 0, sizeof(this->decoder_info));
389 memset(this->decoder_info_ptr, 0, sizeof(this->decoder_info_ptr));
390
391 return &this->demux_plugin;
392 }
393
init_plugin(xine_t * xine,const void * data)394 static void *init_plugin (xine_t *xine, const void *data) {
395
396 (void)xine;
397 (void)data;
398
399 static const demux_class_t demux_slave_class = {
400 .open_plugin = open_plugin,
401 .description = "",
402 .identifier = "slave",
403 .mimetypes = NULL,
404 .extensions = "slave://",
405 .dispose = NULL,
406 };
407
408 return (void *)&demux_slave_class;
409 }
410
411 /*
412 * exported plugin catalog entry
413 */
414 static const demuxer_info_t demux_info_slave = {
415 .priority = 10,
416 };
417
418 const plugin_info_t xine_plugin_info[] EXPORTED = {
419 /* type, API, "name", version, special_info, init_function */
420 { PLUGIN_DEMUX, 27, "slave", XINE_VERSION_CODE, &demux_info_slave, init_plugin },
421 { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
422 };
423