1 /*
2  * Support for "filtering" ES, outputting to either ES or TS.
3  *
4  * This provides the ability to "fast forward" through ES data.
5  *
6  * ***** BEGIN LICENSE BLOCK *****
7  * Version: MPL 1.1
8  *
9  * The contents of this file are subject to the Mozilla Public License Version
10  * 1.1 (the "License"); you may not use this file except in compliance with
11  * the License. You may obtain a copy of the License at
12  * http://www.mozilla.org/MPL/
13  *
14  * Software distributed under the License is distributed on an "AS IS" basis,
15  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16  * for the specific language governing rights and limitations under the
17  * License.
18  *
19  * The Original Code is the MPEG TS, PS and ES tools.
20  *
21  * The Initial Developer of the Original Code is Amino Communications Ltd.
22  * Portions created by the Initial Developer are Copyright (C) 2008
23  * the Initial Developer. All Rights Reserved.
24  *
25  * Contributor(s):
26  *   Amino Communications Ltd, Swavesey, Cambridge UK
27  *
28  * ***** END LICENSE BLOCK *****
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #ifdef _WIN32
37 #include <io.h>
38 #else // _WIN32
39 #include <unistd.h>
40 #endif // _WIN32
41 
42 #include "compat.h"
43 #include "es_fns.h"
44 #include "ts_fns.h"
45 #include "accessunit_fns.h"
46 #include "h262_fns.h"
47 #include "misc_fns.h"
48 #include "filter_fns.h"
49 
50 #define DEBUG 0
51 
52 
53 // ============================================================
54 // Managing H.262 filter contexts
55 // ============================================================
56 /*
57  * Build a new H.262 (MPEG-2 and also MPEG-1) filter context
58  *
59  * Returns 0 if all goes well, 1 if something goes wrong
60  */
new_h262_filter_context(h262_filter_context_p * fcontext)61 static int new_h262_filter_context(h262_filter_context_p *fcontext)
62 {
63   h262_filter_context_p new = malloc(SIZEOF_H262_FILTER_CONTEXT);
64   if (new == NULL)
65   {
66     fprintf(stderr,"### Unable to allocate H.262 filter context\n");
67     return 1;
68   }
69   new->h262 = NULL;
70   new->last_seq_hdr = NULL;
71   new->new_seq_hdr  = FALSE;
72 
73   reset_h262_filter_context(new);
74 
75   *fcontext = new;
76   return 0;
77 }
78 
79 /*
80  * Build a new filter context for "stripping" H.262 data
81  *
82  * - `fcontext` is the new filter context
83  * - `h262` is the H.262 stream to read from
84  * - `all_IP` is true if the software should keep all I and P pictures
85  *
86  * Returns 0 if all goes well, 1 if something goes wrong
87  */
build_h262_filter_context_strip(h262_filter_context_p * fcontext,h262_context_p h262,int all_IP)88 extern int build_h262_filter_context_strip(h262_filter_context_p *fcontext,
89                                            h262_context_p         h262,
90                                            int                    all_IP)
91 {
92   int err = new_h262_filter_context(fcontext);
93   if (err) return 1;
94 
95   (*fcontext)->h262 = h262;
96   (*fcontext)->filter = FALSE;
97   (*fcontext)->allref = all_IP;
98   return 0;
99 }
100 
101 /*
102  * Build a new filter context for "filtering" H.262 data
103  *
104  * - `fcontext` is the new filter context
105  * - `h262` is the H.262 stream to read from
106  * - `freq` is the desired speed-up, or the frequency at which frames
107  *   should (ideally) be kept
108  *
109  * Returns 0 if all goes well, 1 if something goes wrong
110  */
build_h262_filter_context(h262_filter_context_p * fcontext,h262_context_p h262,int freq)111 extern int build_h262_filter_context(h262_filter_context_p *fcontext,
112                                      h262_context_p         h262,
113                                      int                    freq)
114 {
115   int err = new_h262_filter_context(fcontext);
116   if (err) return 1;
117 
118   (*fcontext)->h262 = h262;
119   (*fcontext)->filter = TRUE;
120   (*fcontext)->freq = freq;
121   return 0;
122 }
123 
124 /*
125  * Reset an H.262 filter context, ready to start filtering anew.
126  */
reset_h262_filter_context(h262_filter_context_p fcontext)127 extern void reset_h262_filter_context(h262_filter_context_p  fcontext)
128 {
129   fcontext->pending_EOF = FALSE;
130   fcontext->last_was_slice = FALSE;
131   fcontext->had_previous_picture = FALSE;
132   if (fcontext->last_seq_hdr != NULL)
133     free_h262_picture(&fcontext->last_seq_hdr);
134   fcontext->new_seq_hdr = FALSE;
135 
136   fcontext->count = 0;
137   fcontext->frames_seen = 0;
138   fcontext->frames_written = 0;
139 }
140 
141 /*
142  * Free a filter context
143  *
144  * NOTE that this does *not* free the H.262 datastructure to which the
145  * filter context refers.
146  *
147  * - `fcontext` is the filter context, which will be freed, and returned
148  *   as NULL.
149  */
free_h262_filter_context(h262_filter_context_p * fcontext)150 extern void free_h262_filter_context(h262_filter_context_p *fcontext)
151 {
152   if ((*fcontext) == NULL)
153     return;
154 
155   // It's a little wasteful to call this, but on the other hand it is
156   // guaranteed to free everything we want freeing
157   reset_h262_filter_context(*fcontext);
158 
159   // Just lose our reference to the H.262 datastructure, don't free it
160   (*fcontext)->h262 = NULL;
161 
162   free(*fcontext);
163   *fcontext = NULL;
164   return;
165 }
166 
167 // ============================================================
168 // Managing H.264 filter contexts
169 // ============================================================
170 /*
171  * Build a new H.264 filter context
172  *
173  * Returns 0 if all goes well, 1 if something goes wrong
174  */
new_h264_filter_context(h264_filter_context_p * fcontext)175 static int new_h264_filter_context(h264_filter_context_p *fcontext)
176 {
177   h264_filter_context_p new = malloc(SIZEOF_H264_FILTER_CONTEXT);
178   if (new == NULL)
179   {
180     fprintf(stderr,"### Unable to allocate H.264 filter context\n");
181     return 1;
182   }
183 
184   // Unset the important things - things we might otherwise try to free
185   // (or, for new->es, things that stop us doing anything until we're
186   // setup properly by the user)
187   new->access_unit_context = NULL;
188 
189   reset_h264_filter_context(new);
190 
191   *fcontext = new;
192   return 0;
193 }
194 
195 /*
196  * Build a new filter context for "stripping" ES data
197  *
198  * - `fcontext` is the new filter context
199  * - `access` is the access unit context to read from
200  * - `allref` is true if the software should keep all reference pictures
201  *   (H.264) or all I and P pictures (H.264)
202  *
203  * Returns 0 if all goes well, 1 if something goes wrong
204  */
build_h264_filter_context_strip(h264_filter_context_p * fcontext,access_unit_context_p access,int allref)205 extern int build_h264_filter_context_strip(h264_filter_context_p *fcontext,
206                                            access_unit_context_p  access,
207                                            int                    allref)
208 {
209   int err = new_h264_filter_context(fcontext);
210   if (err) return 1;
211 
212   (*fcontext)->access_unit_context = access;
213   (*fcontext)->filter = FALSE;
214   (*fcontext)->allref = allref;
215   return 0;
216 }
217 
218 /*
219  * Build a new filter context for "filtering" ES data
220  *
221  * - `fcontext` is the new filter context
222  * - `access` is the access unit context to read from
223  * - `freq` is the desired speed-up, or the frequency at which frames
224  *   should (ideally) be kept
225  *
226  * Returns 0 if all goes well, 1 if something goes wrong
227  */
build_h264_filter_context(h264_filter_context_p * fcontext,access_unit_context_p access,int freq)228 extern int build_h264_filter_context(h264_filter_context_p *fcontext,
229                                      access_unit_context_p  access,
230                                      int                    freq)
231 {
232   int err = new_h264_filter_context(fcontext);
233   if (err) return 1;
234 
235   (*fcontext)->access_unit_context = access;
236   (*fcontext)->filter = TRUE;
237   (*fcontext)->freq = freq;
238   return 0;
239 }
240 
241 /*
242  * Reset an H.264 filter context, ready to start filtering anew.
243  */
reset_h264_filter_context(h264_filter_context_p fcontext)244 extern void reset_h264_filter_context(h264_filter_context_p  fcontext)
245 {
246   // `skipped_ref_pic` is TRUE if we've skipped any reference pictures
247   // since our last IDR (hmm - should it start off True or False?)
248   fcontext->skipped_ref_pic = FALSE;
249   // `last_accepted_was_not_IDR` is TRUE if the last frame kept (output)
250   // was not an IDR. We set it TRUE initially so that we will decide
251   // to output the first IDR we *do* find, regardless of the count.
252   fcontext->last_accepted_was_not_IDR = TRUE;
253   // And plainly we didn't have a previous access unit
254   fcontext->had_previous_access_unit = FALSE;
255   // Especially not an IDR
256   fcontext->not_had_IDR = TRUE;
257 
258   fcontext->count = 0;
259   fcontext->frames_seen = 0;
260   fcontext->frames_written = 0;
261 }
262 
263 /*
264  * Free an H.264 filter context
265  *
266  * NOTE that this does *not* free the access unit context to which the
267  * filter context refers.
268  *
269  * - `fcontext` is the filter context, which will be freed, and returned
270  *   as NULL.
271  */
free_h264_filter_context(h264_filter_context_p * fcontext)272 extern void free_h264_filter_context(h264_filter_context_p *fcontext)
273 {
274   if ((*fcontext) == NULL)
275     return;
276 
277   // It's a little wasteful to call this, but on the other hand it is
278   // guaranteed to free everything we want freeing
279   reset_h264_filter_context(*fcontext);
280 
281   // Just lose our reference to the access unit context, don't free it
282   (*fcontext)->access_unit_context = NULL;
283 
284   free(*fcontext);
285   *fcontext = NULL;
286   return;
287 }
288 
289 // ============================================================
290 // Filtering H.262
291 // ============================================================
292 /*
293  * Retrieve the next I (and/or, if fcontext->allref, P) frame in this H.262 ES.
294  *
295  * Any sequence end "pictures" will be ignored.
296  *
297  * Note that the ES data being read should be video-only.
298  *
299  * - `fcontext` is the information that tells us what to filter and how
300  * - if `verbose` is true, then extra information will be output
301  * - if `quiet` is true, then only errors will be reported
302  *
303  * - `seq_hdr` is a sequence header, i.e., that used by the next frame to
304  *   output. This will be NULL if the sequence header has not changed since
305  *   the last call of this function.
306  *
307  *   Note that the caller should *not* free this, and that it will not be
308  *   maintained over calls of this function (i.e., it is a reference to a
309  *   value within the `fcontext` which is altered by this function).
310  *
311  * - `frame` is the next frame to output.
312  *
313  *   Note that it is the caller's responsibility to free this with
314  *   `free_h262_picture()`.
315  *
316  *   If an error or EOF is returned, this value is undefined.
317  *
318  * - `frames_seen` is the number of I and P frames (start code 0)
319  *   found by this call of the function, including the item returned
320  *   if appropriate.
321  *
322  * Returns 0 if it succeeds, EOF if end-of-file is read (or the last call
323  * returned a sequence end item), 1 if some error occurs.
324  *
325  * If command input is enabled, then it can also return COMMAND_RETURN_CODE
326  * if the current command has changed.
327  */
get_next_stripped_h262_frame(h262_filter_context_p fcontext,int verbose,int quiet,h262_picture_p * seq_hdr,h262_picture_p * frame,int * frames_seen)328 extern int get_next_stripped_h262_frame(h262_filter_context_p  fcontext,
329                                         int                    verbose,
330                                         int                    quiet,
331                                         h262_picture_p        *seq_hdr,
332                                         h262_picture_p        *frame,
333                                         int                   *frames_seen)
334 {
335   int  err;
336 
337   // A picture is built up from several items - we start with none in hand
338   h262_picture_p this_picture = NULL;
339 
340   *frames_seen = 0;
341 
342   if (fcontext->filter)
343   {
344     fprintf(stderr,"### Calling get_next_stripped_h262_frame with a context"
345             " set for filtering\n");
346     return 1;
347   }
348 
349   // Otherwise, look for something we want to keep
350   for (;;)
351   {
352     if (es_command_changed(fcontext->h262->es))
353     {
354       *frame = *seq_hdr = NULL;
355       return COMMAND_RETURN_CODE;
356     }
357 
358     err = get_next_h262_frame(fcontext->h262,verbose,quiet,&this_picture);
359     if (err == EOF)
360     {
361       *frame = *seq_hdr = NULL;
362       return err;
363     }
364     else if (err)
365     {
366       fprintf(stderr,"### Error filtering H.262 frames\n");
367       return 1;
368     }
369 
370     // Now to stripping
371     if (this_picture->is_picture)
372     {
373       (*frames_seen) ++;
374       if ((this_picture->picture_coding_type == 1) ||
375           (this_picture->picture_coding_type == 2 && fcontext->allref) )
376       {
377         *frame = this_picture;
378         if (fcontext->new_seq_hdr)
379           *seq_hdr = fcontext->last_seq_hdr;
380         else
381           *seq_hdr = NULL;
382         fcontext->new_seq_hdr = FALSE;
383         if (verbose) printf(">> %s picture \n",
384                             (this_picture->picture_coding_type==1?"I":"P"));
385         return 0;
386       }
387       else
388         free_h262_picture(&this_picture);
389     }
390     else if (this_picture->is_sequence_header)
391     {
392       // We maybe want to remember this sequence header for the next picture
393       if (fcontext->last_seq_hdr == NULL)
394       {
395         fcontext->last_seq_hdr = this_picture;
396         fcontext->new_seq_hdr = TRUE;
397         if (verbose) printf(">> First sequence header\n");
398       }
399       else if (!same_h262_picture(this_picture,fcontext->last_seq_hdr))
400       {
401         if (verbose) printf(">> Different sequence header\n");
402         free_h262_picture(&fcontext->last_seq_hdr);
403         fcontext->last_seq_hdr = this_picture;
404         fcontext->new_seq_hdr = TRUE;
405       }
406       else
407       {
408         fcontext->new_seq_hdr = FALSE;
409         if (verbose) printf(">> Identical sequence header\n");
410         free_h262_picture(&this_picture);
411       }
412     }
413   }
414 }
415 
416 /*
417  * Retrieve the next I frame, from the H.262 ES, aiming for an "apparent" kept
418  * frequency as stated.
419  *
420  * Any sequence end "pictures" will be ignored.
421  *
422  * Note that the ES data being read should be video-only.
423  *
424  * - `fcontext` is the information that tells us what to filter and how
425  *   (including the desired frequency)
426  * - if `verbose` is true, then extra information will be output
427  * - if `quiet` is true, then only errors will be reported
428  *
429  * - `seq_hdr` is a sequence header, i.e., that used by the next picture to
430  *   output. This will be NULL if `frame` is NULL.
431  *
432  *   Note that the caller should *not* free this, and that it will not be
433  *   maintained over calls of this function (i.e., it is a reference to a
434  *   value within the `fcontext` which is altered by this function).
435  *
436  * - `frame` is the next frame to output. This will be NULL if the last frame
437  *   should be output again, to provide the requested apparent frequency.
438  *
439  *   Note that it is the caller's responsibility to free this with
440  *   `free_h262_picture()`.
441  *
442  *   If an error or EOF is returned, this value is undefined.
443  *
444  * - `frames_seen` is the number of I and P frames found by this call of
445  *   the function, including the item returned if appropriate.
446  *
447  * Returns 0 if it succeeds, EOF if end-of-file is read, or we've just read a
448  * sequence end item, or the last call ended a picture on a sequence end
449  * item, 1 if some error occurs.
450  *
451  * If command input is enabled, then it can also return COMMAND_RETURN_CODE
452  * if the current command has changed.
453  */
get_next_filtered_h262_frame(h262_filter_context_p fcontext,int verbose,int quiet,h262_picture_p * seq_hdr,h262_picture_p * frame,int * frames_seen)454 extern int get_next_filtered_h262_frame(h262_filter_context_p  fcontext,
455                                         int                    verbose,
456                                         int                    quiet,
457                                         h262_picture_p        *seq_hdr,
458                                         h262_picture_p        *frame,
459                                         int                   *frames_seen)
460 {
461   int  err;
462 
463   // A picture is built up from several items - we start with none in hand
464   h262_picture_p this_picture = NULL;
465 
466   *frames_seen = 0;
467 
468   if (!fcontext->filter)
469   {
470     fprintf(stderr,"### Calling get_next_filtered_h262_frame with a context"
471             " set for stripping\n");
472     return 1;
473   }
474 
475   // Otherwise, look for something we want to keep
476   for (;;)
477   {
478     if (es_command_changed(fcontext->h262->es))
479     {
480       *frame = *seq_hdr = NULL;
481       return COMMAND_RETURN_CODE;
482     }
483 
484     // If the picture is an I picture, we want it to contain an appropriate
485     // AFD - so ask for that
486     fcontext->h262->add_fake_afd = TRUE;
487 
488     err = get_next_h262_frame(fcontext->h262,verbose,quiet,&this_picture);
489     if (err == EOF)
490     {
491       *frame = *seq_hdr = NULL;
492       fcontext->h262->add_fake_afd = FALSE;
493       return err;
494     }
495     else if (err)
496     {
497       fprintf(stderr,"### Error filtering H.262 frames\n");
498       fcontext->h262->add_fake_afd = FALSE;
499       return 1;
500     }
501 
502     // Reinstate normal "only include actual AFDs"
503     fcontext->h262->add_fake_afd = FALSE;
504 
505     // Now to filtering
506     if (this_picture->is_picture)
507     {
508       fcontext->count ++;
509       (*frames_seen) ++;
510 
511       fcontext->frames_seen ++;
512 
513       if (this_picture->picture_coding_type == 1 &&
514           fcontext->count < fcontext->freq)
515       {
516         // It is an I picture, but it is too soon
517         if (verbose)
518         {
519           printf("+++ %d/%d DROP: Too soon\n",fcontext->count,fcontext->freq);
520         }
521       }
522       else if (this_picture->picture_coding_type != 1)
523       {
524         // It is not an I picture
525         if (verbose)
526         {
527           printf("+++ %d/%d DROP: %s picture\n",fcontext->count,fcontext->freq,
528                  H262_PICTURE_CODING_STR(this_picture->picture_coding_type));
529         }
530         // But do we want to pad with (i.e., repeat) the previous I picture?
531         if (fcontext->freq > 0)
532         {
533           int pictures_wanted = fcontext->frames_seen / fcontext->freq;
534           int repeat = pictures_wanted - fcontext->frames_written;
535           if (repeat > 0 && fcontext->had_previous_picture)
536           {
537             if (verbose) printf(">>> output last picture again\n");
538             free_h262_picture(&this_picture);
539             *seq_hdr = NULL;
540             *frame = NULL;
541             fcontext->frames_written ++;
542             return 0;
543           }
544         }
545       }
546       else
547       {
548         // It was an I picture, and not too soon
549         if (verbose)
550         {
551           printf("+++ %d/%d KEEP\n",fcontext->count,fcontext->freq);
552         }
553         fcontext->count = 0;
554         fcontext->had_previous_picture = TRUE;
555         *seq_hdr = fcontext->last_seq_hdr;
556         *frame = this_picture;
557 
558         fcontext->frames_written ++;
559         return 0;
560       }
561       free_h262_picture(&this_picture);
562     }
563     else if (this_picture->is_sequence_header)
564     {
565       // We want to remember the sequence header for the next picture
566       if (fcontext->last_seq_hdr != NULL)
567         free_h262_picture(&fcontext->last_seq_hdr);
568       fcontext->last_seq_hdr = this_picture;
569     }
570   }
571 }
572 
573 // ============================================================
574 // Filtering H.264
575 // ============================================================
576 /*
577  * Return the next IDR or I (and maybe any reference) frame from this H.264 ES.
578  *
579  * Note that the ES data being read should be video-only.
580  *
581  * - `fcontext` is the information that tells us what to filter and how
582  * - if `verbose` is true, then extra information will be output
583  * - if `quiet` is true, then only errors will be reported
584  * - `frame` is the next frame to output.
585  *   Note that it is the caller's responsibility to free this with
586  *   `free_access_unit()`.
587  *   If an error or EOF is returned, this value is undefined.
588  * - `frames_seen` is the number of frames found by this call
589  *   of the function, including the frame returned.
590  *
591  * Returns 0 if it succeeds, EOF if end-of-file is read (or an an end of
592  * stream NAL unit has been passed), 1 if some error occurs.
593  *
594  * If command input is enabled, then it can also return COMMAND_RETURN_CODE
595  * if the current command has changed.
596  */
get_next_stripped_h264_frame(h264_filter_context_p fcontext,int verbose,int quiet,access_unit_p * frame,int * frames_seen)597 extern int get_next_stripped_h264_frame(h264_filter_context_p  fcontext,
598                                         int                    verbose,
599                                         int                    quiet,
600                                         access_unit_p         *frame,
601                                         int                   *frames_seen)
602 {
603   int err = 0;
604   int keep = FALSE;  // Should we keep the current access unit?
605   access_unit_p  this_access_unit = NULL;
606 
607   *frames_seen = 0;
608 
609   for (;;)
610   {
611     if (es_command_changed(fcontext->access_unit_context->nac->es))
612       return COMMAND_RETURN_CODE;
613 
614     if (verbose)
615       printf("\n");
616 
617     err = get_next_h264_frame(fcontext->access_unit_context,quiet,verbose,
618                               &this_access_unit);
619     if (err == EOF)
620       return err;
621     else if (err)
622       return 1;
623 
624     (*frames_seen) ++;
625 
626     if (this_access_unit->primary_start == NULL)
627     {
628       // We don't have a primary picture - no VCL NAL
629       // There seems little point in keeping the access unit
630       keep = FALSE;
631       if (verbose)
632         printf("++ DROP: no primary picture\n");
633     }
634     else if (this_access_unit->primary_start->nal_ref_idc == 0)
635     {
636       // This is not a reference frame, so it's of no interest
637       keep = FALSE;
638       if (verbose)
639         printf("++ DROP: not reference\n");
640     }
641     else if (fcontext->allref)
642     {
643       // We want to keep all reference frames
644       if (this_access_unit->primary_start->nal_unit_type == NAL_IDR ||
645           this_access_unit->primary_start->nal_unit_type == NAL_NON_IDR)
646       {
647         keep = TRUE;
648         if (verbose)
649           printf("++ KEEP: reference picture\n");
650       }
651       else
652       {
653         keep = FALSE;
654         if (verbose)
655           printf("++ DROP: sequence or parameter set, etc.\n");
656       }
657     }
658     else
659     {
660       // We only want to keep IDR and I frames
661       if (this_access_unit->primary_start->nal_unit_type == NAL_IDR)
662       {
663         keep = TRUE;
664         if (verbose)
665           printf("++ KEEP: IDR picture\n");
666       }
667       else if (this_access_unit->primary_start->nal_unit_type == NAL_NON_IDR &&
668                all_slices_I(this_access_unit))
669       {
670         keep = TRUE;
671         if (verbose)
672           printf("++ KEEP: all slices I\n");
673       }
674       else
675       {
676         keep = FALSE;
677         if (verbose)
678           printf("++ DROP: not IDR or all slices I\n");
679       }
680     }
681 
682     if (keep)
683     {
684       *frame = this_access_unit;
685       return 0;
686     }
687     // We've no further use for this access unit
688     free_access_unit(&this_access_unit);
689   }
690 }
691 
692 /*
693  * Retrieve the next frame from the H.264 (MPEG-4/AVC) ES, aiming
694  * for an "apparent" kept frequency as stated.
695  *
696  * Note that the ES data being read should be video-only.
697  *
698  * - `fcontext` is the information that tells us what to filter and how
699  *   (including the desired frequency)
700  * - if `verbose` is true, then extra information will be output
701  * - if `quiet` is true, then only errors will be reported
702  *
703  * - `frame` is the next frame to output.
704  *
705  *   If the function succeeds and `frame` is NULL, it means that the
706  *   last frame should be output again.
707  *
708  *   Note that it is the caller's responsibility to free this frame with
709  *   `free_access_unit()`.
710  *
711  *   If an error or EOF is returned, this value is undefined.
712  *
713  * - `frames_seen` is the number of frames found by this call of the function,
714  *   including the frame returned.
715  *
716  * Returns 0 if all went well, 1 if something went wrong.
717  *
718  * If command input is enabled, then it can also return COMMAND_RETURN_CODE
719  * if the current command has changed.
720  */
get_next_filtered_h264_frame(h264_filter_context_p fcontext,int verbose,int quiet,access_unit_p * frame,int * frames_seen)721 extern int get_next_filtered_h264_frame(h264_filter_context_p  fcontext,
722                                         int                    verbose,
723                                         int                    quiet,
724                                         access_unit_p         *frame,
725                                         int                   *frames_seen)
726 {
727   int err = 0;
728   int keep = FALSE;  // Should we keep the current access unit?
729   access_unit_p  this_access_unit = NULL;
730 
731   *frames_seen = 0;
732 
733   for (;;)
734   {
735     if (es_command_changed(fcontext->access_unit_context->nac->es))
736       return COMMAND_RETURN_CODE;
737 
738     if (verbose)
739       printf("\n");
740 
741     err = get_next_h264_frame(fcontext->access_unit_context,quiet,verbose,
742                               &this_access_unit);
743     if (err == EOF)
744       return err;
745     else if (err)
746       return 1;
747 
748     fcontext->count ++;
749     (*frames_seen) ++;
750 
751     fcontext->frames_seen ++;
752 
753     if (this_access_unit->primary_start == NULL)
754     {
755       // We don't have a primary picture - no VCL NAL
756       // There seems little point in keeping the access unit
757       keep = FALSE;
758       if (verbose)
759         printf("++ %d/%d DROP: no primary picture\n",
760                fcontext->count,fcontext->freq);
761     }
762     else if (this_access_unit->primary_start->nal_ref_idc == 0)
763     {
764       // This is not a reference frame, so it's of no interest
765       keep = FALSE;
766       if (verbose)
767         printf("++ %d/%d DROP: not a reference frame\n",
768                fcontext->count,fcontext->freq);
769     }
770     else if (this_access_unit->primary_start->nal_unit_type == NAL_IDR &&
771              fcontext->last_accepted_was_not_IDR)
772     {
773       // This frame is an IDR, and the last frame kept was not, so
774       // we'll output it regardless - we don't expect to get enough
775       // IDR pictures that this will be a problem, and they're
776       // valuable because they're the "limit" for other frames that
777       // refer backwards
778       // (should we reset the count to zero? - seems sensible)
779       keep = TRUE;
780       fcontext->not_had_IDR = FALSE;
781       fcontext->skipped_ref_pic = FALSE;
782       fcontext->last_accepted_was_not_IDR = FALSE;
783       if (verbose)
784         printf("++ %d/%d KEEP: IDR and last was not\n",
785                fcontext->count,fcontext->freq);
786     }
787     else if (this_access_unit->primary_start->nal_unit_type == NAL_IDR &&
788              fcontext->not_had_IDR)
789     {
790       // We haven't had an IDR yet in this filter run, so we had better
791       // output this one as a "good start"
792       keep = TRUE;
793       fcontext->skipped_ref_pic = FALSE;
794       fcontext->last_accepted_was_not_IDR = FALSE;
795       if (verbose)
796         printf("++ %d/%d KEEP: IDR and first IDR of filter run\n",
797                fcontext->count,fcontext->freq);
798     }
799     else if (fcontext->count < fcontext->freq)
800     {
801       // It's too soon, so ignore it - but notice that we *have*
802       // ignored a reference picture
803       keep = FALSE;
804       fcontext->skipped_ref_pic = TRUE;
805       if (verbose)
806         printf("++ %d/%d DROP: Too soon (skipping ref frame)\n",
807                fcontext->count,fcontext->freq);
808     }
809     else if (this_access_unit->primary_start->nal_unit_type == NAL_IDR)
810     {
811       // It's an IDR, so output it
812       keep = TRUE;
813       fcontext->skipped_ref_pic = FALSE;
814       fcontext->last_accepted_was_not_IDR = FALSE;
815       if (verbose)
816         printf("++ %d/%d KEEP: IDR\n",fcontext->count,fcontext->freq);
817     }
818     else if (all_slices_I(this_access_unit))
819     {
820       // It is an I picture (either it has all of its slices
821       // type "I", or it has a single slice which is of type "I")
822       keep = TRUE;
823       fcontext->last_accepted_was_not_IDR = TRUE;
824       if (verbose)
825         printf("++ %d/%d KEEP: I frame\n",fcontext->count,fcontext->freq);
826     }
827     else if (!fcontext->skipped_ref_pic && all_slices_I_or_P(this_access_unit))
828     {
829       // It is a P or I&P picture, but we know that we have output all
830       // the reference pictures since the last IDR, so it is
831       // safe to output it
832       keep = TRUE;
833       fcontext->last_accepted_was_not_IDR = TRUE;
834       if (verbose)
835         printf("++ %d/%d KEEP: P frame. no skipped ref frames\n",
836                fcontext->count,fcontext->freq);
837     }
838     else
839     {
840       keep = FALSE;
841       fcontext->skipped_ref_pic = TRUE;
842       if (verbose)
843         printf("++ %d/%d DROP: ref frame skipped earlier\n",
844                fcontext->count,fcontext->freq);
845     }
846 
847     if (keep)
848     {
849       *frame = this_access_unit;
850       fcontext->had_previous_access_unit = TRUE;
851       fcontext->frames_written ++;
852       fcontext->count = 0;
853       return 0;
854     }
855     else
856     {
857       if (fcontext->freq > 0)
858       {
859         int access_units_wanted = fcontext->frames_seen / fcontext->freq;
860         int repeat = access_units_wanted - fcontext->frames_written;
861         if (repeat > 0 && fcontext->had_previous_access_unit)
862         {
863           if (verbose) printf(">>> output last access unit again\n");
864           free_access_unit(&this_access_unit);
865           *frame = NULL;
866           fcontext->frames_written ++;
867           return 0;
868         }
869       }
870       // We've no further use for this access unit
871       free_access_unit(&this_access_unit);
872     }
873   }
874 }
875 
876 // Local Variables:
877 // tab-width: 8
878 // indent-tabs-mode: nil
879 // c-basic-offset: 2
880 // End:
881 // vim: set tabstop=8 shiftwidth=2 expandtab:
882