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