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