1 /*
2 * Serve TS packets from TS or PS data, supporting playing forwards
3 * at normal and accelerated speeds, and reverse play.
4 *
5 * ***** BEGIN LICENSE BLOCK *****
6 * Version: MPL 1.1
7 *
8 * The contents of this file are subject to the Mozilla Public License Version
9 * 1.1 (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
12 *
13 * Software distributed under the License is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 * for the specific language governing rights and limitations under the
16 * License.
17 *
18 * The Original Code is the MPEG TS, PS and ES tools.
19 *
20 * The Initial Developer of the Original Code is Amino Communications Ltd.
21 * Portions created by the Initial Developer are Copyright (C) 2008
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 * Amino Communications Ltd, Swavesey, Cambridge UK
26 *
27 * ***** END LICENSE BLOCK *****
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <math.h>
36
37 #ifdef _WIN32
38 #include <stddef.h>
39 #include <winsock2.h>
40 #include <ws2tcpip.h>
41 #include <process.h>
42 #else // _WIN32
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/wait.h> // WNOHANG
47 #include <netinet/in.h> // sockaddr_in
48 #include <signal.h> // sigaction, etc.
49 #endif // _WIN32
50
51 #include <time.h> // Sleeping and timing
52
53 #include "compat.h"
54 #include "ts_fns.h"
55 #include "ps_fns.h"
56 #include "pes_fns.h"
57 #include "accessunit_fns.h"
58 #include "nalunit_fns.h"
59 #include "misc_fns.h"
60 #include "tswrite_fns.h"
61 #include "es_fns.h"
62 #include "h262_fns.h"
63 #include "filter_fns.h"
64 #include "reverse_fns.h"
65 #include "version.h"
66
67 //#define DEBUG
68 #define SHOW_REVERSE_DATA 1
69 #define DEBUG_COMMANDS 1
70
71 #define TIME_SKIPPING 1
72 #if TIME_SKIPPING
73 #include <time.h>
74 #endif
75
76
77 #define DEFAULT_REVERSE_FREQUENCY 8
78 #define DEFAULT_FORWARD_FREQUENCY 8
79 #define FRAMES_FOR_ONE_SECOND 25
80
81 #define SMALL_SKIP_DISTANCE 10*FRAMES_FOR_ONE_SECOND // 10 seconds
82 #define BIG_SKIP_DISTANCE 3*60*FRAMES_FOR_ONE_SECOND // 3 minutes
83
84 static int extra_info = 0;
85
86 // What we are to do
87 enum ACTION
88 {
89 ACTION_SERVER, // The default action is to be a server
90 ACTION_CMD, // An alternative is to connect and read commands
91 ACTION_TEST, // One of the testing modes
92 };
93
94 #define MAX_INPUT_FILES 10 // i.e., 0..9
95
96 // Command line data
97 // There's a lot of data from the command line that needs passing down
98 // from the top level to the main processing functions, so let's package
99 // it up neatly
100 struct tsserve_context
101 {
102 char *input_names[MAX_INPUT_FILES]; // The files to read from
103 int default_file_index; // Which one is the default
104
105 int video_only; // As it says - no audio?
106 int pad_start; // Number of filler packets to output at start
107
108 int ffrequency; // Fast forward frequency when filtering
109 int rfrequency; // Base reverse frequency
110 int with_seq_hdrs;// For H.262, output sequence headers when not
111 // doing normal play?
112
113 int pes_padding; // Number of dummy PES packets to output per real packet
114 int drop_packets; // 0 or drop TS packets every <n> on output
115 int drop_number; // how many packets to drop
116
117 // Program Stream specific options
118 uint32_t pmt_pid;
119 uint32_t audio_pid;
120 uint32_t video_pid;
121 uint32_t pcr_pid;
122 int want_h262;
123 int dolby_is_dvb;
124 int force_stream_type;
125 int repeat_program_every;
126
127 // Transport Stream specific options
128 int tsdirect;
129 };
130 typedef struct tsserve_context *tsserve_context_p;
131
132
133 // ============================================================
134 // Unions to give us a single view of the two forms of data stream
135 // ============================================================
136 // The form of this single view is limited solely to what is needed
137 // in this program - it is not intended to be a general unification
138 // of the two types of data.
139
140 // Accessing the data stream
141 union u_stream_context
142 {
143 h262_context_p h262;
144 access_unit_context_p h264;
145 };
146 struct _stream_context
147 {
148 int is_h262;
149 int program_number;
150 union u_stream_context u;
151 };
152 typedef struct _stream_context stream_context;
153 typedef struct _stream_context *stream_context_p;
154
155 // Filtering
156 union u_filter_context
157 {
158 h262_filter_context_p h262;
159 h264_filter_context_p h264;
160 };
161 struct _filter_context
162 {
163 int is_h262;
164 union u_filter_context u;
165 };
166 typedef struct _filter_context filter_context;
167 typedef struct _filter_context *filter_context_p;
168
169 // Pictures
170 union u_picture
171 {
172 int is_h262;
173 h262_picture_p h262;
174 access_unit_p h264;
175 };
176
177 struct _picture
178 {
179 int is_h262;
180 union u_picture u;
181 int type; // For H.262, the picture coding type. 0xFF means seq hdr
182 };
183 typedef struct _picture picture;
184 typedef struct _picture *picture_p;
185
186
187 // ============================================================
188 // Utilities to hide the difference between the two data stream types
189 // ============================================================
190
191 // A macro to avoid my mistyping this on the several occasions I need it
192 #define EXTRACT_ES_FROM_STREAM(stream) \
193 ((stream).is_h262?(stream).u.h262->es:(stream).u.h264->nac->es)
194
195 // A related macro for reverse data
196 #define EXTRACT_REVERSE_FROM_STREAM(stream) \
197 ((stream).is_h262?(stream).u.h262->reverse_data:(stream).u.h264->reverse_data)
198
199
200 /*
201 * Note that `program_number` should be 1 or more.
202 */
build_stream(ES_p es,int is_h262,int program_number,stream_context * stream)203 static int build_stream(ES_p es,
204 int is_h262,
205 int program_number,
206 stream_context *stream)
207 {
208 int err;
209 stream->is_h262 = is_h262;
210 stream->program_number = program_number;
211 if (is_h262)
212 {
213 err = build_h262_context(es,&(stream->u.h262));
214 if (err)
215 {
216 fprintf(stderr,"### Error building H.262 context\n");
217 return 1;
218 }
219 }
220 else
221 {
222 err = build_access_unit_context(es,&(stream->u.h264));
223 if (err)
224 {
225 fprintf(stderr,"### Error building H.264 access unit context\n");
226 return 1;
227 }
228 }
229 return 0;
230 }
231
close_stream(stream_context stream)232 static void close_stream(stream_context stream)
233 {
234 if (stream.is_h262)
235 free_h262_context(&(stream.u.h262));
236 else
237 free_access_unit_context(&(stream.u.h264));
238 }
239
build_and_attach_reverse(stream_context stream,reverse_data_p * reverse_data)240 static int build_and_attach_reverse(stream_context stream,
241 reverse_data_p *reverse_data)
242 {
243 int err;
244 err = build_reverse_data(reverse_data,!stream.is_h262);
245 if (err)
246 {
247 fprintf(stderr,"### Unable to build reverse memory\n");
248 return 1;
249 }
250
251 if (stream.is_h262)
252 add_h262_reverse_context(stream.u.h262,*reverse_data);
253 else
254 add_access_unit_reverse_context(stream.u.h264,*reverse_data);
255
256 return 0;
257 }
258
build_filter_context(stream_context stream,int is_strip,int frequency,filter_context * fcontext)259 static int build_filter_context(stream_context stream,
260 int is_strip,
261 int frequency,
262 filter_context *fcontext)
263 {
264 int err;
265 fcontext->is_h262 = stream.is_h262;
266 if (stream.is_h262)
267 {
268 if (is_strip)
269 err = build_h262_filter_context_strip(&(fcontext->u.h262),
270 stream.u.h262,TRUE);
271 else
272 err = build_h262_filter_context(&(fcontext->u.h262),
273 stream.u.h262,frequency);
274 }
275 else
276 {
277 if (is_strip)
278 err = build_h264_filter_context_strip(&(fcontext->u.h264),
279 stream.u.h264,TRUE);
280 else
281 err = build_h264_filter_context(&(fcontext->u.h264),
282 stream.u.h264,frequency);
283 }
284 return err;
285 }
286
free_filter_context(filter_context fcontext)287 static void free_filter_context(filter_context fcontext)
288 {
289 if (fcontext.is_h262)
290 free_h262_filter_context(&(fcontext.u.h262));
291 else
292 free_h264_filter_context(&(fcontext.u.h264));
293 }
294
295 /*
296 * "Reset" a stream, such that the picture reading contexts do not contain
297 * any past memory.
298 */
reset_stream(stream_context stream)299 static inline void reset_stream(stream_context stream)
300 {
301 if (stream.is_h262)
302 {
303 if (stream.u.h262->last_item)
304 free_h262_item(&stream.u.h262->last_item);
305 }
306 else
307 {
308 reset_access_unit_context(stream.u.h264);
309 }
310 }
311
312 /*
313 * Retrieve the next picture. Doesn't distinguish H.262 sequence headers
314 * and pictures.
315 */
get_next_picture(stream_context stream,int verbose,int quiet,picture * pic)316 static inline int get_next_picture(stream_context stream,
317 int verbose,
318 int quiet,
319 picture *pic)
320 {
321 int err;
322 if (stream.is_h262)
323 {
324 h262_picture_p picture;
325 err = get_next_h262_frame(stream.u.h262,verbose,quiet,&picture);
326 if (err) return err;
327 pic->is_h262 = TRUE;
328 pic->u.h262 = picture;
329 if (picture->is_picture)
330 pic->type = picture->picture_coding_type;
331 else
332 pic->type = 0xff;
333 }
334 else
335 {
336 access_unit_p unit;
337 err = get_next_h264_frame(stream.u.h264,quiet,verbose,&unit);
338 if (err) return err;
339 pic->is_h262 = FALSE;
340 pic->u.h264 = unit;
341 }
342 return 0;
343 }
344
free_picture(picture * pic)345 static inline void free_picture(picture *pic)
346 {
347 if (pic->is_h262)
348 free_h262_picture(&pic->u.h262);
349 else
350 free_access_unit(&pic->u.h264);
351 pic->type = 0;
352 }
353
354 /*
355 * Needs to be told if the picture is H.262 or not, because this may be
356 * called on an unused instance of the picture data structure.
357 */
unset_picture(int is_h262,picture * pic)358 static inline void unset_picture(int is_h262,
359 picture *pic)
360 {
361 if (is_h262)
362 pic->u.h262 = NULL;
363 else
364 pic->u.h264 = NULL;
365 pic->is_h262 = is_h262;
366 }
367
is_null_picture(picture pic)368 static inline int is_null_picture(picture pic)
369 {
370 if (pic.is_h262)
371 return pic.u.h262 == NULL;
372 else
373 return pic.u.h264 == NULL;
374 }
375
376 // NB: there is already a macro called "is_seq_header"
is_non_frame(picture pic)377 static inline int is_non_frame(picture pic)
378 {
379 return (pic.is_h262 && pic.type == 0xff);
380 }
381
is_reference_picture(picture pic)382 static inline int is_reference_picture(picture pic)
383 {
384 if (pic.is_h262)
385 return (pic.type == 1 || pic.type == 2);
386 else
387 return (pic.u.h264->primary_start != NULL &&
388 pic.u.h264->primary_start->nal_ref_idc != 0);
389 }
390
is_I_or_IDR_picture(picture pic)391 static inline int is_I_or_IDR_picture(picture pic)
392 {
393 if (pic.is_h262)
394 return (pic.type == 1);
395 else
396 return (pic.u.h264->primary_start != NULL &&
397 pic.u.h264->primary_start->nal_ref_idc != 0 &&
398 (pic.u.h264->primary_start->nal_unit_type == NAL_IDR ||
399 all_slices_I(pic.u.h264)));
400 }
401
print_picture(picture pic)402 static void print_picture(picture pic)
403 {
404 if (pic.is_h262)
405 {
406 if (pic.type == 0xff)
407 printf("sequence header");
408 else
409 printf("%s picture",H262_PICTURE_CODING_STR(pic.type));
410 }
411 else
412 {
413 if (pic.u.h264->primary_start == NULL)
414 printf("<null>");
415 else
416 printf("idc %d/type %d (%s)",
417 pic.u.h264->primary_start->nal_ref_idc,
418 pic.u.h264->primary_start->nal_unit_type,
419 NAL_UNIT_TYPE_STR(pic.u.h264->primary_start->nal_unit_type));
420 }
421 }
422
write_picture_as_TS(stream_context stream,TS_writer_p output,picture pic)423 static inline int write_picture_as_TS(stream_context stream,
424 TS_writer_p output,
425 picture pic)
426 {
427 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
428 if (stream.is_h262)
429 return write_h262_picture_as_TS(output,pic.u.h262,
430 es->reader->output_video_pid);
431 else
432 return write_access_unit_as_TS(pic.u.h264,stream.u.h264,
433 output,es->reader->output_video_pid);
434 }
435
reset_filter_context(filter_context fcontext,int frequency)436 static inline void reset_filter_context(filter_context fcontext,
437 int frequency)
438 {
439 if (fcontext.is_h262)
440 {
441 reset_h262_filter_context(fcontext.u.h262);
442 fcontext.u.h262->freq = frequency;
443 }
444 else
445 {
446 reset_h264_filter_context(fcontext.u.h264);
447 fcontext.u.h264->freq = frequency;
448 }
449 }
450
get_next_stripped(filter_context fcontext,int verbose,int quiet,picture * seq_hdr,picture * this_picture,int * delta_pictures_seen)451 static inline int get_next_stripped(filter_context fcontext,
452 int verbose,
453 int quiet,
454 picture *seq_hdr,
455 picture *this_picture,
456 int *delta_pictures_seen)
457 {
458 int err;
459
460 unset_picture(fcontext.is_h262,seq_hdr);
461 unset_picture(fcontext.is_h262,this_picture);
462
463 if (fcontext.is_h262)
464 {
465 h262_picture_p _this_picture = NULL;
466 h262_picture_p _seq_hdr = NULL;
467 err = get_next_stripped_h262_frame(fcontext.u.h262,verbose,quiet,&_seq_hdr,
468 &_this_picture,delta_pictures_seen);
469 seq_hdr->u.h262 = _seq_hdr;
470 this_picture->u.h262 = _this_picture;
471 }
472 else
473 {
474 access_unit_p this_unit = NULL;
475 err = get_next_stripped_h264_frame(fcontext.u.h264,verbose,quiet,
476 &this_unit,delta_pictures_seen);
477 this_picture->u.h264 = this_unit;
478 }
479 return err;
480 }
481
get_next_filtered(filter_context fcontext,int verbose,int quiet,picture * seq_hdr,picture * this_picture,int * delta_pictures_seen)482 static inline int get_next_filtered(filter_context fcontext,
483 int verbose,
484 int quiet,
485 picture *seq_hdr,
486 picture *this_picture,
487 int *delta_pictures_seen)
488 {
489 int err;
490
491 unset_picture(fcontext.is_h262,seq_hdr);
492 unset_picture(fcontext.is_h262,this_picture);
493
494 if (fcontext.is_h262)
495 {
496 h262_picture_p _this_picture = NULL;
497 h262_picture_p _seq_hdr = NULL;
498 err = get_next_filtered_h262_frame(fcontext.u.h262,verbose,quiet,&_seq_hdr,
499 &_this_picture,delta_pictures_seen);
500 seq_hdr->u.h262 = _seq_hdr;
501 this_picture->u.h262 = _this_picture;
502 }
503 else
504 {
505 access_unit_p this_unit = NULL;
506 err = get_next_filtered_h264_frame(fcontext.u.h264,verbose,quiet,
507 &this_unit,delta_pictures_seen);
508 this_picture->u.h264 = this_unit;
509 }
510 return err;
511 }
512
513 // ============================================================
514 // A common view of handling the two types of data stream
515 // ============================================================
516 /*
517 * Playing at normal speed happens as a "side effect" of gathering
518 * information to allow us to reverse. Basically, each time a PES packet
519 * is read in, it gets automatically written out for us, whilst we
520 * analyse its contents.
521 *
522 * Returns 0 if all went well, EOF if the end of file is reached,
523 * otherwise 1 if an error occurred.
524 *
525 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
526 * if the current command has changed.
527 */
play_normal(stream_context stream,TS_writer_p output,int verbose,int quiet,int num_normal,int tsdirect,reverse_data_p reverse_data)528 static int play_normal(stream_context stream,
529 TS_writer_p output,
530 int verbose,
531 int quiet,
532 int num_normal,
533 int tsdirect,
534 reverse_data_p reverse_data)
535 {
536 int err;
537 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
538 PES_reader_p reader = es->reader;
539
540 if (extra_info) printf("Playing at normal speed\n");
541
542 /* Do not write program data if we're in tsdirect mode -
543 * it'll change and some programs can't cope
544 */
545 if (!tsdirect)
546 {
547 err = write_program_data(reader,output);
548 if (err) return err;
549 }
550
551 start_server_output(reader);
552
553 if (stream.is_h262)
554 {
555 err = collect_reverse_h262(stream.u.h262,num_normal,verbose,quiet);
556 if (err) return err;
557 }
558 else
559 {
560 err = collect_reverse_access_units(stream.u.h264,num_normal,verbose,quiet);
561 if (err) return err;
562 }
563 return 0;
564 }
565
566 /*
567 * Flush our PES packet after normal play
568 *
569 * Call this with server output still on.
570 *
571 * Returns 0 if all went well, EOF if the end of file is reached,
572 * otherwise 1 if an error occurred.
573 *
574 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
575 * if the current command has changed.
576 */
flush_after_normal(stream_context stream,TS_writer_p output,int verbose,int quiet)577 static int flush_after_normal(stream_context stream,
578 TS_writer_p output,
579 int verbose,
580 int quiet)
581 {
582 int err;
583 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
584 PES_reader_p reader = es->reader;
585 ES_offset item_start;
586
587 if (extra_info) printf("Flushing PES data after normal play\n");
588
589 if (reader->packet == NULL)
590 {
591 // We're apparently at the end of file, so there's not much we can do
592 return 0;
593 }
594
595 // When playing forwards at normal speed, each PES packet is read in,
596 // processed to extract information, and then (automatically) written
597 // out again when the next PES packet is read in.
598 //
599 // However, when we start doing fast forward or reverse, that automatic
600 // output of PES packets is switched off. Thus it is up to us to ensure
601 // that any outstanding data gets output before that.
602 //
603 // A command character is received when a write (of a PES packet) is made,
604 // and such a write is (as said above) triggered when a new PES packet has
605 // to be read in. *That* happens when reading the next ES item requires
606 // reading a byte from the next PES packet. Thus we know that the current
607 // picture started in the last PES packet, and that the ES item that
608 // comes after it ends in the next packet.
609 //
610 // To terminate the data for that picture neatly in the output, we thus
611 // need to output the ES data from this PES packet up to the end of the
612 // current picture.
613
614 if (stream.is_h262)
615 {
616 if (stream.u.h262->last_item == NULL)
617 {
618 if (extra_info) printf(".. no H.262 last item\n");
619 return 0; // not much else we can do
620 }
621 // The ES item that comes after (and thus marks the end of) the
622 // last picture *starts* at:
623 item_start = stream.u.h262->last_item->unit.start_posn;
624 }
625 else
626 {
627 if (stream.u.h264->pending_nal == NULL)
628 {
629 // We ended the previous access unit for some reason that didn't
630 // need to read the next NAL unit, or we've not read anything in yet
631 item_start = es->posn_of_next_byte;
632 }
633 else
634 {
635 // The previous access unit was ended by this "pending" NAL unit,
636 // so the "next" item presumably starts with that...
637 item_start = stream.u.h264->pending_nal->unit.start_posn;
638 }
639 }
640
641 if (extra_info) printf(".. last item starts at " OFFSET_T_FORMAT "/%d\n",
642 item_start.infile,item_start.inpacket);
643
644 // We know we haven't written out any data for the current PES packet
645 // - do we need to?
646 if (item_start.infile < reader->packet->posn)
647 {
648 // The terminating item started in the previous packet, which we've
649 // already output. We should read in the next picture, and output
650 // that part of it which hasn't already been output.
651 picture picture;
652 if (extra_info) printf(".. which is in the previous packet - "
653 "reading spanning picture into next packet\n");
654
655 err = get_next_picture(stream,verbose,quiet,&picture);
656 if (err == EOF)
657 {
658 // Clearly there is no next picture
659 if (extra_info) printf("End of file\n");
660 return err;
661 }
662 else if (err)
663 {
664 fprintf(stderr,"### Error trying to read into next packet whilst"
665 " flushing after normal play\n");
666 return 1;
667 }
668 free_picture(&picture);
669 // We now know that we want to output from the current PES packet to the
670 // end of this picture, which is one byte before the new terminating
671 // item
672 if (stream.is_h262)
673 item_start = stream.u.h262->last_item->unit.start_posn;
674 else
675 {
676 if (stream.u.h264->pending_nal == NULL)
677 item_start = es->posn_of_next_byte;
678 else
679 item_start = stream.u.h264->pending_nal->unit.start_posn;
680 }
681 if (extra_info) printf(".. new last item starts at " OFFSET_T_FORMAT
682 "/%d\n",item_start.infile,item_start.inpacket);
683 }
684
685 if (item_start.inpacket == 0)
686 {
687 // The terminating item started at the beginning of this packet,
688 // so we don't have any outstanding data to output.
689 if (extra_info) printf(".. so there's no need to output any of this"
690 " packet\n");
691 return 0;
692 }
693
694 // We need to output whatever came before the terminating item
695 if (extra_info) printf(".. so need to output %d bytes of this"
696 " packet\n",item_start.inpacket);
697
698 err = write_ES_as_TS_PES_packet(output,
699 reader->packet->es_data,
700 item_start.inpacket,
701 reader->output_video_pid,
702 DEFAULT_VIDEO_STREAM_ID);
703 if (err)
704 {
705 fprintf(stderr,
706 "### Error flushing start of PES packet after normal play\n");
707 return 1;
708 }
709 return 0;
710 }
711
712 /*
713 * Output the next reference picture. If `I_only` then only output the next
714 * I (or IDR) picture.
715 *
716 * Returns 0 if all went well, EOF if the end of file is reached,
717 * otherwise 1 if an error occurred.
718 */
output_next_reference_picture(stream_context stream,TS_writer_p output,int verbose,int quiet,int I_only)719 static int output_next_reference_picture(stream_context stream,
720 TS_writer_p output,
721 int verbose,
722 int quiet,
723 int I_only)
724 {
725 int err;
726 picture picture;
727
728 if (extra_info) printf(".. outputting next reference picture\n");
729
730 for (;;)
731 {
732 err = get_next_picture(stream,verbose,quiet,&picture);
733 if (err == EOF)
734 {
735 // Clearly there is no next picture - so we can't output it
736 if (extra_info) printf("End of file\n");
737 return err;
738 }
739 else if (err)
740 {
741 fprintf(stderr,"### Error trying to resynchronise after fast forward\n");
742 return 1;
743 }
744
745 if (extra_info)
746 {
747 printf(".. read next picture: ");
748 print_picture(picture);
749 printf("\n");
750 }
751
752 if (is_non_frame(picture))
753 {
754 // A sequence header doesn't help us directly, but we can output
755 // it as it will in practise be followed by an I picture
756 // A sequence end will be followed by a sequence header, so we can
757 // treat it similarly
758 if (extra_info) printf(".. writing it out\n");
759 err = write_picture_as_TS(stream,output,picture);
760 if (err)
761 {
762 fprintf(stderr,"### Error writing out picture list\n");
763 free_picture(&picture);
764 return 1;
765 }
766 }
767 else if (( I_only && is_I_or_IDR_picture(picture)) ||
768 (!I_only && is_reference_picture(picture)))
769 {
770 if (extra_info) printf(".. picture acceptable\n");
771 break;
772 }
773 free_picture(&picture);
774 }
775 // So we've got something sensible to continue with
776 // - don't forget to write it out!
777 if (extra_info) printf(".. writing it out\n");
778 err = write_picture_as_TS(stream,output,picture);
779 if (err)
780 {
781 fprintf(stderr,"### Error writing out picture list\n");
782 free_picture(&picture);
783 return 1;
784 }
785 free_picture(&picture);
786 return 0;
787 }
788
789 /*
790 * Resynchronise after reverse, ready for forwards playing (at whatever speed)
791 *
792 * Always call this immediately after reversing.
793 *
794 * Returns 0 if all went well, EOF if the end of file is reached,
795 * otherwise 1 if an error occurred.
796 *
797 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
798 * if the current command has changed.
799 */
resync_after_reverse(stream_context stream,TS_writer_p output,int verbose,int quiet)800 static int resync_after_reverse(stream_context stream,
801 TS_writer_p output,
802 int verbose,
803 int quiet)
804 {
805 int err;
806 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
807
808 if (extra_info) printf("\nResynchronising PES packets after reverse\n");
809
810 // When reversing, data is read directly from the required locations
811 // in the input file, without using the normal "get next picture"
812 // mechanisms.
813 //
814 // When we *stop* reversing, we need to "pretend" to have read to the
815 // (end of the) last picture output by the normal mechanisms
816
817 // Undo any memory of previous pictures/context
818 reset_stream(stream);
819
820 if (extra_info)
821 printf(" triple byte = %02x,%02x,%02x, next byte to be from "
822 OFFSET_T_FORMAT "/%d\n",
823 es->prev2_byte,es->prev1_byte,es->cur_byte,
824 es->posn_of_next_byte.infile,
825 es->posn_of_next_byte.inpacket);
826
827 // @@@ The following is not true, methinks, as we've been outputting IDR
828 // and I frames (since there are not enough I frames, in hp-trail
829 // at least). On the other hand, there's not much we can do about it.
830 // If we've just been reversing H.264 data, we know we've just output an
831 // IDR, and we also know that IDRs act as "backstops" for B pictures - they
832 // can't refer "through" them. Thus we don't need to worry about outputting
833 // anything extra.
834
835 // @@@ Even for H.264, it may be safer to output another reference picture,
836 // and it does help get the internal datastructures back in synch.
837 //if (!stream.is_h262)
838 // return 0;
839
840 // However, if it's H.262 data, we know we've just output a reference
841 // picture (specifically, an I picture), but that we can't safely output
842 // a B picture until we've output the *next* reference picture, since B
843 // pictures need to refer "back" (in decoding order - back and forwards
844 // in "play" order) to two reference pictures.
845 err = output_next_reference_picture(stream,output,verbose,quiet,FALSE);
846 if (err == EOF)
847 return EOF;
848 else if (err)
849 {
850 fprintf(stderr,"### Error outputting next reference picture,"
851 " after reversing\n");
852 return 1;
853 }
854 return 0;
855 }
856
857 /*
858 * "Rewind" to the start of our stream, ready to start again from the
859 * beginning.
860 *
861 * Returns 0 if all went well, EOF if the end of file is reached,
862 * otherwise 1 if an error occurred.
863 *
864 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
865 * if the current command has changed.
866 */
rewind_stream(stream_context stream)867 static int rewind_stream(stream_context stream)
868 {
869 if (extra_info) printf("\nRewinding\n");
870 if (stream.is_h262)
871 return rewind_h262_context(stream.u.h262);
872 else
873 return rewind_access_unit_context(stream.u.h264);
874 }
875
876 /*
877 * Resynchronise playing after fast fast forwarding. Always call this when
878 * changing from fast forward back to normal speed playing. We want to
879 * be in video only mode for this function.
880 *
881 * Returns 0 if all went well, EOF if the end of file is reached,
882 * otherwise 1 if an error occurred.
883 *
884 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
885 * if the current command has changed.
886 */
resync_after_filter(stream_context stream,TS_writer_p output,int verbose,int quiet)887 static int resync_after_filter(stream_context stream,
888 TS_writer_p output,
889 int verbose,
890 int quiet)
891 {
892 int err;
893
894 if (extra_info) printf("\nResynchronising after fast fast forward\n");
895
896 // Fast forwarding with "filter" drops reference frames.
897 // B pictures refer "back" (in decoding order) to two the last two
898 // reference frames (although, in H.264 an IDR acts as a "stop" to this).
899 //
900 // If we've just been filtering, we know we just output *some* reference
901 // picture, but we probably (almost certainly) hadn't output the preceding
902 // reference picture.
903 //
904 // Specifically, typical data might be laid out as (using the output
905 // of esdots, slightly massaged)::
906 //
907 // [E>iE bE bE pE bE bE pE bE bE ... <next sequence>
908 //
909 // get_next_filtered_picture() returns us the "i" picture, and if we
910 // then get the 'obvious' next data, we'll end up with a "b" picture,
911 // which is not what we want. Thus we need to read forwards until
912 // we reach the next "i" or "p" picture (in this case, it would be the
913 // next "p" picture).
914
915 // @@@ For H.264, it might perhaps make more sense to "reverse" to the
916 // last IDR, output *that*, and then just continue playing. We know that
917 // B pictures can't refer backwards "through" an IDR. This might also
918 // *look* better when we've finished fast forwarding...
919
920 // So...
921 err = output_next_reference_picture(stream,output,verbose,quiet,FALSE);
922 if (err == EOF)
923 return EOF;
924 else if (err)
925 {
926 fprintf(stderr,"### Error outputting next reference picture,"
927 " after fast forwarding\n");
928 return 1;
929 }
930 return 0;
931 }
932
933 /*
934 * Resynchronise playing ready for normal playing again.
935 *
936 * Call this with server output off, but turn it on again immediately
937 * after this call.
938 *
939 * Returns 0 if all went well, EOF if the end of file is reached,
940 * otherwise 1 if an error occurred.
941 *
942 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
943 * if the current command has changed.
944 */
back_to_normal(stream_context stream,TS_writer_p output,int tsdirect)945 static int back_to_normal(stream_context stream,
946 TS_writer_p output,
947 int tsdirect)
948 {
949 int err;
950 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
951 PES_reader_p reader = es->reader;
952 ES_offset item_start;
953
954 if (extra_info) printf("\nResynchronising PES packets for normal play\n");
955
956
957 if (reader->packet == NULL)
958 {
959 // We appear to have reached the end of file - there's not much
960 // we can do about this, nor (probably) should we
961 return 0;
962 }
963
964 if (!tsdirect)
965 {
966 // It can't hurt to reiterate the program data, and if we were just
967 // playing a different program stream, it's a good idea
968 err = write_program_data(reader,output);
969 if (err) return err;
970 }
971
972 // When playing forwards at normal speed, each PES packet is read in,
973 // processed to extract information, and then (automatically) written
974 // out again when the next PES packet is read in.
975 //
976 // However, if we have been fast forwarding (at whatever speed), then we
977 // will have been outputting only some pictures, and not outputting PES
978 // packets automatically.
979 //
980 // In this case, we need to make it appear as if the (rest of the)
981 // current PES packet had been output by the automatic mechanisms.
982 //
983 // In this context, "the rest of the PES packet" means all the data
984 // (in this PES packet) from the start of the H.262 item that stopped us
985 // reading the last picture
986 //
987 // However, if we have instead been reversing, then we do not have a last
988 // item (since reversing just outputs uninterpreted chunks of data). We do,
989 // however, still know the first byte of the next piece of information after
990 // that chunk of data, and that should be enough.
991 if (stream.is_h262)
992 {
993 if (stream.u.h262->last_item == NULL)
994 {
995 if (extra_info) printf(".. no H.262 last item, presumably been"
996 " reversing\n");
997 item_start = es->posn_of_next_byte;
998 // In which case, we've already output the data for our "last" item
999 // and only some of the following cases can occur...
1000 }
1001 else
1002 {
1003 // The ES item that comes after (and thus marks the end of) the last
1004 // picture *starts* at:
1005 item_start = stream.u.h262->last_item->unit.start_posn;
1006 }
1007 }
1008 else
1009 {
1010 if (stream.u.h264->pending_nal == NULL)
1011 {
1012 // Either we ended the previous access unit for some reason that
1013 // didn't need to read the next NAL unit, or we've been reversing
1014 // (or we just started and there was no previous access unit)
1015 item_start = es->posn_of_next_byte;
1016 }
1017 else
1018 {
1019 // The previous access unit was ended by this "pending" NAL unit,
1020 // so the "next" item presumably starts with that...
1021 item_start = stream.u.h264->pending_nal->unit.start_posn;
1022 }
1023 }
1024
1025 if (extra_info)
1026 {
1027 printf(".. posn_of_next_byte is " OFFSET_T_FORMAT "/%d\n",
1028 es->posn_of_next_byte.infile,es->posn_of_next_byte.inpacket);
1029
1030 if (stream.is_h262)
1031 {
1032 if (stream.u.h262->last_item)
1033 {
1034 printf(" last item starts at " OFFSET_T_FORMAT "/%d,\n",
1035 stream.u.h262->last_item->unit.start_posn.infile,
1036 stream.u.h262->last_item->unit.start_posn.inpacket);
1037 print_data(stdout," last item",
1038 stream.u.h262->last_item->unit.data,
1039 stream.u.h262->last_item->unit.data_len,20);
1040 }
1041 }
1042 else
1043 {
1044 if (stream.u.h264->pending_nal)
1045 {
1046 printf(" last item starts at " OFFSET_T_FORMAT "/%d,\n",
1047 stream.u.h264->pending_nal->unit.start_posn.infile,
1048 stream.u.h264->pending_nal->unit.start_posn.inpacket);
1049 print_data(stdout," pending NAL unit",
1050 stream.u.h264->pending_nal->unit.data,
1051 stream.u.h264->pending_nal->unit.data_len,20);
1052 }
1053 }
1054 printf(".. i.e., last item starts at " OFFSET_T_FORMAT "/%d\n",
1055 item_start.infile,item_start.inpacket);
1056 printf(" PES ES data length is %d\n"
1057 " difference is %d\n",
1058 reader->packet->es_data_len,
1059 reader->packet->es_data_len-item_start.inpacket);
1060 printf(" reader->packet->posn is " OFFSET_T_FORMAT "\n",
1061 reader->packet->posn);
1062 }
1063
1064 if (item_start.infile < reader->packet->posn)
1065 {
1066 // Said last item started in the previous PES packet
1067 // - we need to output the part of it that is in that previous packet
1068 // Given the next byte to be read (from this PES packet)
1069 int32_t curposn = es->posn_of_next_byte.inpacket;
1070 // we can work out how much of the item was in the previous packet
1071 // (sanity check - if the next byte to read was 1, then we've read one
1072 // byte from the current packet, and the following should indeed be right
1073 // - look at pes.c:read_PES_ES_byte and es.c:next_triple_byte for details)
1074 int32_t length_wanted;
1075
1076 if (stream.is_h262)
1077 {
1078 length_wanted = stream.u.h262->last_item->unit.data_len - curposn;
1079 if (extra_info) printf(".. next byte is %d, so length wanted is %d"
1080 " - outputting it\n",curposn,length_wanted);
1081 err = write_ES_as_TS_PES_packet(output,
1082 stream.u.h262->last_item->unit.data,
1083 length_wanted,
1084 reader->output_video_pid,
1085 DEFAULT_VIDEO_STREAM_ID);
1086 }
1087 else
1088 {
1089 // @@@ For H.264, do we know, when we get here, that we always
1090 // have a pending NAL unit?
1091 length_wanted = stream.u.h264->pending_nal->unit.data_len - curposn;
1092 if (extra_info) printf(".. next byte is %d, so length wanted is %d"
1093 " - outputting it\n",curposn,length_wanted);
1094 err = write_ES_as_TS_PES_packet(output,
1095 stream.u.h264->pending_nal->unit.data,
1096 length_wanted,
1097 reader->output_video_pid,
1098 DEFAULT_VIDEO_STREAM_ID);
1099 }
1100 if (err)
1101 {
1102 fprintf(stderr,
1103 "### Error flushing (start of) last item after fast forward\n");
1104 return 1;
1105 }
1106 // That leaves us with the whole of this packet still to output,
1107 // and we can leave that to the automated mechanism next time it
1108 // reads in a new PES packet
1109 }
1110 else if (item_start.inpacket == 0)
1111 {
1112 // Said last item started at the start of this PES packet
1113 // so there's nothing to flush, and we can leave the automated
1114 // mechanism to sort out this packet, as above
1115 if (extra_info) printf(".. i.e., at start of packet, nothing to do\n");
1116 }
1117 else
1118 {
1119 // Said last item starts part way through this PES packet
1120 int32_t start_offset = item_start.inpacket;
1121 int32_t length_wanted = reader->packet->es_data_len - start_offset;
1122 if (extra_info)
1123 {
1124 printf(".. so output %d bytes at end of PES packet\n",length_wanted);
1125 print_data(stdout,".. end bytes",&reader->packet->es_data[start_offset],
1126 length_wanted,20);
1127 }
1128
1129 err = write_ES_as_TS_PES_packet(output,
1130 &reader->packet->es_data[start_offset],
1131 length_wanted,
1132 reader->output_video_pid,
1133 DEFAULT_VIDEO_STREAM_ID);
1134 if (err)
1135 {
1136 fprintf(stderr,
1137 "### Error flushing rest of PES packet after fast forward\n");
1138 return 1;
1139 }
1140
1141 // That's all very well, but when the server restarts, and a call is made
1142 // to read (the next) PES packet in, it will attempt to write *this* PES
1143 // packet out again. So tell it not to do that...
1144 reader->dont_write_current_packet = TRUE;
1145 }
1146 return 0;
1147 }
1148
1149 /*
1150 * Read PES packets and write them out to the target, fast forward.
1151 *
1152 * Returns 0 if all went well, EOF if the end of file is reached,
1153 * otherwise 1 if an error occurred.
1154 *
1155 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
1156 * if the current command has changed.
1157 */
play_stripped(stream_context stream,filter_context fcontext,TS_writer_p output,int verbose,int quiet,int tsdirect,int num_fast,int with_seq_hdrs)1158 static int play_stripped(stream_context stream,
1159 filter_context fcontext,
1160 TS_writer_p output,
1161 int verbose,
1162 int quiet,
1163 int tsdirect,
1164 int num_fast,
1165 int with_seq_hdrs)
1166 {
1167 int err;
1168 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
1169 PES_reader_p reader = es->reader;
1170 int total_pictures = 0;
1171
1172 // stop_server_output(reader);
1173
1174 // And then reset our filter context so that we start filtering without
1175 // remembering anything about last time we filtered
1176 reset_filter_context(fcontext,0);
1177
1178 if (!tsdirect)
1179 {
1180 // Ensure we've got program data available (probably not necessary,
1181 // but unlikely to hurt)
1182 err = write_program_data(reader,output);
1183 if (err) return err;
1184 }
1185
1186 if (extra_info) printf("Fast forwarding (strip)\n");
1187
1188 for (;;)
1189 {
1190 picture this_picture;
1191 picture seq_hdr; // H.262 only - *we* mustn't free this one
1192 int delta_pictures_seen;
1193
1194 if (tswrite_command_changed(output))
1195 return COMMAND_RETURN_CODE;
1196
1197 err = get_next_stripped(fcontext,verbose,quiet,
1198 &seq_hdr,&this_picture,&delta_pictures_seen);
1199 if (err == EOF || err == COMMAND_RETURN_CODE)
1200 {
1201 return err;
1202 }
1203 else if (err)
1204 {
1205 fprintf(stderr,"### Error getting next stripped picture\n");
1206 return 1;
1207 }
1208 if (with_seq_hdrs && !is_null_picture(seq_hdr))
1209 {
1210 err = write_picture_as_TS(stream,output,seq_hdr);
1211 if (err)
1212 {
1213 fprintf(stderr,"### Error writing out sequence header\n");
1214 free_picture(&this_picture);
1215 return 1;
1216 }
1217 }
1218 err = write_picture_as_TS(stream,output,this_picture);
1219 if (err)
1220 {
1221 fprintf(stderr,"### Error writing out picture list\n");
1222 free_picture(&this_picture);
1223 return 1;
1224 }
1225 free_picture(&this_picture);
1226
1227 total_pictures += delta_pictures_seen;
1228 if (num_fast > 0 && total_pictures > num_fast)
1229 break;
1230 }
1231 return 0;
1232 }
1233
1234 /*
1235 * Read PES packets and write them out to the target, fast fast forward.
1236 *
1237 * Returns 0 if all went well, EOF if the end of file is reached,
1238 * otherwise 1 if an error occurred.
1239 *
1240 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
1241 * if the current command has changed.
1242 */
play_filtered(stream_context stream,filter_context fcontext,TS_writer_p output,int verbose,int quiet,int tsdirect,int num_faster,int frequency,int with_seq_hdrs)1243 static int play_filtered(stream_context stream,
1244 filter_context fcontext,
1245 TS_writer_p output,
1246 int verbose,
1247 int quiet,
1248 int tsdirect,
1249 int num_faster,
1250 int frequency,
1251 int with_seq_hdrs)
1252 {
1253 int err;
1254 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
1255 PES_reader_p reader = es->reader;
1256
1257 picture this_picture;
1258 picture last_picture;
1259 picture seq_hdr; // H.262 only - *we* mustn't free this one
1260
1261 int total_pictures = 0;
1262
1263 // stop_server_output(reader);
1264 // Reset our filter context so that we start filtering without remembering
1265 // anything about last time we filtered
1266 reset_filter_context(fcontext,frequency);
1267
1268 if (!tsdirect)
1269 {
1270 // Ensure we've got program data available (probably not necessary,
1271 // but unlikely to hurt)
1272 err = write_program_data(reader,output);
1273 if (err) return err;
1274 }
1275
1276 if (extra_info) printf("Fast forwarding (filter)\n");
1277
1278 unset_picture(stream.is_h262,&this_picture);
1279 unset_picture(stream.is_h262,&last_picture);
1280 for (;;)
1281 {
1282 int delta_pictures_seen;
1283 if (tswrite_command_changed(output))
1284 {
1285 free_picture(&last_picture);
1286 err = COMMAND_RETURN_CODE;
1287 break;
1288 }
1289
1290 err = get_next_filtered(fcontext,verbose,quiet,
1291 &seq_hdr,&this_picture,&delta_pictures_seen);
1292 if (err == EOF || err == COMMAND_RETURN_CODE)
1293 {
1294 free_picture(&last_picture);
1295 break;
1296 }
1297 else if (err)
1298 {
1299 fprintf(stderr,"### Error getting next filtered picture\n");
1300 free_picture(&last_picture);
1301 return 1;
1302 }
1303 if (is_null_picture(this_picture))
1304 {
1305 // We need to repeat the last picture
1306 this_picture = last_picture;
1307 unset_picture(stream.is_h262,&last_picture);
1308 }
1309 if (!is_null_picture(this_picture))
1310 {
1311 if (with_seq_hdrs && !is_null_picture(seq_hdr))
1312 {
1313 err = write_picture_as_TS(stream,output,seq_hdr);
1314 if (err)
1315 {
1316 fprintf(stderr,"### Error writing out sequence header\n");
1317 free_picture(&this_picture);
1318 free_picture(&last_picture);
1319 return 1;
1320 }
1321 }
1322 err = write_picture_as_TS(stream,output,this_picture);
1323 if (err)
1324 {
1325 fprintf(stderr,"### Error writing out picture\n");
1326 free_picture(&this_picture);
1327 free_picture(&last_picture);
1328 return 1;
1329 }
1330 }
1331 free_picture(&last_picture);
1332 last_picture = this_picture;
1333
1334 total_pictures += delta_pictures_seen;
1335 if (num_faster > 0 && total_pictures > num_faster)
1336 break;
1337 }
1338
1339 // We *do* end up here if we run out of for loop...
1340 free_picture(&last_picture);
1341
1342 if (err == EOF)
1343 {
1344 // If we reached the end of the file, then back up to the final
1345 // picture in the reversing arrays, and output that (so that the
1346 // user *sees* that final picture).
1347 //
1348 // (The last picture in the reversing arrays will be the last I or IDR
1349 // frame. We know that we are only outputting I or IDR frames, so we
1350 // know that this would also be the last frame we'd have considered
1351 // outputting. It's possible we've already output it, but on the whole
1352 // that shouldn't be terribly obvious to the user, I think.)
1353 reverse_data_p reverse_data = EXTRACT_REVERSE_FROM_STREAM(stream);
1354 // Try going back 2 I/IDR pictures...
1355 err = output_from_reverse_data_as_TS(es,output,verbose,quiet,2,
1356 reverse_data);
1357 if (err && err != COMMAND_RETURN_CODE)
1358 {
1359 fprintf(stderr,"### Error outputting 'last' picture at EOF\n");
1360 return err;
1361 }
1362 // Which means we need to adjust back to normal playing *this* way
1363 err = resync_after_reverse(stream,output,verbose,quiet);
1364 if (err) return err;
1365 // Let the caller know what we did/where we are
1366 return EOF;
1367 }
1368 else
1369 {
1370 // Adjust back to normal playing
1371 err = resync_after_filter(stream,output,verbose,quiet);
1372 if (err) return err;
1373 }
1374 return 0;
1375 }
1376
1377 /*
1378 * Returns 0 if all went well, EOF if the end of file is reached,
1379 * otherwise 1 if an error occurred.
1380 *
1381 * - `num_to_skip` is the number of frames to skip
1382 *
1383 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
1384 * if the current command has changed.
1385 */
skip_forwards(stream_context stream,TS_writer_p output,filter_context fcontext,int with_seq_hdrs,int num_to_skip,int verbose,int quiet,int tsdirect)1386 static int skip_forwards(stream_context stream,
1387 TS_writer_p output,
1388 filter_context fcontext,
1389 int with_seq_hdrs,
1390 int num_to_skip,
1391 int verbose,
1392 int quiet,
1393 int tsdirect)
1394 {
1395 int err;
1396 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
1397 PES_reader_p reader = es->reader;
1398 picture this_picture;
1399 picture seq_hdr; // H.262 only - *we* mustn't free this one
1400 int delta_pictures_seen;
1401
1402 #if TIME_SKIPPING
1403 time_t start_time,end_time;
1404 clock_t start_clock,end_clock;
1405 start_time = time(NULL);
1406 start_clock = clock();
1407 #endif
1408
1409 // Reset our filter context so that we start filtering without remembering
1410 // anything about last time we filtered
1411 reset_filter_context(fcontext,num_to_skip);
1412
1413 if (!tsdirect)
1414 {
1415 // Ensure we've got program data available (probably not necessary,
1416 // but unlikely to hurt)
1417 err = write_program_data(reader,output);
1418 if (err) return err;
1419 }
1420
1421 if (extra_info) printf("Skipping forwards (%d frames)\n",num_to_skip);
1422
1423 unset_picture(stream.is_h262,&this_picture);
1424
1425 // Say that we don't want our skipping to be interrupted by the next command
1426 tswrite_set_command_atomic(output,TRUE);
1427
1428 err = get_next_filtered(fcontext,verbose,quiet,
1429 &seq_hdr,&this_picture,&delta_pictures_seen);
1430 if (err && err != EOF)
1431 {
1432 tswrite_set_command_atomic(output,FALSE);
1433 if (err == COMMAND_RETURN_CODE)
1434 return err;
1435 else
1436 {
1437 fprintf(stderr,"### Error skipping pictures\n");
1438 return 1;
1439 }
1440 }
1441
1442 if (err == EOF)
1443 {
1444 // We hit the end of file before finding anything - so we should make
1445 // sure to display the "last" picture (actually, the last I/IDR picture)
1446 // Luckily, we can do that by "reversing" to it...
1447 reverse_data_p reverse_data = EXTRACT_REVERSE_FROM_STREAM(stream);
1448 // Try going back 2 I/IDR pictures...
1449 err = output_from_reverse_data_as_TS(es,output,verbose,quiet,2,
1450 reverse_data);
1451 if (err)
1452 {
1453 fprintf(stderr,"### Error outputting 'last' picture at EOF\n");
1454 tswrite_set_command_atomic(output,FALSE);
1455 return err;
1456 }
1457 // Which means we need to adjust back to normal playing *this* way
1458 err = resync_after_reverse(stream,output,verbose,quiet);
1459 if (err)
1460 {
1461 tswrite_set_command_atomic(output,FALSE);
1462 return err;
1463 }
1464 // Let the caller know what we did/where we are
1465 return EOF;
1466 }
1467 else
1468 {
1469 // Since we're only skipping once, we shouldn't get a NULL (repeat)
1470 // picture back
1471 if (is_null_picture(this_picture))
1472 {
1473 fprintf(stderr,"### Skipping returned a NULL picture\n");
1474 free_picture(&this_picture);
1475 tswrite_set_command_atomic(output,FALSE);
1476 return 1;
1477 }
1478 if (with_seq_hdrs && !is_null_picture(seq_hdr))
1479 {
1480 err = write_picture_as_TS(stream,output,seq_hdr);
1481 if (err)
1482 {
1483 fprintf(stderr,"### Error writing out sequence header\n");
1484 free_picture(&this_picture);
1485 tswrite_set_command_atomic(output,FALSE);
1486 return 1;
1487 }
1488 }
1489 err = write_picture_as_TS(stream,output,this_picture);
1490 if (err)
1491 {
1492 fprintf(stderr,"### Error writing out picture\n");
1493 free_picture(&this_picture);
1494 tswrite_set_command_atomic(output,FALSE);
1495 return 1;
1496 }
1497 free_picture(&this_picture);
1498
1499 // And remember to adjust back to normal playing
1500 err = resync_after_filter(stream,output,verbose,quiet);
1501 if (err)
1502 {
1503 tswrite_set_command_atomic(output,FALSE);
1504 return 1;
1505 }
1506 }
1507
1508 #if TIME_SKIPPING
1509 end_clock = clock();
1510 end_time = time(NULL);
1511 printf("Started skipping at %s",ctime(&start_time));
1512 printf("Finished skipping at %s",ctime(&end_time));
1513 printf("Elapsed time %.3fs\n",difftime(end_time,start_time));
1514 printf("Process time %.3fs\n",
1515 ((double)(end_clock-start_clock)/CLOCKS_PER_SEC));
1516 #endif
1517
1518 // Remember to allow future commands to be interrupted
1519 tswrite_set_command_atomic(output,FALSE);
1520 return 0;
1521 }
1522
1523 /*
1524 * Returns 0 if all went well, EOF if the end of file is reached,
1525 * otherwise 1 if an error occurred.
1526 *
1527 * - `num_to_skip` is the number of frames to skip
1528 *
1529 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
1530 * if the current command has changed.
1531 */
skip_backwards(stream_context stream,TS_writer_p output,int num_to_skip,int verbose,int quiet,int tsdirect,reverse_data_p reverse_data)1532 static int skip_backwards(stream_context stream,
1533 TS_writer_p output,
1534 int num_to_skip,
1535 int verbose,
1536 int quiet,
1537 int tsdirect,
1538 reverse_data_p reverse_data)
1539 {
1540 int err;
1541 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
1542 PES_reader_p reader = es->reader;
1543
1544 if (!tsdirect)
1545 {
1546 // Ensure we've got program data available (probably not necessary,
1547 // but unlikely to hurt)
1548 err = write_program_data(reader,output);
1549 if (err) return err;
1550 }
1551
1552 if (extra_info) printf("Skipping backwards (%d frames)\n",num_to_skip);
1553
1554 // Say that we don't want our skipping to be interrupted by the next command
1555 tswrite_set_command_atomic(output,TRUE);
1556
1557 err = output_in_reverse_as_TS(es,output,num_to_skip,verbose,quiet,
1558 -1,num_to_skip,reverse_data);
1559 if (err && err != COMMAND_RETURN_CODE)
1560 {
1561 fprintf(stderr,"### Error skipping backwards\n");
1562 tswrite_set_command_atomic(output,FALSE);
1563 return err;
1564 }
1565
1566 err = resync_after_reverse(stream,output,verbose,quiet);
1567 if (err)
1568 {
1569 tswrite_set_command_atomic(output,FALSE);
1570 return err;
1571 }
1572
1573 // Remember to allow future commands to be interrupted
1574 tswrite_set_command_atomic(output,FALSE);
1575 return 0;
1576 }
1577
1578 /*
1579 * Write pictures out to the target, in reverse
1580 *
1581 * Returns 0 if all went well, EOF if the end of file is reached,
1582 * otherwise 1 if an error occurred.
1583 *
1584 * If command input is enabled, then it can also return COMMAND_RETURN_CODE
1585 * if the current command has changed.
1586 */
play_reverse(stream_context stream,TS_writer_p output,int verbose,int quiet,int tsdirect,int frequency,int num_reverse,reverse_data_p reverse_data)1587 static int play_reverse(stream_context stream,
1588 TS_writer_p output,
1589 int verbose,
1590 int quiet,
1591 int tsdirect,
1592 int frequency,
1593 int num_reverse,
1594 reverse_data_p reverse_data)
1595 {
1596 int err;
1597 ES_p es = EXTRACT_ES_FROM_STREAM(stream);
1598 PES_reader_p reader = es->reader;
1599
1600 if (extra_info) printf("Reversing\n");
1601
1602 if (tsdirect)
1603 {
1604 // Ensure we've got program data available (probably not necessary,
1605 // but unlikely to hurt)
1606 err = write_program_data(reader,output);
1607 if (err) return err;
1608 }
1609
1610 #if SHOW_REVERSE_DATA
1611 if (extra_info)
1612 {
1613 int ii;
1614 for (ii=0; ii<reverse_data->length; ii++)
1615 if (stream.is_h262 && reverse_data->seq_offset[ii] == 0)
1616 printf("%3d: seqh at " OFFSET_T_FORMAT "/%d for %d\n",
1617 ii,
1618 reverse_data->start_file[ii],
1619 reverse_data->start_pkt[ii],
1620 reverse_data->data_len[ii]);
1621 else
1622 printf("%3d: %4d at " OFFSET_T_FORMAT "/%d for %d\n",
1623 ii,reverse_data->index[ii],
1624 reverse_data->start_file[ii],
1625 reverse_data->start_pkt[ii],
1626 reverse_data->data_len[ii]);
1627 }
1628 #endif
1629
1630 err = output_in_reverse_as_TS(es,output,frequency,verbose,quiet,
1631 -1,num_reverse,reverse_data);
1632 if (err && err != COMMAND_RETURN_CODE)
1633 {
1634 fprintf(stderr,"### Error outputting reversed data\n");
1635 return err;
1636 }
1637
1638 // Adjust back to normal playing
1639 err = resync_after_reverse(stream,output,verbose,quiet);
1640 if (err) return err;
1641 return err;
1642 }
1643
1644 /*
1645 * Read PES packets and write them out to the target, obeying user
1646 * commands as to what to do.
1647 *
1648 * Returns 0 if all went well, EOF if the 'q'uit command has been given,
1649 * 1 if an error occurred.
1650 */
obey_command(char this_command,char last_command,int * index,int started[MAX_INPUT_FILES],PES_reader_p reader[MAX_INPUT_FILES],stream_context stream[MAX_INPUT_FILES],filter_context fcontext[MAX_INPUT_FILES],filter_context scontext[MAX_INPUT_FILES],reverse_data_p reverse_data[MAX_INPUT_FILES],TS_writer_p tswriter,int video_only,int verbose,int quiet,int tsdirect,int with_seq_hdrs,int ffrequency,int rfrequency)1651 static int obey_command(char this_command,
1652 char last_command,
1653 int *index,
1654 int started[MAX_INPUT_FILES],
1655 PES_reader_p reader[MAX_INPUT_FILES],
1656 stream_context stream[MAX_INPUT_FILES],
1657 filter_context fcontext[MAX_INPUT_FILES],
1658 filter_context scontext[MAX_INPUT_FILES],
1659 reverse_data_p reverse_data[MAX_INPUT_FILES],
1660 TS_writer_p tswriter,
1661 int video_only,
1662 int verbose,
1663 int quiet,
1664 int tsdirect,
1665 int with_seq_hdrs,
1666 int ffrequency,
1667 int rfrequency)
1668 {
1669 int err = 0;
1670 int new_stream;
1671 int which = *index; // which stream we're reading
1672
1673 // Loop obeying our given command and any "imaginary" commands that
1674 // result therefrom
1675 for (;;)
1676 {
1677 #ifdef DEBUG_COMMANDS
1678 printf("__ obeying command '%c'\n",this_command);
1679 #endif
1680 switch (this_command)
1681 {
1682 case COMMAND_NORMAL:
1683 if (!quiet) printf("****************************************\n"
1684 "** [%3d] File %d: Forwards, normal speed\n",
1685 tswriter->where.socket,which);
1686 if (last_command != COMMAND_NORMAL && started[which])
1687 {
1688 err = back_to_normal(stream[which],tswriter,tsdirect);
1689 if (err) return 1;
1690 }
1691 started[which] = TRUE;
1692 set_PES_reader_video_only(reader[which],video_only);
1693 err = play_normal(stream[which],tswriter,verbose,quiet,0,
1694 tsdirect, reverse_data[which]);
1695 // If we've had a new command, and it's not 'n' again...
1696 if (err == COMMAND_RETURN_CODE && tswriter->command != COMMAND_NORMAL)
1697 err = flush_after_normal(stream[which],tswriter,verbose,quiet);
1698 break;
1699
1700 case COMMAND_PAUSE:
1701 if (!quiet) printf("****************************************\n"
1702 "** [%3d] File %d: Pause\n",
1703 tswriter->where.socket,which);
1704 stop_server_output(reader[which]);
1705 err = wait_for_command(tswriter);
1706 break;
1707
1708 case COMMAND_FAST:
1709 if (!quiet) printf("****************************************\n"
1710 "** [%3d] File %d: Fast forwards\n",
1711 tswriter->where.socket,which);
1712 stop_server_output(reader[which]);
1713 set_PES_reader_video_only(reader[which],TRUE);
1714 err = play_stripped(stream[which],scontext[which],tswriter,
1715 verbose,quiet,tsdirect,0,with_seq_hdrs);
1716 break;
1717
1718 case COMMAND_FAST_FAST:
1719 if (!quiet) printf("****************************************\n"
1720 "** [%3d] File %d: Fast fast forwards\n",
1721 tswriter->where.socket,which);
1722 stop_server_output(reader[which]);
1723 set_PES_reader_video_only(reader[which],TRUE);
1724 err = play_filtered(stream[which],fcontext[which],tswriter,
1725 verbose,quiet,tsdirect,0,ffrequency,with_seq_hdrs);
1726 break;
1727
1728 case COMMAND_REVERSE:
1729 if (!quiet) printf("****************************************\n"
1730 "** [%3d] File %d: Reverse\n",
1731 tswriter->where.socket,which);
1732 stop_server_output(reader[which]);
1733 set_PES_reader_video_only(reader[which],TRUE);
1734 err = play_reverse(stream[which],tswriter,verbose,quiet,
1735 tsdirect,
1736 rfrequency,0,reverse_data[which]);
1737 if (err == 0)
1738 {
1739 if (!quiet) printf("Start of file %d\n",which);
1740 this_command = COMMAND_PAUSE;
1741 break;
1742 }
1743 break;
1744
1745 case COMMAND_FAST_REVERSE:
1746 if (!quiet) printf("****************************************\n"
1747 "** [%3d] File %d: Reverse (faster)\n",
1748 tswriter->where.socket,which);
1749 stop_server_output(reader[which]);
1750 set_PES_reader_video_only(reader[which],TRUE);
1751 err = play_reverse(stream[which],tswriter,verbose,quiet,
1752 tsdirect,
1753 2*rfrequency,0,reverse_data[which]);
1754 if (err == 0)
1755 {
1756 if (!quiet) printf("Start of file %d\n",which);
1757 this_command = COMMAND_PAUSE;
1758 break;
1759 }
1760 break;
1761
1762 case COMMAND_SKIP_FORWARD:
1763 if (!quiet) printf("****************************************\n"
1764 "** [%3d] File %d: Skip forwards 10 seconds\n",
1765 tswriter->where.socket,which);
1766 stop_server_output(reader[which]);
1767 set_PES_reader_video_only(reader[which],TRUE);
1768 err = skip_forwards(stream[which],tswriter,
1769 fcontext[which],with_seq_hdrs,
1770 SMALL_SKIP_DISTANCE,verbose,quiet,tsdirect);
1771 this_command = COMMAND_NORMAL; // aim to continue with normal play
1772 break;
1773
1774 case COMMAND_SKIP_BACKWARD:
1775 if (!quiet) printf("****************************************\n"
1776 "** [%3d] File %d: Skip backwards 10 seconds\n",
1777 tswriter->where.socket,which);
1778 stop_server_output(reader[which]);
1779 set_PES_reader_video_only(reader[which],TRUE);
1780 err = skip_backwards(stream[which],tswriter,SMALL_SKIP_DISTANCE,
1781 verbose,quiet,tsdirect,reverse_data[which]);
1782 this_command = COMMAND_NORMAL; // aim to continue with normal play
1783 break;
1784
1785 case COMMAND_SKIP_FORWARD_LOTS:
1786 if (!quiet) printf("****************************************\n"
1787 "** [%3d] File %d: Skip forwards 3 minutes\n",
1788 tswriter->where.socket,which);
1789 stop_server_output(reader[which]);
1790 set_PES_reader_video_only(reader[which],TRUE);
1791 err = skip_forwards(stream[which],tswriter,
1792 fcontext[which],with_seq_hdrs,
1793 BIG_SKIP_DISTANCE,verbose,quiet,tsdirect);
1794 this_command = COMMAND_NORMAL; // aim to continue with normal play
1795 break;
1796
1797 case COMMAND_SKIP_BACKWARD_LOTS:
1798 if (!quiet) printf("****************************************\n"
1799 "** [%3d] File %d: Skip backwards 3 minutes\n",
1800 tswriter->where.socket,which);
1801 stop_server_output(reader[which]);
1802 set_PES_reader_video_only(reader[which],TRUE);
1803 err = skip_backwards(stream[which],tswriter,BIG_SKIP_DISTANCE,
1804 verbose,quiet,tsdirect,reverse_data[which]);
1805 this_command = COMMAND_NORMAL; // aim to continue with normal play
1806 break;
1807
1808 case COMMAND_SELECT_FILE_0:
1809 new_stream = 0;
1810 goto change_stream;
1811
1812 case COMMAND_SELECT_FILE_1:
1813 new_stream = 1;
1814 goto change_stream;
1815
1816 case COMMAND_SELECT_FILE_2:
1817 new_stream = 2;
1818 goto change_stream;
1819
1820 case COMMAND_SELECT_FILE_3:
1821 new_stream = 3;
1822 goto change_stream;
1823
1824 case COMMAND_SELECT_FILE_4:
1825 new_stream = 4;
1826 goto change_stream;
1827
1828 case COMMAND_SELECT_FILE_5:
1829 new_stream = 5;
1830 goto change_stream;
1831
1832 case COMMAND_SELECT_FILE_6:
1833 new_stream = 6;
1834 goto change_stream;
1835
1836 case COMMAND_SELECT_FILE_7:
1837 new_stream = 7;
1838 goto change_stream;
1839
1840 case COMMAND_SELECT_FILE_8:
1841 new_stream = 8;
1842 goto change_stream;
1843
1844 case COMMAND_SELECT_FILE_9:
1845 new_stream = 9;
1846 goto change_stream;
1847
1848 change_stream:
1849 if (!quiet) printf("****************************************\n"
1850 "** [%3d] File %d: Select file\n",
1851 tswriter->where.socket,new_stream);
1852 if (reader[new_stream] == NULL)
1853 {
1854 printf(".. No input file defined for stream %d - ignored\n",
1855 new_stream);
1856 }
1857 else
1858 {
1859 #if 0 // The following would only make sense if we *knew* we'd just been doing 'n'ormal play...
1860 // Try to ensure we finish at the end of a picture...
1861 err = flush_after_normal(stream[which],tswriter,verbose,quiet);
1862 if (err && err != COMMAND_RETURN_CODE && err != EOF)
1863 return err;
1864 #endif
1865 // Pause the current stream
1866 stop_server_output(reader[which]);
1867 // Change to the new stream
1868 *index = which = new_stream;
1869 // @@@ For the moment, changing channel also means rewinding
1870 // the "new" channel. This can become a "pure" channel change
1871 // when we have the ability to "go back one(ish) reverse item(s)"
1872 // and guarantee to end up at a sensible place to continue from
1873 // (an IDR for H.264, or a GOP for H.262)
1874 // Rewind it
1875 err = rewind_stream(stream[which]);
1876 if (err) return 1;
1877 // And note that we *are* starting from the beginning again
1878 started[which] = FALSE;
1879 }
1880 // And return to normal playing
1881 this_command = COMMAND_NORMAL;
1882 break;
1883
1884 case COMMAND_QUIT:
1885 if (!quiet) printf("****************************************\n"
1886 "** [%3d] File %d: Quitting\n",
1887 which,tswriter->where.socket);
1888 return EOF;
1889
1890 default:
1891 fprintf(stderr,"!!! Command '%c' ignored\n",this_command);
1892 this_command = COMMAND_NORMAL;
1893 break;
1894 }
1895
1896 // Work out what to do next
1897 switch (err)
1898 {
1899 case 0:
1900 case COMMAND_RETURN_CODE:
1901 break;
1902 case EOF:
1903 if (!quiet) printf("End of file %d\n",which);
1904 this_command = COMMAND_PAUSE;
1905 break;
1906 default:
1907 fprintf(stderr,"!!! Error playing file %d - pausing\n",which);
1908 this_command = COMMAND_PAUSE;
1909 break;
1910 // return 1;
1911 }
1912 if (tswriter->command_changed)
1913 return 0;
1914 }
1915 }
1916
1917 /*
1918 * Read PES packets and write them out to the target, obeying user
1919 * commands as to what to do.
1920 *
1921 * Returns 0 if all went well, 1 if an error occurred.
1922 */
play(int default_index,PES_reader_p reader[MAX_INPUT_FILES],stream_context stream[MAX_INPUT_FILES],filter_context fcontext[MAX_INPUT_FILES],filter_context scontext[MAX_INPUT_FILES],reverse_data_p reverse_data[MAX_INPUT_FILES],TS_writer_p tswriter,int video_only,int verbose,int quiet,int tsdirect,int with_seq_hdrs,int ffrequency,int rfrequency)1923 static int play(int default_index,
1924 PES_reader_p reader[MAX_INPUT_FILES],
1925 stream_context stream[MAX_INPUT_FILES],
1926 filter_context fcontext[MAX_INPUT_FILES],
1927 filter_context scontext[MAX_INPUT_FILES],
1928 reverse_data_p reverse_data[MAX_INPUT_FILES],
1929 TS_writer_p tswriter,
1930 int video_only,
1931 int verbose,
1932 int quiet,
1933 int tsdirect,
1934 int with_seq_hdrs,
1935 int ffrequency,
1936 int rfrequency)
1937 {
1938 int err;
1939 int ii;
1940 int started[MAX_INPUT_FILES];
1941 int which = default_index; // which stream we're reading
1942
1943 // Any function which writes to the output may read a new command character,
1944 // but only if tswriter->command_changed is FALSE. Such a function will then
1945 // return COMMAND_RETURN_CODE.
1946 // When a new command character is read, tswriter->command_changed is set to
1947 // TRUE. It is up to us to set it back to FALSE when we have finished
1948 // dealing with the new command letter.
1949
1950 byte this_command = tswriter->command;
1951 byte last_command = COMMAND_NOT_A_COMMAND;
1952
1953 for (ii=0; ii<MAX_INPUT_FILES; ii++)
1954 started[ii] = FALSE;
1955
1956 // Select our current PES reader
1957 if (reader[which] == NULL)
1958 {
1959 fprintf(stderr,"### Default input stream %d has no associated file\n",
1960 which);
1961 return 1;
1962 }
1963
1964 if (!quiet)
1965 printf("Starting with input stream %d\n",which);
1966
1967 #if 0 // Shouldn't need to do this, as any command that *does* anything will output it
1968 // Ensure we output program data before anything else "sensible"
1969 err = write_program_data(reader[which],tswriter);
1970 if (err) return err;
1971 #endif
1972
1973 for (;;)
1974 {
1975 // It is our job to let the underlying interface know that we are
1976 // ready to read a new command character (i.e., that we have "heard"
1977 // the last one). We do that by unsetting the command-changed flag.
1978 tswriter->command_changed = FALSE;
1979
1980 this_command = tswriter->command;
1981
1982 #ifdef DEBUG_COMMANDS
1983 printf("xx Command is '%c', last command '%c'\n",
1984 this_command,last_command);
1985 #endif
1986
1987 err = obey_command(this_command,last_command,&which,
1988 started,reader,stream,fcontext,scontext,reverse_data,
1989 tswriter,video_only,verbose,quiet,tsdirect,with_seq_hdrs,
1990 ffrequency,rfrequency);
1991 if (err == EOF)
1992 return 0; // The user gave the 'q'uit command
1993 else if (err)
1994 {
1995 fprintf(stderr,"### Error terminated play\n");
1996 return 1;
1997 }
1998 last_command = this_command;
1999 }
2000 }
2001
2002 /*
2003 * Read PES packets and write them out to the target, obeying user
2004 * commands as to what to do.
2005 *
2006 * Returns 0 if all went well, 1 if an error occurred.
2007 */
play_pes_packets(PES_reader_p reader[MAX_INPUT_FILES],TS_writer_p tswriter,tsserve_context_p context,int verbose,int quiet)2008 static int play_pes_packets(PES_reader_p reader[MAX_INPUT_FILES],
2009 TS_writer_p tswriter,
2010 tsserve_context_p context,
2011 int verbose,
2012 int quiet)
2013 {
2014 int err;
2015 int ii;
2016 ES_p es[MAX_INPUT_FILES]; // A view of our PES packets as ES units
2017 reverse_data_p reverse_data[MAX_INPUT_FILES];
2018 stream_context stream[MAX_INPUT_FILES];
2019 filter_context fcontext[MAX_INPUT_FILES];
2020 filter_context scontext[MAX_INPUT_FILES];
2021
2022 if (!quiet)
2023 printf("\nSetting up environment\n");
2024
2025 // Request that packets be written out to the TS writer as a "side effect" of
2026 // reading them in. The default is to write PES packets (just for the video
2027 // and audio data), but the alternative is to write all TS packets (if the
2028 // data *is* TS)
2029 for (ii = 0; ii < MAX_INPUT_FILES; ii++)
2030 {
2031 if (reader[ii] != NULL)
2032 {
2033 set_server_output(reader[ii],tswriter,!context->tsdirect,
2034 context->repeat_program_every);
2035 set_server_padding(reader[ii],context->pes_padding);
2036 }
2037 }
2038
2039 for (ii = 0; ii < MAX_INPUT_FILES; ii++)
2040 {
2041 es[ii] = NULL;
2042 reverse_data[ii] = NULL;
2043
2044 // Closing uninitialised things is a bit dodgy if we don't indicate
2045 // what *type* of unset value is being used. However, in practice
2046 // it doesn't matter much, as both the H.262 and H.264 "destroy"
2047 // functions for streams and filter contexts sensibly do nothing
2048 // with a NULL value - so we might as well just say the same for all...
2049 stream[ii].is_h262 = fcontext[ii].is_h262 = scontext[ii].is_h262 = FALSE;
2050 stream[ii].u.h262 = NULL;
2051 fcontext[ii].u.h262 = scontext[ii].u.h262 = NULL;
2052 }
2053
2054 // Start off our output with some null packets - this is in case the
2055 // reader needs some time to work out its byte alignment before it starts
2056 // looking for 0x47 bytes
2057 for (ii=0; ii<context->pad_start; ii++)
2058 {
2059 err = write_TS_null_packet(tswriter);
2060 if (err) return 1;
2061 }
2062
2063 // And sort out our stack-of-streams atop each input file
2064 for (ii = 0; ii < MAX_INPUT_FILES; ii++)
2065 {
2066 if (reader[ii] == NULL)
2067 continue;
2068
2069 if (!quiet)
2070 printf("Setting up stream %d\n",ii);
2071
2072
2073 // Wrap our PES stream up as an ES stream
2074 // Note that this has the side-effect of reading the first packet
2075 // from the file (so that the ES reader can prime its 3-byte buffer).
2076 // This means that we will have read in the first PES packet, and
2077 // thus (for TS data) potentially quite a few TS packets, which
2078 // may also have included PAT/PMT. Luckily, we rely upon our caller
2079 // to have aleady set up PES or TS mirroring.
2080 err = build_elementary_stream_PES(reader[ii],&es[ii]);
2081 if (err)
2082 {
2083 fprintf(stderr,
2084 "### Error trying to build ES reader for PES reader %d\n",ii);
2085 goto tidy_up;
2086 }
2087
2088 // Put an access unit or H.262 unit context around that
2089 err = build_stream(es[ii],!(reader[ii]->is_h264),ii+1,&stream[ii]);
2090 if (err)
2091 {
2092 fprintf(stderr,"### Unable to build input stream %d\n",ii);
2093 goto tidy_up;
2094 }
2095
2096 // Build our reverse memory datastructure
2097 err = build_and_attach_reverse(stream[ii],&reverse_data[ii]);
2098 if (err)
2099 {
2100 fprintf(stderr,"### Unable to build reverse memory for stream %d\n",ii);
2101 goto tidy_up;
2102 }
2103
2104
2105 // Tell it what PID and stream id to use when outputting reversed data
2106 set_reverse_pid(reverse_data[ii],reader[ii]->output_video_pid,
2107 DEFAULT_VIDEO_STREAM_ID);
2108
2109 if (!context->with_seq_hdrs)
2110 reverse_data[ii]->output_sequence_headers = FALSE;
2111
2112 // Build our fast forwards filter contexts
2113 err = build_filter_context(stream[ii],FALSE,context->ffrequency,&fcontext[ii]);
2114 if (err)
2115 {
2116 fprintf(stderr,"### Unable to build filter context for stream %d\n",ii);
2117 goto tidy_up;
2118 }
2119
2120
2121 err = build_filter_context(stream[ii],TRUE,0,&scontext[ii]);
2122 if (err)
2123 {
2124 fprintf(stderr,"### Unable to build strip context for stream %d\n",ii);
2125 goto tidy_up;
2126 }
2127 }
2128
2129 // And, at last, do what we came for
2130 err = play(context->default_file_index,reader,stream,fcontext,scontext,reverse_data,
2131 tswriter,context->video_only,verbose,quiet,context->tsdirect,
2132 context->with_seq_hdrs,context->ffrequency,context->rfrequency);
2133
2134 tidy_up:
2135 for (ii = 0; ii < MAX_INPUT_FILES; ii++)
2136 {
2137 close_elementary_stream(&es[ii]);
2138 free_reverse_data(&reverse_data[ii]);
2139 close_stream(stream[ii]);
2140 free_filter_context(fcontext[ii]);
2141 free_filter_context(scontext[ii]);
2142 }
2143
2144 return err;
2145 }
2146
2147 /*
2148 * Read PES packets and write them out to the target. Alternate normal
2149 * speed, fast forward and reverse (in some sequence).
2150 *
2151 * Returns 0 if all went well, 1 if an error occurred.
2152 */
test_play(PES_reader_p reader,stream_context stream,filter_context fcontext,filter_context scontext,reverse_data_p reverse_data,TS_writer_p tswriter,int video_only,int verbose,int quiet,int tsdirect,int num_normal,int num_fast,int num_faster,int num_reverse,int ffrequency,int rfrequency,int with_seq_hdrs)2153 static int test_play(PES_reader_p reader,
2154 stream_context stream,
2155 filter_context fcontext,
2156 filter_context scontext,
2157 reverse_data_p reverse_data,
2158 TS_writer_p tswriter,
2159 int video_only,
2160 int verbose,
2161 int quiet,
2162 int tsdirect,
2163 int num_normal,
2164 int num_fast,
2165 int num_faster,
2166 int num_reverse,
2167 int ffrequency,
2168 int rfrequency,
2169 int with_seq_hdrs)
2170 {
2171 int err = 0;
2172 int started = FALSE;
2173 int ii;
2174
2175 if (num_fast == 0 && num_faster == 0 && num_reverse == 0)
2176 {
2177 // Special case -- just play through
2178 printf(">> Just playing at normal speed\n");
2179 set_PES_reader_video_only(reader,video_only);
2180 err = play_normal(stream,tswriter,verbose,quiet,tsdirect,0,reverse_data);
2181 if (err == EOF)
2182 return 0;
2183 else
2184 return err;
2185 }
2186
2187 printf(">> Going through sequence twice\n");
2188
2189 for (ii=0; ii<2; ii++)
2190 {
2191 // ------------------------------------------------------------
2192 if (verbose || extra_info) printf("\n\n");
2193 printf("** Normal speed for %d\n",num_normal);
2194 if (started)
2195 {
2196 err = back_to_normal(stream,tswriter,tsdirect);
2197 if (err) return 1;
2198 }
2199 started = TRUE;
2200
2201 set_PES_reader_video_only(reader,video_only);
2202 err = play_normal(stream,tswriter,verbose,quiet,tsdirect,num_normal,reverse_data);
2203 if (err == EOF)
2204 break;
2205 else if (err)
2206 return 1;
2207
2208 err = flush_after_normal(stream,tswriter,verbose,quiet);
2209 if (err == EOF)
2210 break;
2211 else if (err)
2212 return 1;
2213
2214 stop_server_output(reader);
2215
2216 // ------------------------------------------------------------
2217 if (verbose || extra_info) printf("\n\n");
2218 printf("** Fast forward for %d\n",num_fast);
2219 set_PES_reader_video_only(reader,TRUE);
2220 err = play_stripped(stream,scontext,tswriter,verbose,quiet,tsdirect,
2221 num_fast,
2222 with_seq_hdrs);
2223 if (err == EOF)
2224 break;
2225 else if (err)
2226 return 1;
2227
2228 // ------------------------------------------------------------
2229 if (verbose || extra_info) printf("\n\n");
2230 printf("** Normal speed for %d\n",num_normal);
2231
2232 err = back_to_normal(stream,tswriter,tsdirect);
2233 if (err) return 1;
2234
2235 set_PES_reader_video_only(reader,video_only);
2236 err = play_normal(stream,tswriter,verbose,quiet,tsdirect,num_normal,reverse_data);
2237 if (err == EOF)
2238 break;
2239 else if (err)
2240 return 1;
2241
2242 err = flush_after_normal(stream,tswriter,verbose,quiet);
2243 if (err == EOF)
2244 break;
2245 else if (err)
2246 return 1;
2247
2248 stop_server_output(reader);
2249
2250 // ------------------------------------------------------------
2251 if (verbose || extra_info) printf("\n\n");
2252 printf("** Faster forward for %d\n",num_faster);
2253 set_PES_reader_video_only(reader,TRUE);
2254 err = play_filtered(stream,fcontext,tswriter,verbose,quiet,tsdirect,
2255 num_faster,
2256 ffrequency,with_seq_hdrs);
2257 if (err == EOF)
2258 break;
2259 else if (err)
2260 return 1;
2261
2262 // ------------------------------------------------------------
2263 if (verbose || extra_info) printf("\n\n");
2264 printf("** Normal speed for %d\n",num_normal);
2265
2266 err = back_to_normal(stream,tswriter,tsdirect);
2267 if (err) return 1;
2268
2269 set_PES_reader_video_only(reader,video_only);
2270 err = play_normal(stream,tswriter,verbose,quiet,tsdirect,num_normal,reverse_data);
2271 if (err == EOF)
2272 break;
2273 else if (err)
2274 return 1;
2275
2276 err = flush_after_normal(stream,tswriter,verbose,quiet);
2277 if (err == EOF)
2278 break;
2279 else if (err)
2280 return 1;
2281
2282 stop_server_output(reader);
2283
2284 // ------------------------------------------------------------
2285 if (verbose || extra_info) printf("\n\n");
2286 printf("** Reverse for %d\n",num_reverse);
2287 set_PES_reader_video_only(reader,TRUE);
2288 err = play_reverse(stream,tswriter,verbose,quiet,rfrequency,
2289 tsdirect,
2290 num_reverse,reverse_data);
2291 if (err == EOF)
2292 break;
2293 else if (err)
2294 return 1;
2295 }
2296
2297 if (verbose || extra_info) printf("\n\n");
2298 if (err == EOF)
2299 printf("** End of file\n");
2300 else
2301 printf(">> End of sequences\n");
2302 return 0;
2303 }
2304
2305 /*
2306 * Read PES packets and write them out to the target. Test skipping forwards
2307 * and back.
2308 *
2309 * Returns 0 if all went well, 1 if an error occurred.
2310 */
test_skip(PES_reader_p reader,stream_context stream,filter_context fcontext,filter_context scontext,reverse_data_p reverse_data,TS_writer_p tswriter,int video_only,int verbose,int quiet,int tsdirect,int with_seq_hdrs)2311 static int test_skip(PES_reader_p reader,
2312 stream_context stream,
2313 filter_context fcontext,
2314 filter_context scontext,
2315 reverse_data_p reverse_data,
2316 TS_writer_p tswriter,
2317 int video_only,
2318 int verbose,
2319 int quiet,
2320 int tsdirect,
2321 int with_seq_hdrs)
2322 {
2323 int err = 0;
2324 int num_normal = 100;
2325 int started = FALSE;
2326 int ii;
2327
2328 printf(">> Going through sequence once\n");
2329
2330 for (ii=0; ii<1; ii++)
2331 {
2332 printf("\n>> Iteration %d\n\n",ii);
2333
2334 // ------------------------------------------------------------
2335 if (verbose || extra_info) printf("\n\n");
2336 printf("** Normal speed for %d\n",num_normal);
2337 if (started)
2338 {
2339 err = back_to_normal(stream,tswriter,tsdirect);
2340 if (err) return 1;
2341 }
2342 started = TRUE;
2343
2344 set_PES_reader_video_only(reader,video_only);
2345 err = play_normal(stream,tswriter,verbose,quiet,tsdirect,num_normal,reverse_data);
2346 if (err == EOF)
2347 break;
2348 else if (err)
2349 return 1;
2350
2351 err = flush_after_normal(stream,tswriter,verbose,quiet);
2352 if (err == EOF)
2353 break;
2354 else if (err)
2355 return 1;
2356
2357 // ------------------------------------------------------------
2358 if (verbose || extra_info) printf("\n\n");
2359 printf("** Skip forwards\n");
2360 stop_server_output(reader);
2361 set_PES_reader_video_only(reader,TRUE);
2362 err = skip_forwards(stream,tswriter,fcontext,with_seq_hdrs,
2363 SMALL_SKIP_DISTANCE,verbose,quiet,tsdirect);
2364 if (err == EOF)
2365 break;
2366 else if (err)
2367 return 1;
2368
2369 // ------------------------------------------------------------
2370 if (verbose || extra_info) printf("\n\n");
2371 printf("** Skip forwards\n");
2372 stop_server_output(reader);
2373 set_PES_reader_video_only(reader,TRUE);
2374 err = skip_forwards(stream,tswriter,fcontext,with_seq_hdrs,
2375 SMALL_SKIP_DISTANCE,verbose,quiet,tsdirect);
2376 if (err == EOF)
2377 break;
2378 else if (err)
2379 return 1;
2380
2381 // ------------------------------------------------------------
2382 if (verbose || extra_info) printf("\n\n");
2383 printf("** Normal speed for %d\n",num_normal);
2384
2385 err = back_to_normal(stream,tswriter,tsdirect);
2386 if (err) return 1;
2387
2388 set_PES_reader_video_only(reader,video_only);
2389 err = play_normal(stream,tswriter,verbose,quiet,tsdirect,num_normal,reverse_data);
2390 if (err == EOF)
2391 break;
2392 else if (err)
2393 return 1;
2394
2395 err = flush_after_normal(stream,tswriter,verbose,quiet);
2396 if (err == EOF)
2397 break;
2398 else if (err)
2399 return 1;
2400
2401 // ------------------------------------------------------------
2402 if (verbose || extra_info) printf("\n\n");
2403 printf("** Skip backwards\n");
2404 stop_server_output(reader);
2405 set_PES_reader_video_only(reader,TRUE);
2406 err = skip_backwards(stream,tswriter,1,verbose,quiet,tsdirect,reverse_data);
2407 if (err == EOF)
2408 break;
2409 else if (err)
2410 return 1;
2411
2412 // ------------------------------------------------------------
2413 if (verbose || extra_info) printf("\n\n");
2414 printf("** Skip backwards\n");
2415 stop_server_output(reader);
2416 set_PES_reader_video_only(reader,TRUE);
2417 err = skip_backwards(stream,tswriter,1,verbose,quiet,tsdirect,reverse_data);
2418 if (err == EOF)
2419 break;
2420 else if (err)
2421 return 1;
2422
2423 // ------------------------------------------------------------
2424 if (verbose || extra_info) printf("\n\n");
2425 printf("** Normal speed for %d\n",num_normal);
2426
2427 err = back_to_normal(stream,tswriter,tsdirect);
2428 if (err) return 1;
2429
2430 set_PES_reader_video_only(reader,video_only);
2431 err = play_normal(stream,tswriter,verbose,quiet,tsdirect,num_normal,reverse_data);
2432 if (err == EOF)
2433 break;
2434 else if (err)
2435 return 1;
2436
2437 err = flush_after_normal(stream,tswriter,verbose,quiet);
2438 if (err == EOF)
2439 break;
2440 else if (err)
2441 return 1;
2442
2443 // ------------------------------------------------------------
2444 if (verbose || extra_info) printf("\n\n");
2445 printf("** Skip forwards\n");
2446 stop_server_output(reader);
2447 set_PES_reader_video_only(reader,TRUE);
2448 err = skip_forwards(stream,tswriter,fcontext,with_seq_hdrs,
2449 SMALL_SKIP_DISTANCE,verbose,quiet,tsdirect);
2450 if (err == EOF)
2451 break;
2452 else if (err)
2453 return 1;
2454
2455 // ------------------------------------------------------------
2456 if (verbose || extra_info) printf("\n\n");
2457 printf("** Skip backwards\n");
2458 stop_server_output(reader);
2459 set_PES_reader_video_only(reader,TRUE);
2460 err = skip_backwards(stream,tswriter,1,verbose,quiet,tsdirect,reverse_data);
2461 if (err == EOF)
2462 break;
2463 else if (err)
2464 return 1;
2465 }
2466
2467 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2468 num_normal = 100;
2469 if (verbose || extra_info) printf("\n\n");
2470 printf("** Normal speed for %d\n",num_normal);
2471
2472 err = back_to_normal(stream,tswriter,tsdirect);
2473 if (err) return 1;
2474
2475 set_PES_reader_video_only(reader,video_only);
2476 err = play_normal(stream,tswriter,verbose,quiet,tsdirect,num_normal,reverse_data);
2477 if (err == EOF)
2478 {
2479 printf("** End of file\n");
2480 return 0;
2481 }
2482 else if (err)
2483 return 1;
2484
2485 err = flush_after_normal(stream,tswriter,verbose,quiet);
2486 if (err == EOF)
2487 {
2488 printf("** End of file\n");
2489 return 0;
2490 }
2491 else if (err)
2492 return 1;
2493
2494 // ------------------------------------------------------------
2495 if (verbose || extra_info) printf("\n\n");
2496 if (err == EOF)
2497 printf("** End of file\n");
2498 else
2499 printf(">> End of sequences\n");
2500 return 0;
2501 }
2502
2503 /*
2504 * Read PES packets and write them out to the target. Alternate normal
2505 * speed, fast forward and reverse (in some sequence).
2506 *
2507 * Returns 0 if all went well, 1 if an error occurred.
2508 */
test_play_pes_packets(PES_reader_p reader,TS_writer_p tswriter,tsserve_context_p context,int pad_start,int video_only,int verbose,int quiet,int tsdirect,int num_normal,int num_fast,int num_faster,int num_reverse,int ffrequency,int rfrequency,int skiptest,int with_seq_hdrs)2509 static int test_play_pes_packets(PES_reader_p reader,
2510 TS_writer_p tswriter,
2511 tsserve_context_p context,
2512 int pad_start,
2513 int video_only,
2514 int verbose,
2515 int quiet,
2516 int tsdirect,
2517 int num_normal,
2518 int num_fast,
2519 int num_faster,
2520 int num_reverse,
2521 int ffrequency,
2522 int rfrequency,
2523 int skiptest,
2524 int with_seq_hdrs)
2525 {
2526 int err;
2527 int ii;
2528 ES_p es; // A view of our PES packets as ES units
2529 reverse_data_p reverse_data = NULL;
2530 stream_context stream;
2531 filter_context fcontext;
2532 filter_context scontext;
2533
2534
2535 // Start off our output with some null packets - this is in case the
2536 // reader needs some time to work out its byte alignment before it starts
2537 // looking for 0x47 bytes
2538 for (ii=0; ii<pad_start; ii++)
2539 {
2540 err = write_TS_null_packet(tswriter);
2541 if (err) return 1;
2542 }
2543
2544 #if 0
2545 // Ensure we output program data before anything else "sensible"
2546 stream.program_number = 1;
2547 err = write_program_data(reader,tswriter);
2548 if (err) return err;
2549 #endif
2550
2551 // Request that PES packets be written out to the TS writer as
2552 // a "side effect" of reading them in
2553 set_server_output(reader,tswriter,!context->tsdirect,
2554 context->repeat_program_every);
2555 set_server_padding(reader,context->pes_padding);
2556
2557 // Wrap our PES stream up as an ES stream
2558 err = build_elementary_stream_PES(reader,&es);
2559 if (err)
2560 {
2561 fprintf(stderr,"### Error trying to build ES reader from PES reader\n");
2562 return 1;
2563 }
2564
2565 // Build our reverse memory datastructure
2566 err = build_reverse_data(&reverse_data,reader->is_h264);
2567 if (err)
2568 {
2569 fprintf(stderr,"### Unable to build reverse memory\n");
2570 close_elementary_stream(&es);
2571 return 1;
2572 }
2573
2574 stream.is_h262 = fcontext.is_h262 = scontext.is_h262 = !(reader->is_h264);
2575
2576 if (reader->is_h264)
2577 {
2578 access_unit_context_p acontext; // Our ES data as access units
2579 h264_filter_context_p fcontext4 = NULL; // And a filter over that
2580 h264_filter_context_p scontext4 = NULL; // And another
2581
2582 err = build_access_unit_context(es,&acontext);
2583 if (err)
2584 {
2585 fprintf(stderr,
2586 "### Error trying to build access unit reader from ES reader\n");
2587 close_elementary_stream(&es);
2588 free_reverse_data(&reverse_data);
2589 return 1;
2590 }
2591 add_access_unit_reverse_context(acontext,reverse_data);
2592
2593 err = build_h264_filter_context(&fcontext4,acontext,ffrequency);
2594 if (err)
2595 {
2596 fprintf(stderr,"### Unable to build filter context\n");
2597 close_elementary_stream(&es);
2598 free_reverse_data(&reverse_data);
2599 free_access_unit_context(&acontext);
2600 return 1;
2601 }
2602
2603 err = build_h264_filter_context_strip(&scontext4,acontext,TRUE);
2604 if (err)
2605 {
2606 fprintf(stderr,"### Unable to build strip context\n");
2607 close_elementary_stream(&es);
2608 free_reverse_data(&reverse_data);
2609 free_access_unit_context(&acontext);
2610 free_h264_filter_context(&fcontext4);
2611 return 1;
2612 }
2613
2614 stream.u.h264 = acontext;
2615 fcontext.u.h264 = fcontext4;
2616 scontext.u.h264 = scontext4;
2617
2618 if (skiptest)
2619 err = test_skip(reader,stream,fcontext,scontext,reverse_data,tswriter,
2620 video_only,verbose,quiet,tsdirect,FALSE);
2621 else
2622 err = test_play(reader,stream,fcontext,scontext,reverse_data,tswriter,
2623 video_only,verbose,quiet,tsdirect,
2624 num_normal,num_fast,num_faster,num_reverse,
2625 ffrequency,rfrequency,FALSE);
2626
2627 free_access_unit_context(&acontext);
2628 free_h264_filter_context(&fcontext4);
2629 free_h264_filter_context(&scontext4);
2630 }
2631 else
2632 {
2633 h262_context_p h262; // Our ES data as H.262 items
2634 h262_filter_context_p fcontext2 = NULL; // And a filter over that
2635 h262_filter_context_p scontext2 = NULL; // And another
2636
2637 if (!with_seq_hdrs)
2638 reverse_data->output_sequence_headers = FALSE;
2639
2640 err = build_h262_context(es,&h262);
2641 if (err)
2642 {
2643 fprintf(stderr,
2644 "### Error trying to build H.262 reader from ES reader\n");
2645 close_elementary_stream(&es);
2646 free_reverse_data(&reverse_data);
2647 return 1;
2648 }
2649 add_h262_reverse_context(h262,reverse_data);
2650
2651 err = build_h262_filter_context(&fcontext2,h262,ffrequency);
2652 if (err)
2653 {
2654 fprintf(stderr,"### Unable to build filter context\n");
2655 close_elementary_stream(&es);
2656 free_reverse_data(&reverse_data);
2657 free_h262_context(&h262);
2658 return 1;
2659 }
2660
2661 err = build_h262_filter_context_strip(&scontext2,h262,TRUE);
2662 if (err)
2663 {
2664 fprintf(stderr,"### Unable to build strip context\n");
2665 close_elementary_stream(&es);
2666 free_reverse_data(&reverse_data);
2667 free_h262_context(&h262);
2668 free_h262_filter_context(&fcontext2);
2669 return 1;
2670 }
2671
2672 stream.u.h262 = h262;
2673 fcontext.u.h262 = fcontext2;
2674 scontext.u.h262 = scontext2;
2675
2676 if (skiptest)
2677 err = test_skip(reader,stream,fcontext,scontext,reverse_data,tswriter,
2678 video_only,verbose,quiet,tsdirect,with_seq_hdrs);
2679 else
2680 err = test_play(reader,stream,fcontext,scontext,reverse_data,tswriter,
2681 video_only,verbose,quiet,tsdirect,
2682 num_normal,num_fast,num_faster,num_reverse,
2683 ffrequency,rfrequency,with_seq_hdrs);
2684
2685 free_h262_context(&h262);
2686 free_h262_filter_context(&fcontext2);
2687 free_h262_filter_context(&scontext2);
2688 }
2689
2690 close_elementary_stream(&es);
2691 free_reverse_data(&reverse_data);
2692
2693 return err;
2694 }
2695
open_input_file(tsserve_context_p context,int quiet,int verbose,PES_reader_p * reader)2696 static int open_input_file(tsserve_context_p context,
2697 int quiet,
2698 int verbose,
2699 PES_reader_p *reader)
2700 {
2701 int err = open_PES_reader(context->input_names[context->default_file_index],
2702 !quiet,verbose,reader);
2703 if (err)
2704 {
2705 fprintf(stderr,"### Error opening file %s\n",
2706 context->input_names[context->default_file_index]);
2707 return 1;
2708 }
2709
2710 if (!quiet)
2711 printf("Opened input file %s (as %s)\n",
2712 context->input_names[context->default_file_index],
2713 ((*reader)->is_TS?"TS":"PS"));
2714
2715 // If it's PS data, check if we're overriding its stream type
2716 if (!(*reader)->is_TS && context->force_stream_type &&
2717 (*reader)->is_h264 == context->want_h262)
2718 {
2719 if (!quiet)
2720 printf("File appeared to contain %s, forcing %s\n",
2721 (*reader)->is_h264?"MPEG-4/AVC (H.264)":"MPEG-2 (H.272)",
2722 context->want_h262?"MPEG-2":"MPEG-4/AVC");
2723 set_PES_reader_h264(*reader);
2724 }
2725
2726 // If it's PS data, pretend to have read in a PAT and PMT
2727 if (!(*reader)->is_TS)
2728 {
2729 set_PES_reader_program_data(*reader,1,
2730 context->pmt_pid, context->video_pid,
2731 context->audio_pid,context->pcr_pid);
2732 set_PES_reader_dolby_stream_type(*reader,context->dolby_is_dvb);
2733 }
2734
2735 // If we're wanting extra information, also ask to be told about
2736 // the reading and writing of underlying PES packets.
2737 (*reader)->debug_read_packets = extra_info;
2738 return 0;
2739 }
2740
open_input_files(tsserve_context_p context,int quiet,int verbose,PES_reader_p reader[MAX_INPUT_FILES])2741 static int open_input_files(tsserve_context_p context,
2742 int quiet,
2743 int verbose,
2744 PES_reader_p reader[MAX_INPUT_FILES])
2745 {
2746 int ii;
2747 for (ii = 0; ii < MAX_INPUT_FILES; ii++)
2748 {
2749 int err;
2750 if (context->input_names[ii] == NULL)
2751 {
2752 reader[ii] = NULL;
2753 continue;
2754 }
2755
2756 if (!quiet)
2757 printf("\nLooking at input file %d, %s\n",ii,context->input_names[ii]);
2758
2759 err = open_PES_reader(context->input_names[ii],!quiet,verbose,&reader[ii]);
2760 if (err)
2761 {
2762 fprintf(stderr,"!!! Error opening file %d (%s)\n",
2763 ii,context->input_names[ii]);
2764 // return 1;
2765 reader[ii] = NULL;
2766 continue;
2767 }
2768
2769 if (!quiet)
2770 printf("Opened input file %2d, %s, as %s\n",ii,context->input_names[ii],
2771 (reader[ii]->is_TS?"TS":"PS"));
2772
2773 // If it's PS data, check if we're overriding its stream type
2774 // (for the moment, we only allow overriding of *all* files,
2775 // which is clumsy, but may be sufficient for our needs)
2776 if (!reader[ii]->is_TS && context->force_stream_type &&
2777 reader[ii]->is_h264 == context->want_h262)
2778 {
2779 if (!quiet)
2780 printf("File appeared to contain %s, forcing %s\n",
2781 reader[ii]->is_h264?"MPEG-4/AVC (H.264)":"MPEG-2 (H.272)",
2782 context->want_h262?"MPEG-2":"MPEG-4/AVC");
2783 set_PES_reader_h264(reader[ii]);
2784 }
2785
2786 // Ensure that different input files get written out as different
2787 // programs (with differing PIDs)
2788 set_PES_reader_program_data(reader[ii],
2789 ii+1, // program number: 1 upwards
2790 DEFAULT_VIDEO_PID+ii+20, // PMT
2791 DEFAULT_VIDEO_PID+ii, // video
2792 DEFAULT_VIDEO_PID+ii+10, // audio
2793 DEFAULT_VIDEO_PID+ii); // PCR==video
2794
2795 // If we're wanting extra information, also ask to be told about
2796 // the reading and writing of underlying PES packets.
2797 reader[ii]->debug_read_packets = extra_info;
2798 }
2799 return 0;
2800 }
2801
2802 // ============================================================
2803 // Serving multiple clients
2804 // ============================================================
2805 // Arguments for passing to the child server process
2806 struct server_args
2807 {
2808 tsserve_context_p context; // Various arguments we might need
2809 TS_writer_p tswriter; // Where we're writing to
2810 int verbose;
2811 int quiet;
2812 };
2813
tsserve_child_process(struct server_args * args)2814 static int tsserve_child_process(struct server_args *args)
2815 {
2816 int ii, err;
2817 int had_err;
2818 tsserve_context_p context = args->context;
2819 TS_writer_p tswriter = args->tswriter;
2820 int verbose = args->verbose;
2821 int quiet = args->quiet;
2822 PES_reader_p reader[MAX_INPUT_FILES];
2823
2824 if (!quiet) printf("Establishing connection with client on socket %d\n",
2825 tswriter->where.socket);
2826
2827 err = tswrite_start_input(tswriter,tswriter->where.socket);
2828 if (err)
2829 {
2830 fprintf(stderr,"### Unable to start command input from client\n");
2831 (void) tswrite_close(tswriter,TRUE);
2832 return 1;
2833 }
2834
2835 err = open_input_files(context,quiet,verbose,reader);
2836 if (err)
2837 {
2838 fprintf(stderr,"### Unable to open input file\n");
2839 (void) tswrite_close(tswriter,TRUE);
2840 return 1;
2841 }
2842
2843 // And play...
2844 if (!quiet) printf("Playing to client via socket %d\n",
2845 tswriter->where.socket);
2846
2847 err = play_pes_packets(reader,tswriter,context,verbose,quiet);
2848 if (err)
2849 {
2850 fprintf(stderr,"!!! Error playing PES packets to client\n");
2851 (void) tswrite_close(tswriter,TRUE);
2852 for (ii=0;ii<MAX_INPUT_FILES;ii++)
2853 (void) close_PES_reader(&reader[ii]);
2854 return 0; // Treat as normal completion, so we continue
2855 }
2856
2857 if (!quiet) printf("Finished talking to client\n");
2858 err = tswrite_close(tswriter,quiet);
2859 if (err)
2860 {
2861 for (ii=0;ii<MAX_INPUT_FILES;ii++)
2862 (void) close_PES_reader(&reader[ii]);
2863 return 1;
2864 }
2865
2866 had_err = FALSE;
2867 for (ii=0;ii<MAX_INPUT_FILES;ii++)
2868 {
2869 err = close_PES_reader(&reader[ii]);
2870 if (err)
2871 {
2872 fprintf(stderr,"### Error closing input file %d, %s\n",ii,
2873 context->input_names[ii]);
2874 had_err = TRUE;
2875 }
2876 }
2877
2878 #ifdef _WIN32
2879 // The original ("parent") thread does not know when we have finished,
2880 // so it cannot free the resources we are using. Of course, *we* know
2881 // we've now finished, so we can...
2882 free(args);
2883 #endif
2884
2885 return (had_err?1:0);
2886 }
2887
2888 #ifdef _WIN32
2889 // ============================================================
2890 // Windows threading ("fork" alternative)
2891 // ============================================================
2892 /*
2893 * Wrapper for tsserve_child_process, used to coerce args, etc.
2894 */
child_thread_fn(void_p varg)2895 static void child_thread_fn(void_p varg)
2896 {
2897 struct server_args *args = (struct server_args *)varg;
2898 (void) tsserve_child_process(args);
2899 }
2900
2901 /*
2902 * Start up the child thread, to serve a single client
2903 */
start_child(tsserve_context_p context,TS_writer_p tswriter,int verbose,int quiet)2904 static int start_child(tsserve_context_p context,
2905 TS_writer_p tswriter,
2906 int verbose,
2907 int quiet)
2908 {
2909 HANDLE child_thread;
2910 struct server_args *args;
2911
2912 args = malloc(sizeof(struct server_args));
2913 if (args == NULL)
2914 {
2915 fprintf(stderr,"### Unable to allocate memory for child datastructure\n");
2916 return 1;
2917 }
2918
2919 args->context = context;
2920 args->tswriter = tswriter;
2921 args->verbose = verbose;
2922 args->quiet = quiet;
2923
2924 child_thread = (HANDLE) _beginthread(child_thread_fn,0,(void_p)args);
2925 if (child_thread == (HANDLE) -1)
2926 {
2927 fprintf(stderr,"Error creating child process: %s\n",strerror(errno));
2928 return 1;
2929 }
2930 return 0;
2931 }
2932 #else // _WIN32
2933 // ============================================================
2934 // Unix forking ("thread" alternative)
2935 // ============================================================
2936 /*
2937 * Start up the child fork, to handle the circular buffering
2938 */
start_child(tsserve_context_p context,TS_writer_p tswriter,int verbose,int quiet)2939 static int start_child(tsserve_context_p context,
2940 TS_writer_p tswriter,
2941 int verbose,
2942 int quiet)
2943 {
2944 pid_t pid;
2945 struct server_args args = {context,tswriter,verbose,quiet};
2946
2947 pid = fork();
2948 if (pid == -1)
2949 {
2950 fprintf(stderr,"Error forking: %s\n",strerror(errno));
2951 return 1;
2952 }
2953 else if (pid == 0)
2954 {
2955 // Aha - we're the child
2956 _exit(tsserve_child_process(&args));
2957 }
2958 tswriter->child = pid;
2959 return 0;
2960 }
2961
2962 static void set_child_exit_handler();
2963 /*
2964 * Signal handler - catch children and stop them becoming zombies
2965 */
on_child_exit()2966 static void on_child_exit()
2967 {
2968 #if 0
2969 printf("sighandler: starting\n");
2970 #endif
2971 for (;;)
2972 {
2973 int status;
2974 int pid = waitpid(-1, &status, WNOHANG);
2975 #if 0
2976 if (pid > 0)
2977 printf("sighandler: finished with child %08x\n",pid);
2978 else
2979 printf("sighandler: finished with %d\n",pid);
2980 #endif
2981 if (pid <= 0)
2982 break;
2983 }
2984 }
2985
2986 /*
2987 * Setup the "on child exit" signal handler
2988 */
set_child_exit_handler()2989 static void set_child_exit_handler()
2990 {
2991 int ret;
2992 struct sigaction action;
2993 action.sa_handler = on_child_exit;
2994 action.sa_flags = SA_NOCLDSTOP; // we only want terminated children, not stopped children
2995 #ifdef SA_RESTART
2996 action.sa_flags |= SA_RESTART;
2997 #endif
2998 sigemptyset(&action.sa_mask);
2999 // If it goes wrong, there's not much we can do apart from grumble...
3000 #if 0
3001 printf("sighandler: Setting up signal handler to reap child processes\n");
3002 #endif
3003 ret = sigaction(SIGCHLD,&action,0);
3004 if (ret < 0) fprintf(stderr,"!!! tsserve: Error starting signal handler to reap child processes\n");
3005 }
3006 #endif // _WIN32
3007
3008 /*
3009 * Run as a server
3010 */
run_server(tsserve_context_p context,int listen_port,int verbose,int quiet)3011 static int run_server(tsserve_context_p context,
3012 int listen_port,
3013 int verbose,
3014 int quiet)
3015 {
3016 int err;
3017 SOCKET server_socket;
3018 struct sockaddr_in ipaddr;
3019
3020 #ifdef _WIN32
3021 err = winsock_startup();
3022 if (err) return 1;
3023 #else
3024 set_child_exit_handler();
3025 #endif
3026
3027 // Create a socket.
3028 server_socket = socket(AF_INET, SOCK_STREAM, 0);
3029 if (server_socket == -1)
3030 {
3031 #ifdef _WIN32
3032 err = WSAGetLastError();
3033 fprintf(stderr,"### Unable to create socket: ");
3034 print_winsock_err(err);
3035 fprintf(stderr,"\n");
3036 #else // _WIN32
3037 fprintf(stderr,"### Unable to create socket: %s\n",strerror(errno));
3038 #endif // _WIN32
3039 return 1;
3040 }
3041
3042 // Bind it to port `listen_port` on this machine
3043 memset(&ipaddr,0,sizeof(ipaddr));
3044 #if !defined(__linux__) && !defined(_WIN32)
3045 // On BSD, the length is defined in the datastructure
3046 ipaddr.sin_len = sizeof(struct sockaddr_in);
3047 #endif
3048 ipaddr.sin_family = AF_INET;
3049 ipaddr.sin_port = htons(listen_port);
3050 ipaddr.sin_addr.s_addr = INADDR_ANY; // any interface
3051
3052 err = bind(server_socket,(struct sockaddr*)&ipaddr,sizeof(ipaddr));
3053 if (err == -1)
3054 {
3055 #ifdef _WIN32
3056 err = WSAGetLastError();
3057 fprintf(stderr,"### Unable to bind to port %d: ",listen_port);
3058 print_winsock_err(err);
3059 fprintf(stderr,"\n");
3060 #else // _WIN32
3061 fprintf(stderr,"### Unable to bind to port %d: %s\n",
3062 listen_port,strerror(errno));
3063 #endif // _WIN32
3064 return 1;
3065 }
3066
3067 for (;;)
3068 {
3069 TS_writer_p tswriter = NULL;
3070
3071 if (!quiet) printf("\nListening for a connection on port %d"
3072 " with socket %d\n",listen_port,server_socket);
3073
3074 #ifdef _WIN32
3075 // tswrite_close calls winsock_cleanup(), so we need to make sure that
3076 // we call an *extra* winsock_startup to match that (and leave the
3077 // call made before this loop "in scope")
3078 err = winsock_startup();
3079 if (err)
3080 {
3081 fprintf(stderr,"### Error calling winsock_startup before listening\n");
3082 return 1;
3083 }
3084 #endif // _WIN32
3085
3086 err = tswrite_wait_for_client(server_socket,quiet,&tswriter);
3087 if (err)
3088 {
3089 fprintf(stderr,"### Error listening for client on port %d\n",
3090 listen_port);
3091 return 1;
3092 }
3093
3094 if (context->drop_packets)
3095 {
3096 tswriter->drop_packets = context->drop_packets;
3097 tswriter->drop_number = context->drop_number;
3098 }
3099
3100 err = start_child(context,tswriter,verbose,quiet);
3101 if (err)
3102 {
3103 fprintf(stderr,"### Error spawning child server\n");
3104 return 1;
3105 }
3106 #if 0 // The following was a temporary fix to stop zombies without a signal handler
3107 #ifndef _WIN32
3108 // If we've forked, then we need to free our "copy" of the tswriter
3109 err = tswrite_close(tswriter,TRUE);
3110 if (err)
3111 {
3112 fprintf(stderr,"### Error closing socket in parent process\n");
3113 return 1;
3114 }
3115 #endif
3116 #endif
3117 }
3118 return 0;
3119 }
3120
3121 /*
3122 * Run tests
3123 */
test_reader(tsserve_context_p context,int output_to_file,char * output_name,int port,int num_normal,int num_fast,int num_faster,int num_reverse,int skiptest,int verbose,int quiet,int tsdirect)3124 static int test_reader(tsserve_context_p context,
3125 int output_to_file,
3126 char *output_name,
3127 int port,
3128 int num_normal,
3129 int num_fast,
3130 int num_faster,
3131 int num_reverse,
3132 int skiptest,
3133 int verbose,
3134 int quiet,
3135 int tsdirect)
3136 {
3137 int err;
3138 TS_writer_p tswriter = NULL;
3139 PES_reader_p reader = NULL;
3140
3141 err = tswrite_open((output_to_file?TS_W_FILE:TS_W_TCP),
3142 output_name,NULL,port,quiet,&tswriter);
3143 if (err)
3144 {
3145 fprintf(stderr,"### Unable to connect to %s\n",output_name);
3146 return 1;
3147 }
3148
3149 if (context->drop_packets)
3150 {
3151 tswriter->drop_packets = context->drop_packets;
3152 tswriter->drop_number = context->drop_number;
3153 }
3154
3155 err = open_input_file(context,quiet,verbose,&reader);
3156 if (err)
3157 {
3158 fprintf(stderr,"### Unable to open input file\n");
3159 (void) tswrite_close(tswriter,TRUE);
3160 return 1;
3161 }
3162
3163 // And play...
3164 err = test_play_pes_packets(reader,tswriter,context,
3165 context->pad_start,context->video_only,
3166 verbose,quiet,tsdirect,
3167 num_normal,num_fast,num_faster,num_reverse,
3168 context->ffrequency,context->rfrequency,
3169 skiptest,context->with_seq_hdrs);
3170 if (err)
3171 {
3172 fprintf(stderr,"### Error playing PES packets\n");
3173 (void) tswrite_close(tswriter,TRUE);
3174 (void) close_PES_reader(&reader);
3175 return 1;
3176 }
3177
3178 err = tswrite_close(tswriter,quiet);
3179 if (err)
3180 {
3181 fprintf(stderr,"### Error closing output %s: %s\n",output_name,
3182 strerror(errno));
3183 (void) close_PES_reader(&reader);
3184 return 1;
3185 }
3186 err = close_PES_reader(&reader);
3187 if (err)
3188 {
3189 fprintf(stderr,"### Error closing input file %s\n",
3190 context->input_names[context->default_file_index]);
3191 return 1;
3192 }
3193 return 0;
3194 }
3195
3196 /*
3197 * Run as a player, possibly reading commands via a socket
3198 */
command_reader(tsserve_context_p context,char * output_name,int port,int use_stdin,int verbose,int quiet)3199 static int command_reader(tsserve_context_p context,
3200 char *output_name,
3201 int port,
3202 int use_stdin,
3203 int verbose,
3204 int quiet)
3205 {
3206 int err;
3207 int ii, had_err;
3208 TS_writer_p tswriter = NULL;
3209 PES_reader_p reader[MAX_INPUT_FILES];
3210
3211 err = tswrite_open(TS_W_TCP,output_name,NULL,port,quiet,&tswriter);
3212 if (err)
3213 {
3214 fprintf(stderr,"### Unable to connect to %s\n",output_name);
3215 return 1;
3216 }
3217
3218 if (context->drop_packets)
3219 {
3220 tswriter->drop_packets = context->drop_packets;
3221 tswriter->drop_number = context->drop_number;
3222 }
3223
3224 #ifndef _WIN32
3225 // Maybe enable command input from stdin
3226 if (use_stdin)
3227 {
3228 if (!quiet)
3229 printf("Commands from standard input:\n"
3230 " q = quit\n"
3231 " n = normal speed\n"
3232 " p = pause (the initial state)\n"
3233 " f = fast forward\n"
3234 " F = fast fast forward\n"
3235 " r = reverse\n"
3236 " R = fast reverse\n"
3237 " > < = skip forwards, back by 10 seconds\n"
3238 " ] [ = skip forwards, back by 3 minutes\n"
3239 " 0..9 = select file 0 through 9 (if defined),\n"
3240 " rewind it and play at normal speed\n"
3241 "Use newline to 'send' a command or sequence of commands.\n");
3242 err= tswrite_start_input(tswriter,STDIN_FILENO);
3243 if (err)
3244 {
3245 fprintf(stderr,"### Unable to start command input from stdin\n");
3246 (void) tswrite_close(tswriter,TRUE);
3247 return 1;
3248 }
3249 }
3250 else
3251 #endif // _WIN32
3252 {
3253 err= tswrite_start_input(tswriter,tswriter->where.socket);
3254 if (err)
3255 {
3256 fprintf(stderr,"### Unable to start command input from %s\n",
3257 output_name);
3258 (void) tswrite_close(tswriter,TRUE);
3259 return 1;
3260 }
3261 }
3262
3263 err = open_input_files(context,quiet,verbose,reader);
3264 if (err)
3265 {
3266 fprintf(stderr,"### Unable to open input file\n");
3267 (void) tswrite_close(tswriter,TRUE);
3268 return 1;
3269 }
3270
3271 // And play...
3272 err = play_pes_packets(reader,tswriter,context,verbose,quiet);
3273 if (err)
3274 {
3275 fprintf(stderr,"### Error playing PES packets\n");
3276 (void) tswrite_close(tswriter,TRUE);
3277 for (ii=0;ii<MAX_INPUT_FILES;ii++)
3278 (void) close_PES_reader(&reader[ii]);
3279 return 1;
3280 }
3281
3282 err = tswrite_close(tswriter,quiet);
3283 if (err)
3284 {
3285 fprintf(stderr,"### Error closing output %s: %s\n",output_name,
3286 strerror(errno));
3287 for (ii=0;ii<MAX_INPUT_FILES;ii++)
3288 (void) close_PES_reader(&reader[ii]);
3289 return 1;
3290 }
3291 had_err = FALSE;
3292 for (ii=0;ii<MAX_INPUT_FILES;ii++)
3293 {
3294 err = close_PES_reader(&reader[ii]);
3295 if (err)
3296 {
3297 fprintf(stderr,"### Error closing input file %d, %s\n",ii,
3298 context->input_names[ii]);
3299 had_err = TRUE;
3300 }
3301 }
3302 return (had_err?1:0);
3303 }
3304
print_usage()3305 static void print_usage()
3306 {
3307 printf(
3308 "Usage:\n"
3309 " tsserve <infile>\n"
3310 " tsserve <infile> -port <n>\n"
3311 " tsserve [switches] <infile> [switches]\n"
3312 "\n"
3313 );
3314 REPORT_VERSION("tsserve");
3315 printf(
3316 "\n"
3317 " Act as a server which plays the given file (containing Transport\n"
3318 " Stream or Program Stream data). The output is always Transport\n"
3319 " Stream.\n"
3320 "\n"
3321 "Input:\n"
3322 " <infile> An H.222.0 TS or PS file to serve to the client.\n"
3323 " This will be treated as file 0 (see below).\n"
3324 "\n"
3325 " -0 <file0> .. -9 <file9>\n"
3326 " Specify files 0 through 9, selectable with command\n"
3327 " characters 0 through 9. The lowest numbered file\n"
3328 " will be the default for display.\n"
3329 "\n"
3330 "General Switches:\n"
3331 " -details Print out more detailed help information,\n"
3332 " including some less common options.\n"
3333 " -quiet, -q Suppress informational and warning messages.\n"
3334 " -verbose, -v Output additional diagnostic messages\n"
3335 " -port <n> Listen for a client on port <n> (default 88)\n"
3336 " -noaudio Ignore any audio data\n"
3337 " -pad <n> Pad the start of the output with <n> filler TS\n"
3338 " packets, to allow the client to synchronize with\n"
3339 " the datastream. Defaults to 8.\n"
3340 "\n"
3341 " -noseqhdr Do not output sequence headers for fast forward/reverse\n"
3342 " data. Only relevant to H.262 data.\n"
3343 "\n"
3344 "Program Stream Switches:\n"
3345 "\n"
3346 " -prepeat <n> Output the program data (PAT/PMT) after every <n>\n"
3347 " PS packs. Defaults to 100.\n"
3348 "\n"
3349 " -h264, -avc Force the program to treat the input as MPEG-4/AVC.\n"
3350 " -h262 Force the program to treat the input as MPEG-2.\n"
3351 " Both of these affect the stream type of the output data.\n"
3352 "\n"
3353 " If the audio stream being output is Dolby (AC-3), then the stream type\n"
3354 " used to output it differs for DVB (European) and ATSC (USA) data. It\n"
3355 " may be specified as follows:\n"
3356 "\n"
3357 " -dolby dvb Use stream type 0x06 (the default)\n"
3358 " -dolby atsc Use stream type 0x81\n"
3359 "\n"
3360 " For information on using the program in other modes, see -details.\n"
3361 );
3362 }
3363
print_detailed_usage()3364 static void print_detailed_usage()
3365 {
3366 printf(
3367 "Usage: tsserve [switches] <infile>\n"
3368 "\n"
3369 " Copyright (c) 2004 SJ Consulting Ltd.\n"
3370 "\n"
3371 " Reads from a file containing H.222.0 (ISO/IEC 13818-1) Transport\n"
3372 " Stream or Program Stream data (converting PS to TS as it goes),\n"
3373 " and 'plays' the Transport Stream 'at' a client.\n"
3374 "\n"
3375 " Assumes a single program in the file, and for PS assumes that the\n"
3376 " program stream is well formed - i.e., that it starts with a pack\n"
3377 " header. A PS stream that ends after a PES packet, but without an\n"
3378 " MPEG_program_end_code will cause a warning message, but will not\n"
3379 " be treated as an error.\n"
3380 "\n"
3381 " In the default mode, the program acts as a server, listening for\n"
3382 " clients on port 88 (or the port specified with -port). When a\n"
3383 " client connects to the port, the program starts listening for\n"
3384 " commands from the client, and acting appropriately. When the\n"
3385 " client sends the 'q'uit command, the program disconnects from\n"
3386 " the client, and listens for another.\n"
3387 "\n"
3388 " Alternative modes may be specified with -cmd, -cmdstdin and\n"
3389 " -test.\n"
3390 "\n"
3391 "Input:\n"
3392 " <infile> An H.222.0 TS or PS file.\n"
3393 " If given before any of -0..-9, this will be treated\n"
3394 " as a specification of file 0. If given after -0..-9,\n"
3395 " it will be treated as an error.\n"
3396 "\n"
3397 " -0 <file0> .. -9 <file9>\n"
3398 " Specify files 0 through 9, selectable with command\n"
3399 " characters 0 through 9. The lowest numbered name\n"
3400 " will be selected as the default.\n"
3401 "\n"
3402 "General Switches:\n"
3403 " -details Present this text.\n"
3404 " -quiet, -q Only output error messages.\n"
3405 " -verbose, -v Output progress messages.\n"
3406 "\n"
3407 " Normal operation outputs some messages summarising the command line\n"
3408 " choices, information about data from the input file, confirmation\n"
3409 " when the program is ending, etc.\n"
3410 " Quiet operation endeavours only to output error messages.\n"
3411 " Verbose operation outputs diagnostic information, not intended for\n"
3412 " normal use.\n"
3413 "\n"
3414 " -x Output *extra* information."
3415 "\n"
3416 " The extra information output gives more details about what the\n"
3417 " server is doing in reaction to the commands given by the client.\n"
3418 " It is intended as a diagnostic aid during development.\n"
3419 "\n"
3420 " -port <n> Listen for a client on port <n> (default 88)\n"
3421 " Ignored if -cmd, -cmdstdin or -test is\n"
3422 " specified\n"
3423 "\n"
3424 " -noaudio Don't output audio data\n"
3425 "\n"
3426 " -pad <n> Pad the start of the output with <n> filler TS\n"
3427 " packets, to allow the client to synchronize with\n"
3428 " the datastream. Defaults to 8.\n"
3429 "\n"
3430 " -noseqhdr Do not output sequence headers for fast forward/reverse\n"
3431 " data. Only relevant to H.262 data.\n"
3432 "\n"
3433 "Program Stream Switches:\n"
3434 "\n"
3435 " The following switches are only applicable if the input data is PS.\n"
3436 "\n"
3437 " -h264, -avc Force the program to treat the input as MPEG-4/AVC.\n"
3438 " -h262 Force the program to treat the input as MPEG-2.\n"
3439 "\n"
3440 " If input is from a file, then the program will look at the start of\n"
3441 " the file to determine if the stream is H.264 or H.262 data. This\n"
3442 " process may occasionally come to the wrong conclusion, in which case\n"
3443 " the user can override the choice using the switches above.\n"
3444 "\n"
3445 " If the audio stream being output is Dolby (AC-3), then the stream type\n"
3446 " used to output it differs for DVB (European) and ATSC (USA) data. It\n"
3447 " may be specified as follows:\n"
3448 "\n"
3449 " -dolby dvb Use stream type 0x06 (the default)\n"
3450 " -dolby atsc Use stream type 0x81\n"
3451 "\n"
3452 "Transport Stream Switches:\n"
3453 "\n"
3454 " The following switches are only applicable if the input data is TS.\n"
3455 "\n"
3456 " -tsdirect In normal play, copy all TS packets to the client,\n"
3457 " instead of just sending the PES packets for the video\n"
3458 " and audio streams'\n"
3459 "\n"
3460 " Note that when -tsdirect is specified, PES packets are still inspected\n"
3461 " to allow building up the fast forward/reverse indices.\n"
3462 " Also, -prepeat, -pes_padding and -drop will have no effect with this switch.\n"
3463 "\n"
3464 "Other stuff:\n"
3465 "\n"
3466 " -prepeat <n> Output the program data (PAT/PMT) after every <n>\n"
3467 " PES packets, to allow a TS reader to resynchronise\n"
3468 " if it starts reading part way through the stream.\n"
3469 " PAT/PMT pairs are also output before 'significant'\n"
3470 " events (changing speed/direction/etc.).\n"
3471 " Defaults to 100.\n"
3472 "\n"
3473 " -ffreq <n> Frequency for faster forward ('F'). Default is 8.\n"
3474 " -rfreq <n> Frequency for reverse (fast reverse is twice\n"
3475 " the speed). Default is 8.\n"
3476 "\n"
3477 " -pes_padding <n> When outputting in 'normal play' mode, input PES packets\n"
3478 " are copied to the output. If '-pes_padding' is used, then <n>\n"
3479 " dummy PES packets will be added to the output for each input\n"
3480 " packet, causing the amount of data output to be roughly <n>+1\n"
3481 " times as great. This can be useful for benchmarking the recipient.\n"
3482 "\n"
3483 " -drop <k> <d> As TS packets are output, for every <k>+<d> packets,\n"
3484 " keep <k> and then drop (throw away) <d>.\n"
3485 " Applies to all TS packets output, regardless of selected file.\n"
3486 " This can be useful when testing other applications.\n"
3487 "\n"
3488 "Alternate modes\n"
3489 "---------------\n"
3490 " Command input and testing modes connect directly to a host, and thus\n"
3491 " the host to use must be specified.\n"
3492 "\n"
3493 " -host <host>[:<port>\n"
3494 " The host to which to write TS packets, over\n"
3495 " TCP/IP. If <port> is not specified, it defaults\n"
3496 " to 88.\n"
3497 "\n"
3498 "Command input:\n"
3499 " -cmd Enables command input, from the host.\n"
3500 " -cmdstdin Enables command input, from standard input.\n"
3501 " This is not supported on Windows.\n"
3502 "\n"
3503 " In command input mode, the program connects to the host specified\n"
3504 " with -host, and takes commands either from the host, or from\n"
3505 " standard input.\n"
3506 "\n"
3507 " Command characters are:\n"
3508 " q quit.\n"
3509 " n normal play.\n"
3510 " p pause (the startup state).\n"
3511 " f fast forward (uses 'strip').\n"
3512 " F fast fast forward (uses 'filter').\n"
3513 " r reverse.\n"
3514 " R fast reverse.\n"
3515 " > < skip forwards/back by 10 seconds.\n"
3516 " ] [ skip forwards/back by 3 minutes.\n"
3517 " 0..9 select file 0 through 9 (as defined by switches -0 to\n"
3518 " -9, see above), rewind it and play at normal speed.\n"
3519 " Any other character is ignored.\n"
3520 " Note that if command input is from standard input, a newline must\n"
3521 " be typed before command characters are 'seen', and if there are\n"
3522 " multiple characters on a line, they will be obeyed in sequence.\n"
3523 "\n"
3524 "Testing:\n"
3525 " -test Test by running a sequence of pictures at the\n"
3526 " specified host. The exact sequence used can be\n"
3527 " determined with the -f, etc., switches:\n"
3528 "\n"
3529 " -f <nf> Loop outputting <nn> pictures at normal speed,\n"
3530 " -n <nn> then fast forward past <nf> pictures, then <nn> at\n"
3531 " -ff <nF> normal speed, then <nF> at the higher fast forward\n"
3532 " -r <nr> speed, then <nn> at normal speed again, then\n"
3533 " reverse past <nr>. Repeat until stopped.\n"
3534 "\n"
3535 " If '-f 0 -ff 0 -r 0' is specified, then the data will just play at\n"
3536 " normal speed, ignoring -n.\n"
3537 "\n"
3538 " -skiptest Test forwards and backwards skipping.\n"
3539 "\n"
3540 " -output <name>, -o <name>\n"
3541 " If -test is being used then output may be\n"
3542 " redirected to a file, instead of a host.\n"
3543 );
3544 }
3545
main(int argc,char ** argv)3546 int main(int argc, char **argv)
3547 {
3548 char *output_name = NULL;
3549 int had_input_name = FALSE;
3550 int had_output_name = FALSE;
3551 int output_port = 88; // Useful default port number
3552 int quiet = FALSE;
3553 int verbose = FALSE;
3554 int use_stdin = FALSE; // for command input...
3555 int listen_port = 88;
3556
3557 enum ACTION action = ACTION_SERVER;
3558
3559 int err = 0;
3560 int ii;
3561 int argno = 1;
3562
3563 // Testing specific options
3564 int num_normal = 100;
3565 int num_fast = 100;
3566 int num_faster = 100;
3567 int num_reverse = 100;
3568 int output_to_file = FALSE;
3569 int skiptest = FALSE;
3570
3571 struct tsserve_context context;
3572
3573 for (ii = 0; ii < MAX_INPUT_FILES; ii++)
3574 context.input_names[ii] = NULL;
3575
3576 context.video_only = FALSE;
3577 context.pad_start = 8;
3578 context.ffrequency = DEFAULT_FORWARD_FREQUENCY;
3579 context.rfrequency = DEFAULT_REVERSE_FREQUENCY;
3580 context.with_seq_hdrs = TRUE;
3581 context.pes_padding = 0;
3582 context.drop_packets = 0;
3583 context.drop_number = 0;
3584
3585 // Program Stream specific options
3586 context.pmt_pid = 0x66;
3587 context.audio_pid = 0x67;
3588 context.video_pid = 0x68;
3589 context.pcr_pid = context.video_pid; // Use PCRs from the video stream
3590 context.repeat_program_every = 100;
3591
3592 // Transport Stream specific options
3593 context.tsdirect = FALSE; // Write to server as a side effect of PES reading
3594
3595 context.force_stream_type = FALSE;
3596 context.want_h262 = TRUE; // shouldn't matter
3597 context.dolby_is_dvb = TRUE;
3598
3599 if (argc < 2)
3600 {
3601 print_usage();
3602 return 0;
3603 }
3604
3605 while (argno < argc)
3606 {
3607 if (argv[argno][0] == '-')
3608 {
3609 if (!strcmp("--help",argv[argno]) || !strcmp("-h",argv[argno]) ||
3610 !strcmp("-help",argv[argno]))
3611 {
3612 print_usage();
3613 return 0;
3614 }
3615 else if (!strcmp("-details",argv[argno]))
3616 {
3617 print_detailed_usage();
3618 return 0;
3619 }
3620 else if (!strcmp("-noseqhdr",argv[argno]) ||
3621 !strcmp("-noseqhdrs",argv[argno]))
3622 {
3623 context.with_seq_hdrs = FALSE;
3624 }
3625 else if (!strcmp("-skiptest",argv[argno]))
3626 {
3627 action = ACTION_TEST;
3628 skiptest = TRUE;
3629 }
3630 else if (!strcmp("-test",argv[argno]))
3631 {
3632 action = ACTION_TEST;
3633 skiptest = FALSE;
3634 }
3635 else if (!strcmp("-tsdirect",argv[argno]))
3636 {
3637 context.tsdirect = TRUE; // Write to server as a side effect of TS reading
3638 }
3639 else if (!strcmp("-n",argv[argno]))
3640 {
3641 CHECKARG("tsserve",argno);
3642 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,10,
3643 &num_normal);
3644 if (err) return 1;
3645 argno++;
3646 }
3647 else if (!strcmp("-f",argv[argno]))
3648 {
3649 CHECKARG("tsserve",argno);
3650 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,10,&num_fast);
3651 if (err) return 1;
3652 argno++;
3653 }
3654 else if (!strcmp("-ff",argv[argno]))
3655 {
3656 CHECKARG("tsserve",argno);
3657 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,10,
3658 &num_faster);
3659 if (err) return 1;
3660 argno++;
3661 }
3662 else if (!strcmp("-r",argv[argno]))
3663 {
3664 CHECKARG("tsserve",argno);
3665 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,10,
3666 &num_reverse);
3667 if (err) return 1;
3668 argno++;
3669 }
3670 else if (!strcmp("-ffreq",argv[argno]))
3671 {
3672 CHECKARG("tsserve",argno);
3673 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,0,
3674 &context.ffrequency);
3675 if (err) return 1;
3676 argno++;
3677 }
3678 else if (!strcmp("-rfreq",argv[argno]))
3679 {
3680 CHECKARG("tsserve",argno);
3681 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,0,
3682 &context.rfrequency);
3683 if (err) return 1;
3684 argno++;
3685 }
3686 else if (!strcmp("-pes_padding",argv[argno]))
3687 {
3688 CHECKARG("tsserve",argno);
3689 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,10,
3690 &context.pes_padding);
3691 if (err) return 1;
3692 argno++;
3693 }
3694 else if (!strcmp("-drop",argv[argno]))
3695 {
3696 if (ii+2 >= argc)
3697 {
3698 fprintf(stderr,"### tsserve: missing argument(s) to -drop\n");
3699 return 1;
3700 }
3701 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,0,
3702 &context.drop_packets);
3703 if (err) return 1;
3704 err = int_value("tsserve",argv[argno],argv[argno+2],TRUE,0,
3705 &context.drop_number);
3706 if (err) return 1;
3707 argno += 2;
3708 }
3709 else if (!strcmp("-quiet",argv[argno]) || !strcmp("-q",argv[argno]))
3710 {
3711 quiet = TRUE;
3712 verbose = FALSE;
3713 }
3714 else if (!strcmp("-verbose",argv[argno]) || !strcmp("-v",argv[argno]))
3715 {
3716 quiet = FALSE;
3717 verbose = TRUE;
3718 }
3719 else if (!strcmp("-x",argv[argno]))
3720 {
3721 extra_info = TRUE;
3722 }
3723 else if (!strcmp("-noaudio",argv[argno]))
3724 {
3725 context.video_only = TRUE;
3726 }
3727 else if (!strcmp("-avc",argv[argno]) || !strcmp("-h264",argv[argno]))
3728 {
3729 context.force_stream_type = TRUE;
3730 context.want_h262 = FALSE;
3731 }
3732 else if (!strcmp("-h262",argv[argno]))
3733 {
3734 context.force_stream_type = TRUE;
3735 context.want_h262 = TRUE;
3736 }
3737 else if (!strcmp("-dolby",argv[argno]))
3738 {
3739 CHECKARG("tsserve",argno);
3740 if (!strcmp("dvb",argv[argno+1]))
3741 context.dolby_is_dvb = TRUE;
3742 else if (!strcmp("atsc",argv[argno+1]))
3743 context.dolby_is_dvb = FALSE;
3744 else
3745 {
3746 fprintf(stderr,"### tsserve: -dolby must be followed by dvb or atsc\n");
3747 return 1;
3748 }
3749 ii++;
3750 }
3751 else if (!strcmp("-prepeat",argv[argno]))
3752 {
3753 CHECKARG("tsserve",argno);
3754 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,10,
3755 &context.repeat_program_every);
3756 if (err) return 1;
3757 argno++;
3758 }
3759 else if (!strcmp("-pad",argv[argno]))
3760 {
3761 CHECKARG("tsserve",argno);
3762 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,10,
3763 &context.pad_start);
3764 if (err) return 1;
3765 argno++;
3766 }
3767 else if (!strcmp("-port",argv[argno]))
3768 {
3769 CHECKARG("tsserve",argno);
3770 err = int_value("tsserve",argv[argno],argv[argno+1],TRUE,0,
3771 &listen_port);
3772 if (err) return 1;
3773 argno++;
3774 }
3775 else if (!strcmp("-cmd",argv[argno]))
3776 {
3777 action = ACTION_CMD;
3778 }
3779 else if (!strcmp("-cmdstdin",argv[argno]))
3780 {
3781 use_stdin = TRUE;
3782 action = ACTION_CMD;
3783 }
3784 else if (!strcmp("-host",argv[argno]))
3785 {
3786 CHECKARG("tsserve",argno);
3787 err = host_value("tsserve",argv[argno],argv[argno+1],
3788 &output_name,&output_port);
3789 if (err) return 1;
3790 had_output_name = TRUE; // more or less
3791 argno++;
3792 }
3793 else if (!strcmp("-0",argv[argno]))
3794 {
3795 CHECKARG("tsserve",argno);
3796 had_input_name = TRUE;
3797 context.input_names[0] = argv[argno+1];
3798 argno++;
3799 }
3800 else if (!strcmp("-1",argv[argno]))
3801 {
3802 CHECKARG("tsserve",argno);
3803 had_input_name = TRUE;
3804 context.input_names[1] = argv[argno+1];
3805 argno++;
3806 }
3807 else if (!strcmp("-2",argv[argno]))
3808 {
3809 CHECKARG("tsserve",argno);
3810 had_input_name = TRUE;
3811 context.input_names[2] = argv[argno+1];
3812 argno++;
3813 }
3814 else if (!strcmp("-3",argv[argno]))
3815 {
3816 CHECKARG("tsserve",argno);
3817 had_input_name = TRUE;
3818 context.input_names[3] = argv[argno+1];
3819 argno++;
3820 }
3821 else if (!strcmp("-4",argv[argno]))
3822 {
3823 CHECKARG("tsserve",argno);
3824 had_input_name = TRUE;
3825 context.input_names[4] = argv[argno+1];
3826 argno++;
3827 }
3828 else if (!strcmp("-5",argv[argno]))
3829 {
3830 CHECKARG("tsserve",argno);
3831 had_input_name = TRUE;
3832 context.input_names[5] = argv[argno+1];
3833 argno++;
3834 }
3835 else if (!strcmp("-6",argv[argno]))
3836 {
3837 CHECKARG("tsserve",argno);
3838 had_input_name = TRUE;
3839 context.input_names[6] = argv[argno+1];
3840 argno++;
3841 }
3842 else if (!strcmp("-7",argv[argno]))
3843 {
3844 CHECKARG("tsserve",argno);
3845 had_input_name = TRUE;
3846 context.input_names[7] = argv[argno+1];
3847 argno++;
3848 }
3849 else if (!strcmp("-8",argv[argno]))
3850 {
3851 CHECKARG("tsserve",argno);
3852 had_input_name = TRUE;
3853 context.input_names[8] = argv[argno+1];
3854 argno++;
3855 }
3856 else if (!strcmp("-9",argv[argno]))
3857 {
3858 CHECKARG("tsserve",argno);
3859 had_input_name = TRUE;
3860 context.input_names[9] = argv[argno+1];
3861 argno++;
3862 }
3863 else if (!strcmp("-output",argv[argno]) || !strcmp("-o",argv[argno]))
3864 {
3865 CHECKARG("tsserve",argno);
3866 output_to_file = TRUE;
3867 had_output_name = TRUE;
3868 output_name = argv[argno+1];
3869 argno++;
3870 }
3871 else
3872 {
3873 fprintf(stderr,"### tsserve: "
3874 "Unrecognised command line switch '%s'\n",argv[argno]);
3875 return 1;
3876 }
3877 }
3878 else
3879 {
3880 if (had_input_name)
3881 {
3882 fprintf(stderr,"### tsserve: Unexpected '%s'\n",argv[argno]);
3883 return 1;
3884 }
3885 else
3886 {
3887 context.input_names[0] = argv[argno];
3888 had_input_name = TRUE;
3889 }
3890 }
3891 argno++;
3892 }
3893
3894 if (!had_input_name)
3895 {
3896 fprintf(stderr,"### tsserve: No input file specified\n");
3897 return 1;
3898 }
3899 if (!had_output_name && action != ACTION_SERVER)
3900 {
3901 fprintf(stderr,"### tsserve: No output specified\n");
3902 return 1;
3903 }
3904 if (output_to_file && action != ACTION_TEST)
3905 {
3906 fprintf(stderr,"### tsserve: Output to a file (-output) is only allowed"
3907 " with -test\n");
3908 return 1;
3909 }
3910
3911 if (!quiet)
3912 {
3913 printf("Input files:\n");
3914 for (ii = 0; ii < MAX_INPUT_FILES; ii++)
3915 {
3916 if (context.input_names[ii] != NULL)
3917 printf(" %2d: %s\n",ii,context.input_names[ii]);
3918 }
3919 }
3920
3921 for (ii = 0; ii < MAX_INPUT_FILES; ii++)
3922 {
3923 if (context.input_names[ii] != NULL)
3924 {
3925 context.default_file_index = ii;
3926 if (!quiet) printf("File %d (%s) selected as default\n",
3927 ii,context.input_names[ii]);
3928 break;
3929 }
3930 }
3931
3932 if (context.tsdirect && !quiet)
3933 printf("Serving all TS packets, not just video/audio streams\n");
3934
3935 if (context.drop_packets && !quiet)
3936 printf("DROPPING: Keeping %d TS packet%s, then dropping (throwing away) %d\n",
3937 context.drop_packets,(context.drop_packets==1?"":"s"),
3938 context.drop_number);
3939
3940 switch (action)
3941 {
3942 case ACTION_SERVER:
3943 err = run_server(&context,listen_port,verbose,quiet);
3944 if (err)
3945 {
3946 fprintf(stderr,"### tsserve: Error in server\n");
3947 return 1;
3948 }
3949 break;
3950
3951 case ACTION_TEST:
3952 err = test_reader(&context,output_to_file,output_name,output_port,
3953 num_normal,num_fast,num_faster,num_reverse,skiptest,
3954 verbose,quiet,context.tsdirect);
3955 if (err)
3956 {
3957 fprintf(stderr,"### tsserve: Error playing to %s\n",output_name);
3958 return 1;
3959 }
3960 break;
3961
3962 case ACTION_CMD:
3963 err = command_reader(&context,output_name,output_port,
3964 use_stdin,verbose,quiet);
3965 if (err)
3966 {
3967 fprintf(stderr,"### tsserve: Error playing to %s\n",output_name);
3968 return 1;
3969 }
3970 break;
3971
3972 default:
3973 fprintf(stderr,"### No action specified\n");
3974 return 1;
3975 }
3976 return 0;
3977 }
3978
3979 // Local Variables:
3980 // tab-width: 8
3981 // indent-tabs-mode: nil
3982 // c-basic-offset: 2
3983 // End:
3984 // vim: set tabstop=8 shiftwidth=2 expandtab:
3985