2 #define AUTHOR "inkling"
3 #define EMAIL "inkling@users.sourceforge.net"
4 #define WEBPAGE "http://atscap.sourceforge.net"
5 #define COPYRIGHT "(C) 2005-2008"
6 #define LICENSE "GNU General Public License Version 2"
7 #define LASTEDIT "20080102"
8 #define VERSION "1.3.5"
10 * xtscut.c (C) Copyright 2005-2008 by inkling@users.sorceforge.net
11 * X Transport Stream Cut Utility
12 * xtscut is a graphical visual ATSC transport stream cutting utility, with
13 * IPB frame statistics display from atscap/atscut generated .tsx files.
15 * xtscut is free software; you may only redistribute it and/or modify
16 * it under the terms of the GNU General Public License, Version 2 or later,
17 * as published by the Free Software Foundation.
19 * xtscut is distributed to you in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License Version 2
25 * along with this program; if not, write the Free Software Foundation, Inc.,
26 * at 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 Xtscut.c version 1.3.5
32 X Transport Stream Cutting Utility
33 Copyright (c) 2004-2008 by inkling@users.sourceforge.net
38 * Compile xtscut with:
40 * gcc -Wall -O3 -lrt -lImlib2 -lmpeg2 -lmpeg2convert -lX11 \
41 * (optional) -L/usr/lib/X11R6 or -L/usr/X11R6/lib \
45 * librt real time clock for timing file i/o
46 * you may have to change defined clock_method
48 * imlib-config or imlib2-config --libs will show full dependency list
50 * libImlib2 ImageMagick 2 library handles X images
51 * tested with version 1.2.1.009
52 * libImlib ImageMagick library handles X images
53 * tested with version 1.9.14
55 * libmpeg2 mpeg2dec package
56 * libmpeg2convert mpeg2dec package
57 * tested with version 0.4.1
59 * libX11 Xlib generic interface
66 The MPEG2 Intra frame parse is, more or less, sample2.c from libmpeg2.
67 It needed a few changes, but not a whole lot, so the authors deserve
68 credit for making it flexible and explanatory enough to get the job done.
69 It would have taken longer w/o libmpeg2. Thank you libmpeg2 developers!
71 The Imlib and X developers also deserve many thanks for their
72 good documentation and example code to get the bitmap moves done easily.
78 A different visual approach to content extraction, that tries to have
79 minimal impact on stream content aside from clock discontinuities at cuts.
81 This is no nifty stream re-timing tool. It splits-em-apart and
82 slaps them back together, without giving any stream cut indication.
84 It will be up to your player to handle it properly through the cuts.
85 xine-hd 0.8 from pcHDTV.com is what I use and it handles it well enough.
86 Newer version of xine may also work fine. Mplayer acts a bit odd.
88 xtscut will open a .ts file and a window to display the packet counts
89 used for each of the I, P and B frames. Cuts are done on sequence starts.
90 .tsx sequence files can be auto generated with atscap -m option, or
91 may be generated separately with atscut -s file.ts.
93 Time is estimate from frame number, is also x dimension, or width.
94 Short sequences will make this number useless, but it's only for display.
95 Usually, it's either nearly correct or 2x too large. Needs a frame rate
96 detection routine to set the nominal frame rate to make it more accurate.
98 Packet count for each frame type is y dimension, or height. Each
99 pixel represents a single packet used for that frame type. Each frame
100 type has a different color, but they blend together to make other colors.
102 The IPB legend at the bottom may be used to select or de-select each
103 frame type packet count line from the display list to help identify frames.
106 Scrollwheel moves forwards (down) and backwards (up) through the file:
108 LMB selects cut point to nearest sequence start/Intra frame
109 A vertical red line is used to indicate the cut, with the
110 cut number at the bottom of the line. It's a toggle. Click
111 it again to remove the cut if it's in the wrong place. Cuts
112 are saved and renumbered automatically.
114 MMB moves to percentage of viewable horizontal area as view-finder
115 Grey bar below IPB legend area gives a visual positional indication.
117 RMB is the preview, it renders the Intra frame under the mouse.
118 The vertical white line indicates the current preview Intra frame.
120 MB4 and MB5 are backwards and forwards 480 frames, 1/2 window width.
121 Jump distance will be proportional to the current window size.
122 480 frames is half of the horizontal default video size of 960x544.
124 Keys (X input focus is IPB graph window, not xterm invoked from):
131 d dump cuts list to stdout and cut file
132 q quit (or control C the xterm invocation)
133 g list local memory allocations (not libvo or imlib)
135 F1 toggles hide histogram lines
136 F2 toggles hide IPB legend and buttons
137 F3 toggles hide timecodes
138 F4 toggles hide visual position bar
142 a jump to previous cut
145 j or LeftArrow moves backward one Intra frame
146 k or RightArrow moves forward one Intra frame
148 i or UpArrow moves backward about 960 frames, or 1x window width
149 o or DownArrow moves backward about 960 frames, or 1x window width
151 y or PageUP moves backward about 1920 frames, 2x window width
152 o or PageDown moves forward about 1920 frames, 2x window width
154 [ or Home moves to start of stream
155 ] or End moves to end of stream
158 Keys are only active when not cutting, but the X keybuf remembers them.
163 EXTRA FREE BONUS X BACKWARDS COMPATIBILITY LIMITATION:
165 Using RGB instead of YUV overlay means it will probably run on any X.
166 The default is to use imlib and not the YUV overlay. If you have a new
167 system, compile it with -DUSE_LIBVO to see a 2x render speed boost.
169 You might be able to compile the XVideo version with "make xtscut3".
170 Don't compile the -DUSE_LIBVO version if using XFree86 4.0.2 or older.
171 XVideo and XShm were not well supported until XFree86 4.0.3 and later.
172 Video devices older than appoximately GeForce2 may not work with YUV12.
174 It was tested back to XFree86 3.3 (a couple of years ago), but because
175 of the simple nature of the Xlib usage, it will probably work on any
176 X server from the past 10 years, if you compile the right version.
178 Two-button mouse and keyboard navigation should work fine, and has
179 actually been tested with old 2-button mouse and trackball.
181 XLIB EXTENSION "SHAPE" MISSING ON DISPLAY
182 You might see this with OpenSSH. Use ssh -Y to start the session.
183 Also, make sure X config has Load "extmod" in Section "Module". [?]
184 You may want to use the -w option to reduce the resolution if you
185 have limited bandwidth over the connection.
188 XVideo uses XShm. It will only work when server is same as client.
189 Use imlib1 or imlib2 versions for remote client sessions.
190 For remote sessions, you will probably want to turn off the extra
191 eye-candy (shading, cornering) too, by hitting F5 key.
197 1.3.5 added Eye-Candy in time for Christmas!
198 added x_draw_cutlist and supporting functions
199 added 8x8 rounded corner algorithm to x_draw_quad_gradientv
200 added gradient to timecode area at top of histogram
201 added gradient to IPB button area
202 added gradient to cut number area
203 added cut area indication to slider bar coloration
204 added F5 key to turn off gradientv eye candy (remote session)
206 TODO change slider looks bad with eye-candy off, either
207 draw all 3 lines or only draw one if arg_ec clear
209 changed x_draw_histogram P & B lines use XDrawSegments
210 changed special keys use X11/keysym.h
211 changed [d] key uses x_draw_cutlist
212 changed -k0 tests last Sequence for missing data cruft
213 changed -k1 doesn't test last Sequence for missing data cruft
214 changed timecode colors for odd cuts is now red
215 changed timecode font size to 10x20, format to hh:mm:ss
216 changed timecode spacing to 120 frames to fit font+format
218 disabled stdout show_scuts
220 fixed input scan failed on very short file
221 fixed draw_histogram case 1: missing break, I/P frame wrong
222 fixed short streams were not starting render at frame 0
223 fixed frames[].cut not cleared, confused find prev/next cut
225 removed USE_BUFFER and buffer.h related code
227 ------------------------------------------------------------------------------
229 1.3.4 added show allocs to view local memory allocations
230 added [g] key toggles GOP broken_link setting (is slower)
231 added frames, iframes, and sequences use dynamic allocation
232 added error return checking to alloc frames
233 added input file extension check for '.e*' for Video ES
234 added open/close buffer function, calls init/flush
235 added parse ts header function and tsh_t typedef
236 added x_draw_slider bead visual stream position feedback
237 added -g option run test GOP broken link at start of cut
238 added cursor pad and numpad keys for navigation
239 added F1-F4 keys to toggle: histogram, IPB, timecodes, slider
240 added load/save_config for /etc/atscap/xtscut.conf
241 added x_draw_quad_gradientv for shaded fill quadrangles
242 added show_keyhelp and show_cfghelp video overlays
244 TEST added -z option to set buffer sizes, 188 to 128k bytes
245 [may be useless, 4096 always seem to be optimal]
247 changed -n option not given strips (most) NULLs from output
248 changed PSI0 audio packets at start of cut NULL'd or skipped
249 changed test GOP broken link to work with larger buffers
250 changed input scan uses parse ts header
251 changed input scan looks for video and audio start codes
252 changed build tsx uses parse ts header
253 changed parse video es uses parse ts header
254 changed read/write code to use buffers larger than 188 bytes
255 changed [n] toggles nulls, [m] toggles multi/single write
256 changed [j]/[k] now 1 Intra skip
258 disabled libvo XShm and non YV12 XVideo modes, temporarily
259 [YV12 gets by without mpeg2_convert, XShm and YUYV need it]
261 TODO fix x resize display so keys 1 2 4 8 and 0 set display size
262 TODO and fix maximize to set non-native image aspect and offsets
264 fixed button select can get two buttons at once
265 fixed cut numbering wasn't correct, should not start at 0.
267 removed buffer.h code to optional compile (is slower)
268 removed -y option, uses input file extension .e* check now
269 removed shifted navkeys, due to lack of use
270 removed shifted option key duplicates, also unused
271 removed +/- 30/60/90 frame navkeys
273 1.3.3 added -y option for Video Elementary Stream (file.es)
274 added build fsx, build tsx, build esx
275 added generic buffer code in buffer.h
276 added lltoasc to show scuts
278 changed ts/es file I/O to use buffer.h functions
279 changed 'system atscut -s' is fallback, uses build_fsx now
281 fixed write disabled when input and output are same file
282 fixed GOP broken link wasn't being set at start of cuts
284 Peter Knaggs sent in this modern system benchmark for XvYV12:
285 128 Intra frames in 3.06 seconds at 41.83 FPS WOW!
288 1.3.2 added [b] key for NULL blanks at start of each cut
289 added discard Xvideo buffers to x close_display
290 added mpeg2dec/libvo/video_out_x11 for faster video render
291 Benchmarks for 1280x720p on 1.6 GHz ATSC test system:
294 XvYV12 IFPS 23 WOW! (-m1 is default)
296 changed back to [n]/[m] keys for Intra frame single-step
298 make xtscut for new imlib2 version (is new default)
299 make xtscut1 for old imlib1 version (slowest)
300 make xtscut2 for new imlib2 version (medium)
301 make xtscut3 for new XVideo version (fastest & buggy?)
304 TODO export signal and console code from atscap
305 TODO fix missing PNG images for XVideo compiles (needs imlib2)
306 TODO verify XVideo + libmpeg2 renders directly to video memory
309 1.3.1 changed [w] key, -ca option writes multiple files [ivary]
310 REVERSED changed [n] key to null toggle, single iframe step is [,][.]
311 changed NULL packet generation code
312 changed show scuts counts the NULLs if -n option used
313 changed write block subs NULLs for initial PSI0 audio packets
316 1.3.0 added support for imlib2, 10-25% faster render with -b
317 added -b option for benchmark test of imlib1 vs imlib2
318 added build outname function
319 added [1] key for single/multi file write toggle
320 added abort write and warn user if output same as input
321 added button1 toggles legend area timecode/frame rate
322 added define for librt clock_gettime and fallback
324 changed start and stop timer to be more generic if no librt
325 changed MMB is disabled for short streams with frames < width
326 changed read_buffer switch cleanup
327 changed how and where xtscut end png is rendered
328 changed [n] & [m] keys use find prev/next_iframe function
330 fixed x close display memory leak, not freeing image
331 fixed test clock res had wrong clock method string indices
332 fixed Makefile to try to use imlib1/2-config scripts
333 fixed find nearest_iframe sometimes returns 0 [ivary]
334 fixed preview on 'The End' doesn't redraw image after cut
336 testing x_resize_display, aspect is OK, but has render bugs.
337 Need to scale the image w/o changing vw.* and leave
338 vw.* for window control only
340 TODO? build outname should create new name if output exists
343 1.2.9 added option -k to keep last sequence via fake last I-frame
344 added option -a to set mpeg2 accel mode (default autodetects)
345 added PNG_PATH define to set path for png files
346 added pngs for splash, bad frame and end of stream
347 added -n outputs 1/8s NULL packets to start of each cut
348 added [a] key shows previous cut
349 assed [s] key shows next cut
350 added [t] key toggles frame count/time code
352 changed global mpeg2 init and structures
353 changed -n outbasename is now -o outbasename
354 changed frame 0 cut selection disabled
355 changed frame histogram stops at last I-frame line + 2
356 changed display xtscut-end for last I-frame
357 changed short stream starts preview at frame 0
359 fixed generational loss of last sequence with -k option
360 fixed skip initial P and B frames until first I-frame
361 fixed repetitive redraws when scrolling hits container limits
363 fixed disable x events debug code discarding events [rich123]
366 1.2.8 added setenv "XLIB_SKIP_ARGB_VISUAL=1" [Peter Knaggs]
367 added x button quad for mouse button position test
368 added aspect ratio of source to GC setup
369 added white line as current preview location
370 added show current preview frame number above IPB legend
371 added -w option to set intial screen width
372 added xrender hold down to x_redraw_display
374 changed button test logic is a bit cleaner
375 changed ignore mouse events in frame count area above IPB
376 changed button2 uses floating point to calc frame offset
377 changed cut line is white if current preview frame
378 changed X event VisibilityChangeMask to FocusChangeMask
379 changed Expose event handling to conditional complile
381 removed redundant calls to x_redraw_display
383 TODO closer button and event handler for it [is it really needed?]
384 TODO time-code toggle to frame number? [done in 1.3.0]
385 TODO full-screen toggle is still broken, fix it or scrap it?
386 [slightly better results in 1.3.0, but still broken]
389 1.2.7 added track last frame drawn to hold down parse intra_frame
390 added bit rate scaling to histogram lines
391 added write nulls to start of stream to help player sync
393 fixed coarse navkeys do not redraw near start/end [ivary]
396 1.2.6 added frame count for cuts and totals
397 added frame rate calc from libmpeg2 info structure
398 changed show ss_timecode to show frame_timecode
399 changed timecode colors for odd cuts
400 fixed input scan fails with certain test streams with nulls
403 1.2.5 fixed -ce not writing even cuts, missing build scut_frames
404 fixed draw cutnum uneven and obscured by IPB controls
407 1.2.4 added percentage of source file for cuts and totals
408 added save cuts each time a cut is inserted or deleted
409 fixed crash on last sequence preview, can still select as cut
413 1.2.3 added x_set_font
414 changed x draw_time called every 2s of frames, not 4 i-frames
415 changed x draw_cutnum larger font
418 1.2.2 changed show_ss_timecode so [d]ump cuts is more accurate
419 changed volume status checks arg_path free space
422 1.2.1 disabled [f] key full screen because it's still too buggy
425 1.2.0 added write patpmt and fixed write scuts to use it
426 added payload start hold downs for cuts to smooth audio start
427 changed input scan to autodetect of PAT + PMT pids
428 fixed -p path wasn't being used for -1 multiple file cuts
431 1.1.9 added system call to atscut -s if .tsx file missing
432 added input scan autodetect video PID
433 changed -v option is version instead of video PID
435 TEST: -p -n from read-only source directory. will fail?
437 Final throughput testing results for PATA 300G Maxtors:
438 Testing cuts to same volume ~ 30-40MB/second, EXT3 & XFS.
439 Testing XFS to EXT3 maxes out around 60MB/second.
440 Testing EXT3 to XFS maxes out around 80MB/second.
441 Testing XFS to XFS maxes out around 111MB/second. WOW!
444 1.1.8 added [c] key to clear all cuts
447 1.1.7 added cuts have gop broken link set if closed gop not set
448 fixed cut from gui needs i/o block index reset. -ce is OK.
451 1.1.6 added delays in lieu of duty-cycle limiter to output
454 1.1.5 remap keys: o to r; y/o now 2x u/i jump, shift does 2x more
455 added test clock res to set more accurate clock method
458 1.1.4 fixed IPB legend/buttons above cut number, not covering it
459 fixed -1 and even or odd cut write only wrote one file
460 added -co/e/a option, if .tsc file exists, cut and exit
461 added -r cut renumber option for 01 02 03 from odd/even
464 1.1.3 added limit check for input sequences and frames
467 1.1.2 reduced gcc pre-processor million item list with alloc frames
468 use malloc not calloc to hold down page commit until use
471 1.1.1 nfs read performance far exceeds samba read performance:
472 added -o -n output options for read only media.
475 1.1 changed to single .tsx file after changing atscap and atscut
478 1.0r3 gcc audit: cleanups for -pedantic -std=c99 and gcc -ansi
481 1.0r2 added -b output basename [changed to -n in 1.1.1]
484 1.0r1 trimmed cruft for addition to atscut, which may not get done
487 1.0 added buffered i/o for cut writes and video preview, faster now
490 0.9 added MMB, added .cut save to [d]ump, fixed save, fixed expose
492 20060105 renamed to xtscut
493 0.8 added MPEG2 Intra render behind histogram + RMB, fixed mem leak
495 0.7 added load and save .cut file, even/odd/all save keys
497 20060106 merge of frames.c and ipb.c codebases
498 0.6 added cut number to bottom of red Intra frame cut line
500 20051227 ipb.c theory works
501 0.5 imported -i ipb cuts from atscut; added LMB mouse cut toggle
503 0.4 added mouse buttons (4, 5) for scrollwheel support
505 0.3 added legend buttons to toggle display of each frame type
508 0.2 show I, P and B frames as colored lines, with times at top
510 0.1 one histogram line per I frame. looks useful already!
514 /* NOTES: GOP broken_link and closed_gop
516 #define USE_GOP_BROKEN_LINK
518 The worst case scenario is user sees junk in video at cut point.
519 Actually, the user saw a lot of junk TV way before the cut point. :>
521 Editors should set Broken GOP flag at cut point to trigger decoder to
522 reset the internal macroblocks to reduce artifacting at cut point.
524 If you cut at fade to black this artifact problem may not appear.
525 It all depends on what (last) next two B frames look like. GOP broken_link
526 set tells decoder to ignore next B frames until a P frame. GOP closed_gop
527 set tells decoder it's OK to use the B frames: one or other will be set.
528 The spec doesn't say 'mutually exclusive' but the practice seems to be.
530 The spec for these two bits seems obtuse, but the general idea is that
531 the GOP broken_link flag is the one that should purge junk frames on cuts.
532 Whether or not various decoders actually honor GOP broken_link is unknown.
533 Hardware decoders may see less bad blocks with GOP broken_link flag set.
540 This code does not set the Discontinuity Flags in the TS Adaptation
541 Field Control, at the cut point, so it may be non-compliant MPEGv2-wise.
542 It does try to set the GOP broken_link bit if closed_gop bit is clear.
543 I may experiment with inserting 1 video packet at the start of each cut
544 that has AFC = 2 and only a Discontinuity Flag as the payload.
546 [ AFC Discontinuity Flag tells the decoder to use the new time, like DVD.
547 Most players seem to handle getting the new time from the stream, so
548 playing with the AFC DF bit is, maybe, not necessary. ]
551 Buttons for the following:
553 Threshold select/clear all cuts/process cuts(write even odd)/display:
554 These may not be needed. Threshold set from mouse input could be useful.
555 *** [Threshold is for long abandoned auto-cutting idea, remove references]
557 Status inside X frame for the following:
558 During writes, file name and current sequence number updated in frame.
559 Showing current cut frame is not useful because it should be blank. DOH!
560 Maybe show 15 frames ahead instead.
563 nramsey requested these:
564 DONE keys for nearest cut to left or right: [a]/[s] keys
566 .tsc file as ascii text, nl delimited for each cut
568 Can X detect shift key press as scroll wheel accelerator?
569 [ Better yet, can another key serve as mouse buttons? ]
575 There used to be a limit on the number of hours of video. That
576 limit has been removed with the new dynamic frame data allocation.
577 The .tsx file generation for a 4 hour capture will take a while. If the
578 atscap -m generated .tsx file is wrong, delete it to build a new one.
580 It may crash on bad streams. Jumping around with the MMB to get part
581 of the stream may help, but it's usually pointless on really bad captures.
582 Other than making libmpeg2/mpeg2dec more robust there is no real solution.
583 If the data isn't there, it isn't there and it's difficult to handle this.
585 Good antennas that will help you receieve better are the easiest fix.
586 Your current antenna may be good enough but only needs better placement.
590 /* makes large files work right on 32 bit machines */
591 /* #define GNULINUX */
593 #define __USE_LARGEFILE64
594 #define __USE_FILE_OFFSET64
595 #define _FILE_OFFSET_BITS 64
598 /* always try to set GOP broken_link */
599 #define USE_GOP_BROKEN_LINK
602 #include <features.h>
604 #ifdef USE_PLATFORM_H
605 #ifndef PLATFORM_NAME
606 #include <platform.h>
610 #ifndef PLATFORM_NAME
611 #define PLATFORM_NAME "Linux"
614 #define FILE_RMODE O_RDONLY
615 #define FILE_WMODE O_RDWR | O_CREAT | O_TRUNC
616 /* umask 022 makes this 0644 */
617 #define FILE_PERMS 0666
619 #define PNG_PATH "/dtv/pg/img/"
621 /* debug the MPEG2 parsing in build_esx/tsx */
628 #include <sys/types.h>
629 #include <sys/stat.h>
634 #include <sys/ioctl.h>
635 #include <inttypes.h>
637 /* #include <math.h> */
639 /* libX* needs these */
640 #include <X11/Xlib.h>
641 #include <X11/Xutil.h>
642 #include <X11/cursorfont.h>
643 #include <X11/keysym.h>
646 /********************************************************** libmpeg2 libvo */
648 #include <inttypes.h>
651 #include <X11/extensions/XShm.h>
652 #include <X11/extensions/Xvlib.h>
653 #define FOURCC_YV12 0x32315659
654 #define FOURCC_UYVY 0x59565955
655 /* since it doesn't seem to be defined on some platforms */
656 int XShmGetEventBase (Display
*);
658 /* slightly modified video_out_x11.c */
659 #include "video_out.h"
660 /********************************************************** libmpeg2 libvo */
661 #endif /* USE_LIBVO */
663 /* both Imlib1/2 need this? */
664 #include <X11/extensions/shape.h>
674 /* libmpeg2 needs these */
675 #include <mpeg2dec/mpeg2.h>
676 #include <mpeg2dec/mpeg2convert.h>
678 /* statfs for volume free space */
680 /****************************************************************** macros */
682 #define xprintf if (0 != arg_xmsg) fprintf
683 #define iprintf if (0 != arg_imsg) fprintf
684 #define dprintf if (0 != arg_dmsg) fprintf
685 #define WHOAMI if (0 != arg_fmsg) fprintf( stdout, "%s\n", __FUNCTION__ )
687 #define WHO (char *) __FUNCTION__
688 #define WHERE (int) __LINE__
690 /* 24hrs at 60fps, maximum page allocation will be around 45mb */
692 #define SCUT_MAX 1000
699 #define MPEG_NULL 0x1FFF
703 #define MPEG_SYN 0x47
704 #define MPEG_PES 0xE0
705 #define MPEG_UPS 0xB2
706 #define MPEG_SEQ 0xB3
707 #define MPEG_EXT 0xB5
708 #define MPEG_GOP 0xB8
709 #define MPEG_PIC 0x00
713 //#define TSBZ (21 * TSZ)
716 /****************************************************************** globals */
718 /* reused for .tsx .tsc, .esx, .esc */
720 static char in_name
[256] = "";
721 static ino_t in_inode
;
723 /* used to save cuts */
725 static char out_name
[256];
726 static char out_base
[256];
727 static ino_t out_inode
;
728 static long long in_size
;
732 long long bytes_total
;
734 static char *fxt
[2] = { "ts", "es" };
735 static char *fxp
= NULL
;
737 static char pictypes
[4] = "?IPB";
739 int pct
; /* picture coding type */
740 int vpn
; /* video packet number */
743 /* one entry for input file transform to larger structure */
744 /* static struct in_frame_s in_frame; */
745 /* static long long in_seq; */
749 /* from input file */
750 unsigned char pct
; /* picture coding type */
751 unsigned char cut
; /* non-zero is a cut point */
752 unsigned char cutnum
; /* build scut_frames computes */
753 unsigned char tcolor
; /* non-zero is frame in odd cut, timecode rv */
754 int vpn
; /* picture start video packet number */
755 int fpc
; /* frame packet count */
756 int num
; /* frame number for this picture type */
759 //typedef long long sequence_t;
760 //typedef int iframe_t;
763 struct timespec start
;
764 struct timespec stop
;
765 struct timespec diff
;
768 struct timer_s timer_gui
;
769 struct timer_s timer_cut
;
770 struct timer_s timer_bif
;
771 struct timer_s timer_fsx
;
773 static frame_t
*frames
;
775 static long long *sequences
;
777 static int frame_idx
;
778 static int iframe_idx
;
779 static int sequence_idx
;
783 /* these are set by sequence parse */
784 static unsigned int frame_rate
= 0;
785 static unsigned int frame_period
= 0;
786 static unsigned int byte_rate
= 0;
787 static unsigned int bit_rate
= 0;
788 static unsigned int null_pkts
= 0;
790 static double hist_scale
= 0.5;
791 //static int hist_hpn = 0;
793 /* each scut entry has index to sequence[] */
794 static int scuts
[ SCUT_MAX
];
795 static int scut_idx
= 0;
796 static int scut_done
= 0;
798 /* 0 no cuts, 1 is all [w], 2 even scuts [e], 3 odd scuts [o] */
799 static int cut_type
= 0;
802 static int nif
= 0; /* number of i frames rendered */
804 char *votypes
[] = { "imlib1 RGB",
812 /* TS header order */
813 unsigned char tei
; /* Transport Error Indicator 1 bit */
814 unsigned char psi
; /* Payload Start Indicator 1 bit */
815 unsigned char pri
; /* Priority (unused) 1 bit */
816 unsigned short pid
; /* Packet ID 13 bits */
817 unsigned char tsc
; /* Transport Scramble Control 2 bits */
818 unsigned char afc
; /* Adaptation Field Control 2 bits */
819 unsigned char cc
; /* Continuity Counter 4 bits */
820 unsigned char afb
; /* Adaptation Field byte count 8 bits */
822 unsigned char pso
; /* computed: TS packet payload start index */
823 unsigned char psb
; /* computed: TS payload segment byte count */
827 /************************************************************ CLI arguments */
829 static int arg_vo
= 1; /* default 0 x11, 1 xv, 2 xv2 */
830 #endif /* USE_LIBVO */
831 static int arg_hl
= 0; /* F1 histogram on or off */
832 static int arg_lb
= 0; /* F2 legend button toggle */
833 static int arg_tc
= 0; /* F3 timecode at top toggle */
834 static int arg_sb
= ~0; /* F4 visual position toggle */
835 static int arg_ec
= ~0; /* F5 eye candy toggle */
836 static int arg_ft
= ~0; /* frame code or time code */
837 static int arg_wd
= 0; /* video window divisor */
838 static int arg_es
= 0; /* 0 is de-packetize input */
839 static int arg_gop
= 0; /* default sets GOP broken_link */
840 static int arg_bfr
= 0; /* benchmark iframe render test len */
841 static int arg_kls
= 0; /* don't discard last sequence */
842 static int arg_mbz
= 4096; /* buffer size */
843 static int arg_jump
= 0; /* unused, start at this frame */
844 static int arg_xmsg
= 0; /* zero to disable x event text */
845 static int arg_imsg
= 0; /* frame IPB scan verbosity */
846 static int arg_dmsg
= 0;
847 static int arg_fmsg
= 0; /* function name flow */
848 static int arg_nulls
= 0; /* nulls at start of cuts? */
849 static int arg_delay
= 0;
850 static unsigned int arg_mx
= MPEG2_ACCEL_DETECT
; /* FPU select for IDCT */
851 static unsigned short arg_vpid
= 0;
852 static unsigned short arg_apid
= 0;
854 static char arg_base
[256] = ""; /* basename */
855 static char arg_path
[256] = "."; /* output path, default is cwd */
856 /* [path]filename on command line */
858 static unsigned char arg_name[256];
863 static int arg_ti
= 265;
864 static int arg_tp
= 64;
865 static int arg_tb
= 64;
868 static char ** arg_v
;
871 static int arg_cut
= CUT_NONE
; /* -cx cut without gui */
872 static int arg_renum
= 0; /* -r renumber cut option */
873 static int arg_one
= ~0; /* -1 clears for multiple cuts */
874 static int arg_alpha
= 0xFF; /* alpha blend opacity, 0 - 255 */
875 static int pkt_vid
= 0;
876 static int pkt_num
= 0;
878 static unsigned int accel
; /* return from mpeg2_accel */
879 char *x86mx
[] = { "NONE", "MMX", "3DNOW", "3DNOW+MMX",
880 "MMX2", "MMX2+MMX", "MMX2+3DNOW", "MMX2+MMX+3DNOW" };
882 static unsigned char *ts_buf
;
883 static unsigned char *es_buf
;
886 //static clockid_t clock_method;
889 static char pat
[188]; /* PAT is always one packet */
890 static char pmt
[188*2]; /* PMT is usually one packet. KQED has two */
891 static int psib
[0x2000]; /* payload start indicators by pid */
892 static short pmt_pid
= 0; /* input scan sets this */
893 static char pmt_pkts
= 0; /* input scan sets this */
898 //static int frame_threshold; /* RMB sets, [a] [s] keys jump forward/back */
901 /******************************************************** X related globals */
903 static int uoffset
= 0; /* user frame offset */
904 static int xoffset
= 0; /* histogram draw offset */
905 static int coffset
= 0; /* current histogram offset */
906 static int toffset
= 0; /* target histogram offset */
907 static int xredraw
= ~0;
908 static int xumove
= ~0; /* user moved position */
909 static int xlfd
= 0; /* last frame displayed */
910 static int xcfd
= 0; /* current frame displayed */
918 /* IPB button areas */
919 static int bxi
; /* button x i */
920 static int bxp
; /* button x p */
921 static int bxb
; /* button x b */
922 static int bys
; /* button y start */
923 static int byh
; /* button y height */
924 static int tys
; /* timecode y start */
926 /* IPB button color/line visible status */
931 static int xinit
= 0; /* X11 initialized */
932 static int m2init
= 0; /* libmpeg2 initialized */
933 char *dispname
= ":0";
934 static Display
*xdisplay
;
935 static Window xwindow
;
936 static int xscreen
; /* X screen from init */
939 static XWindowAttributes xattr
;
940 static XEvent xev
; /* generic event */
941 static unsigned long sattrmask
;
942 static XSetWindowAttributes sattr
;
945 static Visual
*xvisual
;
946 static XVisualInfo vinfo
;
947 static Colormap xcmap
;
948 static int xtbh
= 0; /* X title bar height */
949 static XGCValues xgcv
;
952 static int xbpp
= 0; /* X bits per pixel depth */
953 static unsigned int xfg
, xbg
; /* x foreground and background colors */
954 static XFontStruct
* xfonts
;
955 static XSizeHints xhint
;
956 static char xtitle
[256];
957 //static Cursor fscur;
959 unsigned int *xcolors
;
960 unsigned long expose_serial
; /* serial number of last expose event */
962 #define COLOR_BLACK xcolors[8]
963 #define COLOR_GREY0 xcolors[9]
964 #define COLOR_GREY1 xcolors[0]
965 #define COLOR_WHITE xcolors[7]
966 #define COLOR_CYAN xcolors[6]
967 #define COLOR_MAGENTA0 xcolors[10]
968 #define COLOR_MAGENTA xcolors[2]
969 #define COLOR_YELLOW xcolors[4]
970 #define COLOR_RED xcolors[3]
971 #define COLOR_GREEN0 xcolors[11]
972 #define COLOR_GREEN xcolors[5]
973 #define COLOR_BLUE xcolors[1]
975 /* 16 bit visual primary color index */
976 unsigned int xcolors565
[] = {
977 (0x07 << 11) | (0x0F << 6) | 0x07, /* Bright Grey */
978 (0x00 << 11) | (0x00 << 6) | 0x1F, /* Bright Blue */
979 (0x1F << 11) | (0x00 << 6) | 0x1F, /* Bright Magenta */
980 (0x1F << 11) | (0x00 << 6) | 0x00, /* Bright Red */
981 (0x1F << 11) | (0x3F << 6) | 0x00, /* Bright Yellow */
982 (0x00 << 11) | (0x3F << 6) | 0x00, /* Bright Green */
983 (0x00 << 11) | (0x3F << 6) | 0x1F, /* Bright Cyan */
984 (0x1E << 11) | (0x3E << 6) | 0x1E, /* Bright White */
985 (0x00 << 11) | (0x00 << 6) | 0x00, /* Black */
986 (0x03 << 11) | (0x03 << 6) | 0x03, /* Dark Grey */
987 (0x0F << 11) | (0x00 << 6) | 0x0F, /* Mid Magenta */
988 (0x00 << 11) | (0x1F << 6) | 0x00, /* Mid Green */
991 /* 24/32 bit visual primary color index */
992 unsigned int xcolors888
[] = {
993 (0x80 << 16) | (0x80 << 8) | 0x80, /* Brigt Grey */
994 (0x00 << 16) | (0x00 << 8) | 0xFF, /* Bright Blue */
995 (0xFF << 16) | (0x00 << 8) | 0xFF, /* Bright Magenta */
996 (0xFF << 16) | (0x00 << 8) | 0x00, /* Bright Red */
997 (0xFF << 16) | (0xFF << 8) | 0x00, /* Bright Yellow */
998 (0x00 << 16) | (0xFF << 8) | 0x00, /* Bright Green */
999 (0x00 << 16) | (0xFF << 8) | 0xFF, /* Bright Cyan */
1000 (0xFF << 16) | (0xFF << 8) | 0xFF, /* Bright White */
1001 (0x00 << 16) | (0x00 << 8) | 0x00, /* Black */
1002 (0x40 << 16) | (0x40 << 8) | 0x40, /* Dark Grey */
1003 (0x7F << 16) | (0x00 << 8) | 0x7F, /* Mid Magenta */
1004 (0x00 << 16) | (0x7F << 8) | 0x00, /* Mid Green */
1014 char *xevtypes
[] = {
1060 /* pcf fonts found in /usr/share/fonts/misc */
1062 { 12, 24, "12x24" },
1063 { 10, 20, "10x20" },
1064 { 9, 18, "9x18" }, // 9x18 is broken here
1076 static int xpcf_idx
= 13;
1077 static int xhelp
= 0;
1079 #define MPEG2_STAT_MAX 12
1083 "SEQUENCE_REPEATED",
1096 /* default window size, works equally well for 720p or 1080i w/o artifacts */
1097 static struct vw_s vw
= { 0, 0, 960, 544 };
1100 /* FIXME: maximize is broken */
1101 /* maximize saves previous window size here */
1102 static int xmaxi
= 0;
1103 static struct vw_s vw0
= { 0, 0, 0, 0 };
1106 /* decoded Intra frame that is put in window before histogram drawn */
1108 #warning using Imlib version 2
1109 static Imlib_Image
*image
= NULL
; /* temp mpeg render image */
1113 #warning using Imlib version 1
1114 static ImlibImage
*image
= NULL
;
1115 static ImlibData
*idata
= NULL
;
1117 /************************************************************ X globals END */
1119 /* libmpeg2 structures */
1121 const mpeg2_info_t
* m2inf
;
1122 const mpeg2_sequence_t
* m2seq
;
1123 mpeg2_state_t m2sta
;
1126 struct timespec event_loop_sleep
= { 0, 1 }; /* X event loop sleep */
1127 struct timespec write_sleep
= { 0, 77 }; /* write sleep -d option */
1128 struct timespec splash_sleep
= { 5, 0 }; /* splash sleep */
1129 struct timespec endimg_sleep
= { 0, 500000000 };/* splash sleep */
1131 static unsigned int framew
= 1920;
1132 static unsigned int frameh
= 1088;
1134 static double aspectf
= 0.0;
1137 static unsigned char ckey
; /* last cooked console key */
1140 static unsigned int sc
; /* MPEG2 start code */
1142 static char keyhelp
[4096] = "Key Help";
1143 static char cfghelp
[4096] = "Config Help";
1144 static char cutlist
[8192] = "\n No cuts selected yet! \n\n";
1145 static char outlist
[8192] = "\n Nothing written yet! \n\n";
1147 /************************************************************ GLOBALS END */
1150 /*********************************************************** functions BEGIN */
1152 /************************************************************* prototypes */
1153 static void x_event( void );
1154 static void c_exit( int );
1155 /************************************************************* prototypes */
1159 static char date_now
[64];
1168 tnow
= time( NULL
);
1169 localtime_r( &tnow
, &tloc
);
1170 asctime_r( &tloc
, date_now
);
1174 /* write configuration to a text file */
1177 save_config ( void )
1180 char *n
= "/etc/atscap/xtscut.conf";
1188 fprintf(stdout
, "%s could not open %s\n", WHO
, n
);
1195 fprintf(f
, "# "NAME
"-"VERSION
"-"LASTEDIT
" configuration saved on %s",
1198 fprintf(f
, "# Binary options, 0=off/1=on\n");
1199 fprintf(f
, "H%d\t\t# Histogram lines are %svisible\n",
1200 1 & arg_hl
, (arg_hl
)? t1
:t0
);
1202 fprintf(f
, "L%d\t\t# IPB Buttons are %svisible\n",
1203 1 & arg_lb
, (arg_lb
)? t1
:t0
);
1205 fprintf(f
, "T%d\t\t# Timecodes are %svisible\n",
1206 1 & arg_tc
, (arg_tc
)? t1
:t0
);
1208 fprintf(f
, "E%d\t\t# Eye-Candy is %srendered\n",
1209 1 & arg_ec
, (arg_tc
)? t1
:t0
);
1211 fprintf(f
, "F%d\t\t# Timecodes are Intra frame %s\n",
1212 1 & arg_ft
, (arg_ft
) ? "mmm:ss":"counts");
1214 fprintf(f
, "S%d\t\t# Slider bead is %svisible\n",
1215 1 & arg_sb
, (arg_sb
)? t1
:t0
);
1217 fprintf(f
, "G%d\t\t# GOP broken_link will %sbe set\n",
1218 1 & arg_gop
, (arg_gop
)? t1
:t0
);
1220 fprintf(f
, "N%d\t\t# NULL packets will %sbe written\n",
1221 1 & arg_nulls
, (arg_nulls
)? t1
:t0
);
1223 fprintf(f
, "D%d\t\t# Writes will %shave delays\n",
1224 1 & arg_delay
, (arg_delay
)? t1
:t0
);
1226 fprintf(f
, "K%d\t\t# Last Sequence %s kept\n",
1227 1 & arg_kls
, (arg_kls
)? "always":"may not be");
1229 fprintf(f
, "R%d\t\t# Cuts will %sbe renumbered\n",
1230 1 & arg_renum
, (arg_renum
)? t1
:t0
);
1235 fprintf(f
, "I%d\t\t# Intra frame lines are %s\n",
1236 1 & bis
, (bis
)? t1
:t0
);
1238 fprintf(f
, "P%d\t\t# Predictor frame lines are %s\n",
1239 1 & bps
, (bps
)? t1
:t0
);
1241 fprintf(f
, "B%d\t\t# Bidi frame lines are %s\n",
1242 1 & bbs
, (bbs
)? t1
:t0
);
1244 fprintf(f
, "M%d\t\t# Cuts will go to %s file%s\n",
1245 1 & arg_one
, (arg_one
)?"one":"multiple", (arg_one
)?"":"s");
1247 fprintf(f
, "# Non-binary options\n");
1249 fprintf(f
, "A0x%08X\t# libmpeg2 acceleration bits (0x%X autodetect)\n",
1250 arg_mx
, MPEG2_ACCEL_DETECT
);
1252 fprintf(f
, "O%s\t\t# Output path\n", arg_path
);
1254 fprintf(f
, "Q%f\t# Histogram scale, float 0.0 to 1.0\n", hist_scale
);
1256 // fprintf(f, "U0x%02X\t\t# Histogram alpha blend opacity\n", arg_alpha);
1257 // fprintf(f, "J%d\t\t# Jump to last Intra displayed\n", xcfd);
1259 fprintf(f
, "W%d\t\t# Video size divisor, or 0 default\n", arg_wd
);
1261 fprintf(f
, "X%d\t\t# Video X axis dimension\n", vw
.w
);
1263 fprintf(f
, "Y%d\t\t# Video Y axis dimension\n", vw
.h
);
1265 fprintf(f
, "Z%d\t\t# Buffer size\n", arg_mbz
);
1281 char *n
= "/etc/atscap/xtscut.conf";
1287 if (NULL
== f
) return;
1289 memset(b
, 0, sizeof(b
));
1292 r
= fgets( b
, sizeof(b
), f
);
1293 if (NULL
== r
) break;
1296 /* strip NL, TAB, comment and space */
1297 p
= strchr(r
, '\n');
1298 if (NULL
!= p
) *p
= 0;
1300 p
= strchr(r
, '\t');
1301 if (NULL
!= p
) *p
= 0;
1304 if (NULL
!= p
) *p
= 0;
1307 if (NULL
!= p
) *p
= 0;
1309 if (0 == *r
) continue;
1316 /* 32 bits, top bit is auto-detect, rest of bits are force certain FPU type */
1319 sscanf( p
, "%08X", &arg_mx
);
1326 case 'C': /* unused */
1330 arg_delay
= 1 & atoi(p
);
1333 /* eye candy toggle, enabled uses more CPU, slows navigation a little bit */
1335 arg_ec
= 1 & atoi(p
);
1339 arg_ft
= 1 & atoi(p
);
1343 arg_gop
= 1 & atoi(p
);
1347 arg_hl
= 1 & atoi(p
);
1354 case 'J': /* broken */
1359 arg_kls
= 1 & atoi(p
);
1363 arg_lb
= 1 & atoi(p
);
1367 arg_one
= 1 & atoi(p
);
1371 arg_nulls
= 1 & atoi(p
);
1377 memset(arg_path
, 0, sizeof(arg_path
));
1378 strncpy(arg_path
, p
, sizeof(arg_path
)-1);
1379 arg_path
[ sizeof(arg_path
)-1 ] = 0;
1388 hist_scale
= atof(p
);
1392 arg_renum
= 1 & atoi(p
);
1396 arg_sb
= 1 & atoi(p
);
1400 arg_tc
= 1 & atoi(p
);
1403 /* Alpha blend is currently not implemented. It may use too much CPU. */
1405 arg_alpha
= 0xFF & atoi(p
);
1408 case 'V': /* unused */
1412 arg_wd
= 15 & atoi(p
);
1413 if (0 == arg_wd
) { vw
.w
= 960; vw
.h
= 544; }
1430 /* no divisor unless it's 1, 2, 4, or 8 */
1434 && (8 != arg_wd
) ) arg_wd
= 0;
1436 /* if either width or height are incorrect, reset to default video size */
1437 if ((vw
.w
< 160) || (vw
.w
> 1920)) {
1442 if ((vw
.h
< 90) || (vw
.h
> 1088)) {
1449 /* dump config to stdout in human readable form */
1452 show_config ( void )
1464 fprintf(f
, "\nCURRENT CONFIGURATION:\n");
1465 fprintf(f
, "\tlibmpeg2 acceleration bits 0x%08X\n\n", arg_mx
);
1466 fprintf(f
, "\tIPB Histogram is %svisible,", (arg_hl
)? t1
:t0
);
1467 fprintf(f
, " Scale is %f\n", hist_scale
);
1468 fprintf(f
, "\tIPB Histogram buttons are %svisible\n",
1470 fprintf(f
, "\t\tI frames %s,", (bis
)? s1
:s0
);
1471 fprintf(f
, " P frames %s,", (bps
)? s1
:s0
);
1472 fprintf(f
, " B frames %s\n\n", (bbs
)? s1
:s0
);
1474 fprintf(f
, "\tCuts go to output path %s\n", arg_path
);
1475 fprintf(f
, "\t\tCuts to %s file%s,",
1476 (arg_one
)?"single":"multiple", (arg_one
)?"":"s");
1478 fprintf(f
, " Renumbering %s,", (arg_renum
)? s1
:s0
);
1479 fprintf(f
, " Last Sequence %s,\n", (arg_kls
)? s1
:s0
);
1480 fprintf(f
, "\t\tGOP broken_link %s,", (arg_gop
)? s1
:s0
);
1481 fprintf(f
, " NULL packets %s,", (arg_nulls
)? s1
:s0
);
1482 fprintf(f
, " Delays %s\n", (arg_delay
)? s1
:s0
);
1483 fprintf(f
, "\t\tBuffer size %d\n\n", arg_mbz
);
1485 fprintf(f
, "\tVideo dimensions: Source %dx%d Display %dx%d",
1486 framew
, frameh
, vw
.w
, vw
.h
);
1488 fprintf(f
, ", Divisor %d\n", arg_wd
);
1490 fprintf(stdout
, "\n");
1491 fprintf(f
, "\tSlider bead is %svisible\n\n", (arg_sb
)? t1
:t0
);
1492 fprintf(f
, "\tTimecodes are %svisible,", (arg_tc
)? t1
:t0
);
1493 fprintf(f
, " Format %s\n",
1494 (arg_ft
) ? "mmm:ss":"frames");
1501 build_keyhelp ( char *d
, int z
, int s
)
1512 " ? This help text ESC Refresh display\n"
1513 " F1 Toggles hide IPB histogram F2 Toggles hide IPB buttons\n"
1514 " F3 Toggles hide timecodes at top F4 Toggles hide slider bead\n"
1515 " [+] Increase histogram scale [-] Decrease histogram scale\n"
1517 " m Toggle single/multiple cuts d Display cut list\n"
1518 " n Toggle NULLs packets c Clear all cuts\n"
1519 " t Toggle timecode format w Write all cuts\n"
1520 " g Toggle GOP broken_link e Write even cuts\n"
1521 " q Quit r Write odd cuts\n"
1523 "KEY NAVIGATION: W is window width in frames (same as pixel width)\n"
1525 " y or [PageUp] backward 2W o or [PageDown] forward 2W\n"
1526 " u or [Up] backward 1W i or [Down] forward 1W\n"
1527 " j or [Left] back 1 Sequence k or [Right] forward 1 Sequence\n"
1528 " [ or [Home] jump to start ] or [End] jump to end\n"
1529 " a jump to previous cut s jump to next cut\n"
1533 " LMB Left Button selects/deselects a cut or IPB legend line\n"
1534 " RMB Right Button previews the Intra-frame under the mouse\n"
1535 " MMB Middle button jumps to stream position (invisible slider)\n"
1536 " SCROLL Scroll Wheel navigates backward or forward by 1/2 W.\n"
1537 "%s", t
, t
, t
, t
, t
);
1544 build_cfghelp ( char *d
, int z
, int s
)
1550 // char *s0 = "OFF";
1562 /* set the output path but truncate it after first 40 chars */
1563 memset(path
, 0, sizeof(path
));
1566 ok
= chdir(arg_path
);
1573 pok
= getcwd(path
, sizeof(path
));
1574 if (pok
== NULL
) *path
= 0;
1575 if (0 != *path
) path
[40] = 0;
1577 asnprintf(d
, z
, "\n");
1578 asnprintf(d
, z
, " CURRENT CONFIGURATION:\n");
1580 if (0x80000000 != arg_mx
) at
= x86mx
[7 & arg_mx
];
1583 dt
= votypes
[2 + arg_vo
];
1591 asnprintf(d
, z
, " libmpeg2 acceleration bits %s \n", at
);
1592 asnprintf(d
, z
, " Video Source %dx%d Display %dx%d \n",
1593 framew
, frameh
, vw
.w
, vw
.h
);
1594 asnprintf(d
, z
, " Render Video with %s", dt
);
1596 asnprintf(d
, z
, ", Divisor %d ", arg_wd
);
1597 asnprintf(d
, z
, "\n\n");
1599 asnprintf(d
, z
, " IPB Histogram is %svisible", (arg_hl
)? t1
:t0
);
1600 if (arg_hl
) asnprintf(d
, z
, ", Scale is %.0f%% ", hist_scale
* 100.0);
1601 asnprintf(d
, z
, "\n");
1603 asnprintf(d
, z
, " IPB Buttons are %svisible",
1606 asnprintf(d
, z
, ": I %s, ", (bis
)? y1
:y0
);
1607 asnprintf(d
, z
, "P %s, ", (bps
)? y1
:y0
);
1608 asnprintf(d
, z
, "B %s ", (bbs
)? y1
:y0
);
1610 asnprintf(d
, z
, "\n");
1612 asnprintf(d
, z
, " Cut%s to: ", (arg_one
)?"":"s");
1613 // if (0 != *arg_path) astrncpy(path, arg_path, sizeof(path));
1614 asnprintf(d
, z
, "%s \n", path
);
1616 asnprintf(d
, z
, " Renumber cuts %s", (arg_renum
)? y1
:y0
);
1617 asnprintf(d
, z
, " Delay %s\n", (arg_delay
)? y1
:y0
);
1618 asnprintf(d
, z
, " Buffer size %d", arg_mbz
);
1619 asnprintf(d
, z
, " Keep Cruft %s\n", (arg_kls
)? y1
:y0
);
1621 asnprintf(d
, z
, " GOP broken %s", (arg_gop
)? y1
:y0
);
1622 asnprintf(d
, z
, " Keep NULLs %s\n", (arg_nulls
)? y1
:y0
);
1624 asnprintf(d
, z
, "\n");
1626 asnprintf(d
, z
, " Slider bead is %svisible\n", (arg_sb
)? t1
:t0
);
1627 asnprintf(d
, z
, " Timecodes are %svisible", (arg_tc
)? t1
:t0
);
1628 if (arg_tc
) asnprintf(d
, z
,
1629 ", Format %s", (arg_ft
) ? "mmm:ss":"frames");
1630 asnprintf(d
, z
, "\n");
1631 asnprintf(d
, z
, "\n");
1635 /* build scut[] from frames[].cut, adding cut index to frames[].cutnum */
1638 build_scut_frames ( void )
1645 memset( scuts
, 0, sizeof(scuts
));
1647 scuts
[scut_idx
++] = 0; /* first is sequence 0 */
1651 for (i
= 0; i
< frame_idx
; i
++) {
1656 /* Intra frame type */
1659 /* reset in case f->cut changed */
1664 "Cut %02d Frame %6d Intra %6d Byte %11llX Color %d\n",
1665 scut_idx
, i
, f
->num
, sequences
[f
->num
], f
->tcolor
1668 f
->cutnum
= scut_idx
;
1669 scuts
[scut_idx
] = f
->num
;
1678 /* Compute the number of nulls to use at the start of each cut. */
1679 /* NULLs are 1/8th packet rate, in case broadcaster screws up and
1680 sends a bogus stream bitrate of 80Mbits/sec. This will limit
1681 the maximum size of NULL packet pre-amble to about 1.25Mbytes.
1683 NOTE: mplayer is easily confused by a lot of initial NULL packets.
1684 If you use mplayer, you should not use the -n option.
1686 TODO: Put the above note into the man page for future reference.
1690 calc_null_packets ( void )
1695 if (0 == arg_nulls
) return;
1696 null_pkts
= byte_rate
/ TSZ
;
1697 null_pkts
/= 8; /* fewer nulls, about 1/8th of a second */
1701 /* i is frame index, divide by frame rate to determine the time code */
1704 build_frame_timecode ( char *d
, int z
, int i
)
1714 asnprintf(d
, z
, " %03d:%02d", m
, s
);
1721 build_cutlist( char *d
, int z
)
1723 int i
, fce
, fco
, fca
;
1724 long long cpt
, cat
, cet
, cot
, vbf
;
1728 // FIXME: disabled for debug, but enable for output redux on cl-usage
1729 // if (0 != arg_cut) return;
1733 memset( cutlist
, 0, sizeof(cutlist
));
1735 /* preamble byte count is PAT + PMT + optional NULLs */
1736 calc_null_packets();
1737 cpt
= TSZ
* (1 + pmt_pkts
);
1738 cpt
+= TSZ
* null_pkts
;
1740 fce
= fco
= fca
= 0;
1741 cet
= cot
= cat
= 0;
1743 build_scut_frames();
1745 /* if it only shows this one, this trick is ok */
1746 scuts
[scut_idx
] = sequence_idx
-1;
1748 asnprintf(d
, z
, "Cut # SEQ # byte offset file size start runtime frames %%\n");
1749 asnprintf(d
, z
, "----- ------ ---------------- ---------------- ------- ------- ------ -----\n");
1752 asnprintf(d
, z
, " none none %16lld %16lld PAT+PMT%s pre-amble\n",
1753 0LL, cpt
, (0 != arg_nulls
) ?"+NULL":"");
1755 asnprintf(d
, z
, "Elementary Stream write has no pre-amble\n");
1757 for (i
= 0 ; i
< scut_idx
-1; i
++) {
1761 /* don't need last I frame plus junk */
1762 if (0 == scuts
[i
+1]) break;
1764 /* byte start of cut and byte count of cut */
1765 cs
= sequences
[ scuts
[ i
] ];
1766 cz
= sequences
[ scuts
[ i
+1 ] ] - cs
;
1768 /* frame start of cut and frame count of cut */
1769 fcs
= iframes
[ scuts
[ i
] ];
1770 fcz
= iframes
[ scuts
[ i
+1 ] ] - fcs
;
1777 /* off by one, list starts with cut 1 */
1779 /* cut even total */
1790 lltoasc( anum
, cz
+ cpt
);
1792 /* cut 001 to last cut: cut size includes pre-amble for each cut */
1793 asnprintf(d
, z
, "%3d @ %6d ", i
+1, scuts
[i
]);
1794 lltoasc( anum
, cs
);
1795 asnprintf(d
, z
, "%16s ", anum
);
1796 lltoasc( anum
, cz
+ cpt
);
1797 asnprintf(d
, z
, "%16s ", anum
);
1799 build_frame_timecode(d
, z
, fcs
);
1800 asnprintf(d
, z
, " ");
1801 build_frame_timecode(d
, z
, fcz
);
1802 asnprintf(d
, z
, "%7d ", fcz
);
1803 asnprintf(d
, z
, " %3lld%%", (cz
* 100) / in_size
);
1804 asnprintf(d
, z
, "\n");
1808 /* data after last cut not displayed because it's irrelevant */
1809 asnprintf(d
, z
, "%3d @ %6d %11lld %11lld End cruft\n",
1812 sequences
[ sequence_idx
- 1 ],
1813 in_size
- sequences
[ scuts
[ scut_idx
- 1 ]]
1817 statfs( arg_path
, &fs
);
1818 vbf
= fs
.f_bsize
* fs
.f_bavail
;
1819 asnprintf(d
, z
, "Free space %11lld bytes in volume %s:\n",
1822 asnprintf(d
, z
, " EVEN cut time");
1823 build_frame_timecode(d
, z
, fce
);
1824 asnprintf(d
, z
, ", bytes %11lld, ", cet
);
1825 asnprintf(d
, z
, "frames %6d (%2d%%), will %sfit.\n",
1826 fce
, (100 * fce
) / frame_idx
, (cet
< vbf
) ?"":"NOT " );
1828 asnprintf(d
, z
, " ODD cut time");
1829 build_frame_timecode(d
, z
, fco
);
1830 asnprintf(d
, z
, ", bytes %11lld, ", cot
);
1831 asnprintf(d
, z
, "frames %6d (%2d%%), will %sfit.\n",
1832 fco
, (100 * fco
) / frame_idx
, (cot
< vbf
) ?"":"NOT " );
1834 asnprintf(d
, z
, " ALL cut time");
1835 build_frame_timecode(d
, z
, fca
);
1836 asnprintf(d
, z
, ", bytes %11lld, ", cat
);
1837 asnprintf(d
, z
, "frames %6d (%2d%%), will %sfit.\n",
1838 fca
, (100 * fca
) / frame_idx
, (cat
< vbf
) ?"":"NOT " );
1840 asnprintf(d
, z
, "\n" );
1848 build_keyhelp( keyhelp
, sizeof(keyhelp
), s
);
1849 fprintf( stdout
, "%s", keyhelp
);
1852 /* TODO: needs control c handler to give it up properly */
1855 free_frames ( void )
1859 sequences
= ifree(sequences
, "sequences");
1860 iframes
= ifree(iframes
, "iframes");
1861 frames
= ifree(frames
, "frames");
1866 // error: dereferencing pointer to incomplete type
1869 free_mpeg2 ( mpeg2dec_t
* mpeg2dec
)
1871 if (NULL
== mpeg2dec
) return;
1873 if (mpeg2dec
->sequence
->width
!= (unsigned)-1) {
1876 mpeg2dec
->sequence
.width
= (unsigned)-1;
1877 if (!mpeg2dec
->custom_fbuf
)
1878 for (i
= mpeg2dec
->alloc_index_user
;
1879 i
< mpeg2dec
->alloc_index
; i
++) {
1880 mpeg2_free (mpeg2dec
->fbuf_alloc
[i
].fbuf
.buf
[0]);
1881 mpeg2_free (mpeg2dec
->fbuf_alloc
[i
].fbuf
.buf
[1]);
1882 mpeg2_free (mpeg2dec
->fbuf_alloc
[i
].fbuf
.buf
[2]);
1884 if (mpeg2dec
->convert_start
)
1885 for (i
= 0; i
< 3; i
++) {
1886 mpeg2_free (mpeg2dec
->yuv_buf
[i
][0]);
1887 mpeg2_free (mpeg2dec
->yuv_buf
[i
][1]);
1888 mpeg2_free (mpeg2dec
->yuv_buf
[i
][2]);
1890 if (mpeg2dec
->decoder
.convert_id
)
1891 mpeg2_free (mpeg2dec
->decoder
.convert_id
);
1897 /* alloc frames, iframes and sequences arrays, one item until expanded */
1900 alloc_frames ( void )
1904 /* frame_s, 2x int */
1912 /* frames frame_t */
1913 snprintf(n
, sizeof(n
), "%s[%d]", "frames", 0 );
1914 frames
= icalloc( 2, sizeof(frame_t
), n
);
1915 if (NULL
== frames
) {
1916 fprintf(stdout
, "%s failed *frames alloc\n", WHO
);
1921 snprintf(n
, sizeof(n
), "%s[%d]", "iframes", 0 );
1922 iframes
= icalloc( 2, sizeof(int), n
);
1923 if (NULL
== iframes
) {
1924 fprintf(stdout
, "%s failed *iframes alloc\n", WHO
);
1928 /* sequences long long */
1929 snprintf(n
, sizeof(n
), "%s[%d]", "sequences", 0 );
1930 sequences
= icalloc( 2, sizeof(long long), n
);
1931 if (NULL
== sequences
) {
1932 fprintf(stdout
, "%s failed *sequences alloc\n", WHO
);
1940 alloc_streams( void )
1942 es_buf
= icalloc( 1, arg_mbz
+ TSZ
, "ES buffer" );
1943 if (NULL
== es_buf
) {
1944 fprintf(stdout
, "%s failed es_buf alloc(%d)\n",
1945 WHO
, arg_mbz
+ TSZ
);
1949 ts_buf
= icalloc( 1, arg_mbz
+ TSZ
, "TS buffer" );
1950 if (NULL
== es_buf
) {
1951 fprintf(stdout
, "%s failed es_buf alloc(%d)\n",
1952 WHO
, arg_mbz
+ TSZ
);
1960 clear_arrays ( void )
1965 /* FIXME: sizeof doesn't work on pointers, need to use data types */
1966 memset( frames
, 0, sizeof( frames
));
1967 memset( iframes
, 0, sizeof( iframes
));
1968 memset( sequences
, 0, sizeof( sequences
));
1971 memset( scuts
, 0, sizeof(scuts
));
1980 #include "xtc_signals.c"
1984 /*********************************************************** mpeg2dec libvo */
1985 #warning using libvo for XShm and XVideo
1986 #include "xtc_libvo.c"
1987 vo_instance_t
* xoutput
;
1988 vo_setup_result_t xsetup
;
1989 /******************************************************* mpeg2dec libvo END */
1990 #endif /* USE_LIBVO */
1998 es_buf
= ifree(es_buf
, "ES buffer");
1999 ts_buf
= ifree(ts_buf
, "TS buffer");
2005 /* 1920/1088 = x/y, same as 1088/1920 = y/x */
2006 /* y is returned after calc from width x */
2009 calc_aspect_h ( unsigned int w
)
2016 if ( (0 == w
) || (0 == framew
) || (0 == frameh
) ) return 0;
2024 /* 1920/1088 = x/y, same as 1088/1920 = y/x */
2025 /* y is returned after calc from width x */
2028 calc_aspect_w ( unsigned int h
)
2035 if ( (0 == h
) || (0 == framew
) || (0 == frameh
) ) return 0;
2046 /* build a NULL packet filled with zeros at poiner p */
2049 build_null_packet ( char *p
)
2053 memset( p
, 0, TSZ
);
2066 dump_packet ( long long o
, unsigned char *p
, int z
, int e
)
2073 for (i
= 0; i
< z
; i
++) {
2074 if (0 == (15 & i
)) fprintf( stdout
, "%9llX: ", o
+ i
);
2076 if (e
== i
) b
= "*";
2077 fprintf(stdout
, "%s%02X ", b
, p
[i
]);
2078 if (15 == (15 & i
)) fprintf( stdout
, "\n");
2080 fprintf( stdout
, "\n\n");
2086 /* Copy from libmpeg2 parsed intra frame buffer to imlib buffer.
2087 The image is not freed so that x draw_image may reuse it.
2091 update_iframe ( int w
, int h
, unsigned char *b
)
2095 if (0 == xinit
) return;
2099 /* free old img before creating new */
2100 if (NULL
!= image
) imlib_free_image();
2101 image
= imlib_create_image_using_data( w
, h
, (DATA32
*) b
);
2102 if (NULL
!= image
) imlib_context_set_image( image
);
2106 /* free old img before creating new */
2107 if (NULL
!= image
) Imlib_kill_image( idata
, image
);
2108 image
= Imlib_create_image_from_data( idata
, b
, NULL
, w
, h
);
2112 #endif /* ~USE_LIBVO */
2114 /* Parses Transport Stream header and store result in tsh_t
2117 p start of 188 byte packet, *p should be 0x47 or sync is lost
2118 h tsh_t holds the parsed flags
2119 pid 0 returns all ES, nz returns only ES found in PID
2122 -1 if transport sync lost, *p isn't 0x47
2123 0 if no ES data in PID
2124 >0 ES byte count starting at packet offset h->pso
2128 input scan, called with 0 for pid
2129 build tsx, called with arg_vpid found by input scan
2130 parse video es, called with arg_vpid found by input scan
2132 Future ES-only write function could use this, if ES need arises.
2133 Future resync code could use to find new sync point, if needed.
2137 parse_ts_header( unsigned char *p
, tsh_t
*h
, unsigned short pid
)
2139 unsigned char *r
= NULL
;
2151 if (MPEG_SYN
!= *r
) { /* sync lost? */
2152 /* TODO: needs a way to resync */
2157 h
->tei
= 1 & (*r
>>7); /* transport error nz */
2159 h
->psi
= 1 & (*r
>>6); /* payload start 1 */
2160 h
->pri
= 1 & (*r
>>5); /* priority ignored */
2162 h
->pid
= 0x1F00 & (*r
<<8); /* top half */
2164 h
->pid
|= *r
; /* bottom half */
2166 r
++; /* TSC/AFC/CC */
2167 h
->tsc
= 3 & (*r
>>6); /* scrambled error nz */
2169 h
->afc
= 3 & (*r
>>4); /* adaptation field control */
2170 h
->cc
= 0x0F & *r
; /* continuity counter */
2171 r
++; /* AFC count or video */
2175 /* Payload[0] after header is for Adaptation Field Control size if afc > 1 */
2179 z
= ESZ
; /* no AFC has 184 bytes ES */
2180 h
->pso
= 0xFF & (r
- p
);
2182 if (2 == h
->afc
) z
= 0; /* AFC len, no ES payload */
2184 if (3 == h
->afc
) { /* w/AFC, ES is <= 182 bytes */
2185 h
->afb
= *r
; /* AFC len */
2187 r
+= h
->afb
; /* skip AFC count + data */
2188 z
= (ESZ
- 1) - h
->afb
; /* subtract AFC len+data */
2189 h
->pso
= 0xFF & (r
- p
);
2195 if (0 != h
->tei
) return 0;
2196 if (0 != pid
) /* zero PID doesn't */
2197 if (pid
!= h
->pid
) /* check for Video PID */
2200 if (0 != h
->tsc
) return 0;
2206 /* Parse transport stream to MPEG2 Elementary Stream, without AFC data.
2207 This is modified by arg_es global to not attempt to de-packetize it,
2208 but instead read the data as Video Elementary Stream.
2213 parse_video_es ( void )
2215 unsigned char *r
, *p
, *d
;
2223 /* -y or .es file specified only uses simple Elementary Stream data */
2225 z
= read( in_file
, es_buf
, arg_mbz
);
2227 if (z
< 1) fprintf( stdout
, "%s ES EOF %s\n", WHO
, in_name
);
2231 /* TS requires some extraction of the packet data to the ES buffer */
2232 z
= read( in_file
, ts_buf
, arg_mbz
);
2233 if (z
< 1) { /* EOF or error? */
2234 fprintf( stdout
, "%s TS EOF %s\n", WHO
, in_name
);
2235 return z
; /* return what was read */
2238 /* es_buf will always have enough for extracted ES from same size ts_buf */
2243 for (i
= 0; i
< arg_mbz
; i
+= TSZ
) {
2246 z
= parse_ts_header( r
, h
, arg_vpid
);
2248 /* fatal abort on sync lost */
2251 "\nFATAL: %s Transport Stream Sync lost.\n\n", WHO
);
2258 "%s PID %04X PSI%d AFC%d i %4d es[%4d] afb %3d z %3d ts[%4d)\n",
2259 WHO
, h
->pid
, h
->psi
, h
->afc
, i
, c
, h
->afb
, z
, r
- p
);
2262 if (0 == z
) continue;
2263 if (h
->pid
== MPEG_NULL
) continue;
2264 if (h
->pid
!= arg_vpid
) continue;
2266 pkt_vid
++; /* skip non video ES PIDs */
2268 memcpy(&d
[c
], r
, z
);
2270 /* counter should never exceed mbz */
2271 c
+= z
; /* bump the byte counter */
2281 x_draw_line ( unsigned int fgc
, int xc1
, int yc1
, int xc2
, int yc2
)
2283 if (0 == xinit
) return;
2285 // if (0 != arg_xmsg) WHOAMI;
2288 //#define USE_IMLIB2_DRAW
2289 #ifdef USE_IMLIB2_DRAW
2290 /* FIXME: doesn't take into account 16 bit colors, only 24 bit.
2291 Also, the histogram displays wrong. Disabled until debugged.
2296 c1
= 0xFF & (fgc
>> 16);
2297 c2
= 0xFF & (fgc
>> 8);
2300 imlib_context_set_color( arg_alpha
, c1
, c2
, c3
);
2301 imlib_image_draw_line( xc1
, yc1
, xc2
, yc2
, 0 );
2304 XSetForeground( xdisplay
, xgc
, fgc
);
2305 XDrawLine( xdisplay
, xwindow
, xgc
, xc1
, yc1
, xc2
, yc2
);
2309 XSetForeground( xdisplay
, xgc
, fgc
);
2310 XDrawLine( xdisplay
, xwindow
, xgc
, xc1
, yc1
, xc2
, yc2
);
2320 x_clear_display ( void )
2322 if (0 == xinit
) return;
2327 if (0 != arg_xmsg
) WHOAMI
;
2329 XClearWindow( xdisplay
, xwindow
);
2333 /* Draw a filled quadrangle, many examples, buttons, backgrounds, etc.
2334 The biggest visual problem these suffer is they're flat-color quads.
2338 x_draw_quad_fill ( unsigned int fgc
, int x
, int y
, int w
, int h
)
2340 if (0 == xinit
) return;
2342 // if (0 != arg_xmsg) WHOAMI;
2344 XSetForeground( xdisplay
, xgc
, fgc
);
2345 XFillRectangle( xdisplay
, xwindow
, xgc
, x
, y
, w
, h
);
2348 /* Draw a quadrangle, i.e. de-selected button border, or help text border. */
2351 x_draw_quad_nofill ( unsigned int fgc
, int x
, int y
, int w
, int h
)
2353 if (0 == xinit
) return;
2355 // if (0 != arg_xmsg) WHOAMI;
2357 XSetForeground( xdisplay
, xgc
, fgc
);
2358 XDrawRectangle( xdisplay
, xwindow
, xgc
, x
, y
, w
, h
);
2361 /* Draw a vertical gradient filled quad. g float gradient for line color.
2362 g positive starts at black and works way up to color fgc
2363 g negative starts at color fbc and works way down to black
2364 g is limited to the real numbers between -1.0 and 1.0, inclusive
2369 x_draw_quad_gradientv (
2370 unsigned int fgc
, int x
, int y
, int w
, int h
, float e
)
2372 int i
, k
, mr
, mg
, mb
, r
, r1
, g
, g1
, b
, b1
, x1
, w1
;
2374 float j
, f
, rf
, gf
, bf
;
2376 int cx
[8] = { 8, 6, 4, 3, 2, 2, 1, 1 };
2378 if (0 == xinit
) return;
2380 x_draw_quad_fill( COLOR_BLACK
, x
, y
, w
, h
);
2383 // if (0 != arg_xmsg) WHOAMI;
2385 /* ignore bogus text box limit */
2389 /* ignore bogus gradients */
2390 if (e
< -1.0) return;
2391 if (e
> +1.0) return;
2393 /* divide gradient by number of rows for gradient step */
2396 mr
= mg
= mb
= 1 << (xbpp
/3);
2397 /* FIXME: need to know if 5 bit or 6 bit green for max green value */
2398 // mg = 1 << xgreenbits;
2400 r
= (0xFF & (fgc
>> 16));
2401 g
= (0xFF & (fgc
>> 8));
2404 // fprintf( stdout, "gradient step j %f\n", j);
2405 for (i
= 0; i
< h
; i
++) {
2407 if (f
< 0.0) f
= 1.0 + f
;
2409 /* color change per color channel */
2417 /* clamp color excursion to maximum color limit */
2418 if (r1
> mr
) r1
= mr
;
2419 if (g1
> mg
) g1
= mg
;
2420 if (b1
> mb
) b1
= mb
;
2421 c
= (r1
<< 16) | (g1
<< 8) | b1
;
2423 // fprintf(stdout, "i %d fgc %08X f %.2f c %08X\n",i, fgc, f, c);
2424 // x_draw_line( fc, x, i, x + w, i );
2426 XSetForeground( xdisplay
, xgc
, c
);
2428 /* if top or bottom 8 lines, change x1 w1 to give rounded corners */
2431 if ( (i
< 8) || (i
> (h
-9)) ) {
2440 // fprintf( stdout, "i%4d k %d x'%4d w'%4d\n", i, k, x1 - x, w - w1 );
2443 XDrawLine( xdisplay
, xwindow
, xgc
, x1
, y
+ i
, x1
+ w1
, y
+ i
);
2448 Open ts file, seek to offset and start parsing until Intra-frame arrives,
2449 then parsed intra is sent to imlib for display processing. The file is
2450 left open so the next seek shouldn't be impacted by file open delay.
2452 This is drawn before the histogram when redraw is called. The frame rate
2453 and scale factor for histogram are set before time-codes and lines drawn.
2455 Histogram scale factor is bit rate / 5 megabit to give 1-16 for 5-80 Mbit.
2456 This should draw the lines short enough to not take up the whole frame.
2460 parse_intra_frame ( long long offset
, char *caller
)
2464 // uint8_t m2buf[ ESZ ];
2466 if (NULL
== m2dec
) {
2467 fprintf(stdout
, "%s MPEG2 decoder not initialized.\n", WHO
);
2474 iprintf(stdout
, "%s %s offset %09llX\n", WHO
, caller
, offset
);
2476 /* last (fake) sequence can't do preview but can be selected for cut. */
2478 if (offset
>= sequences
[ sequence_idx
-1 ]) {
2480 /* image is not freed after load so x draw_image can do render */
2483 /* Xvideo make the screen flash white momentarily when at 'The End' */
2484 x_draw_quad_fill( COLOR_RED
, 0, 0, vw
.w
, vw
.h
);
2488 /* free old img before creating new */
2489 if (NULL
!= image
) imlib_free_image();
2490 image
= imlib_load_image( PNG_PATH
"xtscut-end.png");
2491 if (NULL
!= image
) imlib_context_set_image( image
);
2495 /* free old img before creating new */
2496 if (NULL
!= image
) Imlib_kill_image( idata
, image
);
2497 image
= Imlib_load_image( idata
, PNG_PATH
"xtscut-end.png");
2503 ok
= lseek( in_file
, offset
, SEEK_SET
);
2505 fprintf( stdout
, "%s seek error %lld\n", WHO
, ok
);
2509 iprintf( stdout
, "%s->%s seek %09llX \n",
2510 caller
, WHO
, offset
);
2514 /* reset decoder. 1 sets look for next Sequence (0 is next picture) */
2515 mpeg2_reset( m2dec
, 1 );
2518 m2sta
= mpeg2_parse( m2dec
);
2519 dprintf( stdout
, "m2sta %d %s\n", m2sta
, m2stat
[m2sta
]);
2522 /* m2buf needs more data */
2524 sz
= parse_video_es();
2525 if (-1 != sz
) /* if it was part of video, use it */
2526 mpeg2_buffer (m2dec
, es_buf
, es_buf
+ sz
);
2529 /* SEQUENCE needs to set output size and frame rate for i frame time codes,
2530 with bit rate and aspect ratio updates based on height and width.
2532 case STATE_SEQUENCE
:
2533 iprintf( stdout
, "%s xinit %d\n", m2stat
[m2sta
], xinit
);
2535 /* might set nb fbuf, convert format, stride */
2536 /* might set fbufs */
2538 iprintf( stdout
, "Xoutput->setup Y %d %d UV %d %d\n",
2541 m2seq
->chroma_width
,
2542 m2seq
->chroma_height
);
2544 if ( xoutput
->setup( xoutput
,
2547 m2seq
->chroma_width
,
2548 m2seq
->chroma_height
,
2551 fprintf(stdout
, "Xoutput->setup failed\n");
2556 /* if this is disabled, XShm RGB and XVideo YUYV break */
2559 && mpeg2_convert (m2dec
, xsetup
.convert
, NULL
))
2561 fprintf (stdout
, "conversion setup failed\n");
2567 if (xoutput
->set_fbuf
) {
2571 dprintf(stdout
, "Xoutput->set_fbuf\n");
2572 mpeg2_custom_fbuf (m2dec
, 1);
2573 xoutput
->set_fbuf (xoutput
, buf
, &id
);
2574 mpeg2_set_buf (m2dec
, buf
, id
);
2575 xoutput
->set_fbuf (xoutput
, buf
, &id
);
2576 mpeg2_set_buf (m2dec
, buf
, id
);
2577 } else if (xoutput
->setup_fbuf
) {
2581 dprintf(stdout
, "Xoutput->setup_fbuf\n");
2582 xoutput
->setup_fbuf (xoutput
, buf
, &id
);
2583 mpeg2_set_buf (m2dec
, buf
, id
);
2584 xoutput
->setup_fbuf (xoutput
, buf
, &id
);
2585 mpeg2_set_buf (m2dec
, buf
, id
);
2586 xoutput
->setup_fbuf (xoutput
, buf
, &id
);
2587 mpeg2_set_buf (m2dec
, buf
, id
);
2589 mpeg2_skip(m2dec
, (xoutput
->draw
== NULL
));
2594 mpeg2_convert( m2dec
, mpeg2convert_rgb32
, NULL
);
2598 mpeg2_convert( m2dec
, mpeg2convert_rgb24
, NULL
);
2601 #endif /* USE_LIBVO */
2602 m2seq
= m2inf
->sequence
;
2603 frame_period
= m2seq
->frame_period
;
2604 frame_rate
= 2700000000UL / frame_period
;
2606 if (byte_rate
!= m2seq
->byte_rate
)
2608 "Frame rate %u.%u, period %5.3f mS, "
2609 "Bit rate %u, Byte rate %u\n",
2612 (double)m2seq
->frame_period
2614 m2seq
->byte_rate
<< 3,
2617 framew
= m2seq
->picture_width
;
2618 frameh
= m2seq
->picture_height
;
2619 aspectf
= (double)framew
/ (double)frameh
;
2621 byte_rate
= m2seq
->byte_rate
;
2622 bit_rate
= byte_rate
<< 3;
2628 /* might set fbuf */
2631 if (m2inf
->display_fbuf
)
2632 pictype
= 3 & m2inf
->display_picture
->flags
;
2634 /* only output I-frames, pictype 1 */
2635 if (1 != pictype
) break;
2637 iprintf( stdout
, "PICTURE %c #%d\n",
2638 pictypes
[pictype
], nif
++);
2644 /* discard should be done on picture header */
2645 if (xoutput
->discard
&& m2inf
->discard_fbuf
)
2646 xoutput
->discard( xoutput
,
2647 m2inf
->discard_fbuf
->buf
,
2648 m2inf
->discard_fbuf
->id
);
2650 if (xoutput
->set_fbuf
) {
2654 xoutput
->set_fbuf (xoutput
, buf
, &id
);
2655 mpeg2_set_buf (m2dec
, buf
, id
);
2657 if (xoutput
->start_fbuf
)
2658 xoutput
->start_fbuf( xoutput
,
2659 m2inf
->current_fbuf
->buf
,
2660 m2inf
->current_fbuf
->id
);
2662 #endif /* USE_LIBVO */
2666 /* normal end of picture or sequence end seen */
2671 if (m2inf
->display_fbuf
)
2672 pictype
= 3 & m2inf
->display_picture
->flags
;
2675 if (STATE_SLICE
== m2sta
)
2676 iprintf( stdout
, " PICTURE %c SLICE\n",
2680 if (STATE_END
== m2sta
) {
2681 iprintf( stdout
, " PICTURE %c END\n",
2687 /* make parse loop stop at the intra frame */
2689 if (m2inf
->display_fbuf
) {
2695 "x_draw_image will render\n");
2696 /* draw has been moved to x_draw_image */
2699 /* draw has been moved to x_draw_image */
2700 /* draw current picture */
2701 /* might free frame buffer */
2703 xoutput
->draw (xoutput
,
2704 m2inf
->display_fbuf
->buf
,
2705 m2inf
->display_fbuf
->id
);
2707 /* discard has been moved to x_close_display */
2708 if (xoutput
->discard
&& m2inf
->discard_fbuf
)
2709 xoutput
->discard( xoutput
,
2710 m2inf
->discard_fbuf
->buf
,
2711 m2inf
->discard_fbuf
->id
);
2716 /* filter keeps iframes */
2720 m2inf
->display_fbuf
->buf
[0] );
2721 #endif /* USE_LIBVO */
2727 /* X not init yet only scans for sequence header info */
2733 /* TESTME: why does STATE_INVALID trigger every time? */
2735 dprintf( stdout
, "STATE_INVALID\n" );
2738 /* error end of picture, show png if using X: free before use, not after */
2739 case STATE_INVALID_END
:
2740 fprintf( stdout
, "STATE_INVALID_END\n" );
2741 fprintf(stdout
, "%s offset %lld has bad data\n",
2745 /* free old img before creating new. new image is kept until reloaded */
2746 if (NULL
!= image
) imlib_free_image();
2747 image
= imlib_load_image( PNG_PATH
"xtscut-bad.png" );
2748 if (NULL
!= image
) imlib_context_set_image( image
);
2751 /* free old img before creating new. new image is kept until reloaded */
2752 if (NULL
!= image
) Imlib_kill_image( idata
, image
);
2753 image
= Imlib_load_image( idata
, PNG_PATH
"xtscut-bad.png");
2760 iprintf( stdout
, "Unhandled %d %s\n",
2761 m2sta
, m2stat
[m2sta
]);
2768 iprintf(stdout
, "\n");
2770 /* close it to reset it, even more than a reset would do? */
2771 // mpeg2_close( m2dec );
2775 /* destroy the window, close the display and clear the init flag */
2778 x_close_display ( void )
2783 /* discard any remaining buffers before closing */
2784 if (xoutput
->discard
&& m2inf
->discard_fbuf
)
2785 xoutput
->discard( xoutput
,
2786 m2inf
->discard_fbuf
->buf
,
2787 m2inf
->discard_fbuf
->id
);
2788 xoutput
->close( xoutput
);
2794 if (NULL
!= image
) imlib_free_image();
2797 if (NULL
!= image
) Imlib_kill_image( idata
, image
);
2799 XFreeGC( xdisplay
, xgc
);
2800 XDestroyWindow( xdisplay
, xwindow
);
2801 XCloseDisplay( xdisplay
);
2803 #endif /* USE_LIBVO */
2808 /* exit, with X structure deallocate */
2819 dprintf(stdout
, "%s:%d\n", WHO
, WHERE
);
2822 dprintf(stdout
, "%s:%d\n", WHO
, WHERE
);
2824 mpeg2_close( m2dec
);
2827 dprintf(stdout
, "%s:%d\n", WHO
, WHERE
);
2837 dprintf(stdout
, "%s:%d\n", WHO
, WHERE
);
2840 if (alloc_idx
> 0) {
2842 "alloc_t structures to be freed (%d):", alloc_idx
);
2851 /* i is frame index, divide by frame rate to determine the time code */
2854 show_frame_timecode ( int i
)
2864 fprintf( stdout
, " %03d:%02d", m
, s
);
2871 start_timer ( struct timer_s
*t
)
2876 if (NULL
== t
) return;
2877 memset( t
, 0, sizeof(t
));
2879 clock_gettime( clock_method
, &t
->start
);
2881 time( &t
->start
.tv_sec
);
2888 stop_timer ( struct timer_s
*t
)
2892 if (NULL
== t
) return;
2894 clock_gettime( clock_method
, &t
->stop
);
2896 /* minimum ET is 1s */
2897 time( &t
->stop
.tv_sec
);
2898 if ( t
->start
.tv_sec
== t
->stop
.tv_sec
) t
->stop
.tv_sec
++;
2905 diff_timer ( struct timer_s
*t
)
2909 memcpy( &t
->diff
, &t
->stop
, sizeof(struct timespec
) );
2911 dprintf(stdout
, "\n%s\n", WHO
);
2912 dprintf(stdout
, "start %d.%09d\n", (int)t
->start
.tv_sec
, (int)t
->start
.tv_nsec
);
2913 dprintf(stdout
, "stop %d.%09d\n", (int)t
->stop
.tv_sec
, (int)t
->stop
.tv_nsec
);
2914 /* nanosecond borrow from second? */
2915 if (t
->start
.tv_nsec
> t
->stop
.tv_nsec
) {
2916 t
->diff
.tv_sec
--; /* borrow */
2917 t
->diff
.tv_nsec
+= 1000000000; /* lend */
2920 t
->diff
.tv_sec
-= t
->start
.tv_sec
;
2921 t
->diff
.tv_nsec
-= t
->start
.tv_nsec
;
2922 dprintf(stdout
, "diff %d.%09d\n", (int)t
->diff
.tv_sec
, (int)t
->diff
.tv_nsec
);
2928 /* show the byte offsets and total for cuts, check against free volspace */
2933 int i
, fce
, fco
, fca
;
2934 long long cpt
, cat
, cet
, cot
, vbf
;
2938 // FIXME: disabled for debug, but enable for output redux on cl-usage
2939 // if (0 != arg_cut) return;
2943 /* preamble byte count is PAT + PMT + optional NULLs */
2944 calc_null_packets();
2945 cpt
= TSZ
* (1 + pmt_pkts
);
2946 cpt
+= TSZ
* null_pkts
;
2948 fce
= fco
= fca
= 0;
2949 cet
= cot
= cat
= 0;
2951 build_scut_frames();
2954 fprintf( stdout, "Cut list from I:P:B thresholds of %d:%d:%d\n",
2955 arg_ti, arg_tp, arg_tb);
2958 /* if it only shows this one, this trick is ok */
2959 scuts
[scut_idx
] = sequence_idx
-1;
2961 fprintf( stdout
, "Cut # SEQ # byte offset file size start runtime frames %%\n");
2962 fprintf( stdout
, "----- ------ ---------------- ---------------- ------- ------- ------ -----\n");
2965 fprintf( stdout
, " none none %16lld %16lld PAT+PMT%s pre-amble\n",
2966 0LL, cpt
, (0 != arg_nulls
) ?"+NULL":"");
2968 fprintf( stdout
, "Elementary Stream write has no pre-amble\n");
2970 for (i
= 0 ; i
< scut_idx
-1; i
++) {
2974 /* don't need last I frame plus junk */
2975 if (0 == scuts
[i
+1]) break;
2977 /* byte start of cut and byte count of cut */
2978 cs
= sequences
[ scuts
[ i
] ];
2979 cz
= sequences
[ scuts
[ i
+1 ] ] - cs
;
2981 /* frame start of cut and frame count of cut */
2982 fcs
= iframes
[ scuts
[ i
] ];
2983 fcz
= iframes
[ scuts
[ i
+1 ] ] - fcs
;
2990 /* off by one, list starts with cut 1 */
2992 /* cut even total */
3003 lltoasc( anum
, cz
+ cpt
);
3005 /* cut 001 to last cut: cut size includes pre-amble for each cut */
3006 fprintf( stdout
, "%3d @ %6d ", i
+1, scuts
[i
]);
3007 lltoasc( anum
, cs
);
3008 fprintf( stdout
, "%16s ", anum
);
3009 lltoasc( anum
, cz
+ cpt
);
3010 fprintf( stdout
, "%16s ", anum
);
3012 show_frame_timecode( fcs
);
3013 fprintf( stdout
, " ");
3014 show_frame_timecode( fcz
);
3015 fprintf( stdout
, "%7d ", fcz
);
3016 fprintf( stdout
, " %3lld%%", (cz
* 100) / in_size
);
3017 fprintf( stdout
, "\n");
3021 /* data after last cut not displayed because it's irrelevant */
3022 fprintf( stdout
, "%3d @ %6d %11lld %11lld End cruft\n",
3025 sequences
[ sequence_idx
- 1 ],
3026 in_size
- sequences
[ scuts
[ scut_idx
- 1 ]]
3030 fprintf( stdout
, "\nEnd of cut list\n");
3032 statfs( arg_path
, &fs
);
3033 vbf
= fs
.f_bsize
* fs
.f_bavail
;
3034 fprintf( stdout
, "Free space %11lld bytes in volume %s:\n",
3037 fprintf( stdout
, " EVEN cut time");
3038 show_frame_timecode( fce
);
3039 fprintf( stdout
, ", bytes %11lld, ", cet
);
3040 fprintf( stdout
, "frames %6d (%2d%%), will %sfit.\n",
3041 fce
, (100 * fce
) / frame_idx
, (cet
< vbf
) ?"":"NOT " );
3043 fprintf( stdout
, " ODD cut time");
3044 show_frame_timecode( fco
);
3045 fprintf( stdout
, ", bytes %11lld, ", cot
);
3046 fprintf(stdout
, "frames %6d (%2d%%), will %sfit.\n",
3047 fco
, (100 * fco
) / frame_idx
, (cot
< vbf
) ?"":"NOT " );
3049 fprintf( stdout
, " ALL cut time");
3050 show_frame_timecode( fca
);
3051 fprintf( stdout
, ", bytes %11lld, ", cat
);
3052 fprintf(stdout
, "frames %6d (%2d%%), will %sfit.\n",
3053 fca
, (100 * fca
) / frame_idx
, (cat
< vbf
) ?"":"NOT " );
3055 fprintf( stdout
, "\n" );
3060 /* search forwards and backwards in frames[] and pick nearest I frame */
3061 /* f is the frame number from where to start looking, inclusive */
3064 find_nearest_iframe ( int f
, char *caller
)
3072 iprintf( stdout
, "%s:%d %s %d\n\n", WHO
, WHERE
, caller
, f
);
3074 if (0 == f
) return 0;
3077 if (f
>= frame_idx
) {
3079 iprintf(stdout
, "%s last frame %d, last sequence frame %d \n",
3080 WHO
, f
, iframes
[iframe_idx
- 1 ]);
3083 /* search backwards until an Intra frame found or start of list reached */
3084 for (i
= f
; i
>= 0; i
--) {
3085 if (1 == frames
[i
].pct
) {
3091 /* search forwards until an Intra frame found or end of list reached */
3092 for (i
= f
; i
< frame_idx
; i
++) {
3093 if (1 == frames
[i
].pct
) {
3099 // fprintf(stdout, "%s f %d p %d n %d\n\n", WHO, f, pi, ni);
3101 /* both negative is invalid, return frame 0 */
3102 if (( pi
< 0) && (ni
< 0)) return 0;
3104 /* is previous or next invalid? use other value if so */
3105 if ( pi
< 0 ) return ni
;
3106 if ( ni
< 0 ) return pi
;
3108 if ( pi
== ni
) return pi
;
3111 /* if distance to previous is more than distance to next, use next */
3112 if ( (f
- pi
) > (ni
- f
) ) i
= ni
;
3114 // fprintf( stdout, "%s frames[%d].num %d\n", WHO, i, frames[i].num);
3120 /* Search forward in frames[] to get frame number of next I frame. */
3121 /* f is the frame number from where to start looking, non-inclusive */
3124 find_next_iframe ( int f
)
3132 /* search forwards until out of frames or Intra found */
3133 for (i
= (f
+ 1); i
< frame_idx
; i
++)
3134 if (1 == frames
[i
].pct
)
3140 /* Search backward in frames[] to get frame number of previous I frame. */
3141 /* f is the frame number from where to start looking, non-inclusive */
3144 find_prev_iframe ( int f
)
3152 /* search backwards until out of frames or Intra found */
3153 for (i
= (f
- 1); i
>= 0; i
--)
3154 if (1 == frames
[i
].pct
)
3158 /****************************************************************************/
3161 /************************************************************** X functions */
3163 /* compute vertical y offset from fontsize and text line (row) number */
3164 /* xfonts is what font was loaded */
3165 /* FIXME: doesn't check for offscreen */
3168 x_fontline ( int row
)
3171 if (0 == xinit
) return 0;
3172 // if (0 != arg_xmsg) WHOAMI;
3174 /* total font height = cell height (ascender) + descender + blank */
3175 ytot
= xfonts
->ascent
+ xfonts
->descent
+ 1; /* 1 or 2 blanks? */
3177 /* however, font baseline does not count descender (leave a blank?) */
3178 /* should already have ytot correct with ascent+descent+blank */
3179 return (row
* ytot
) + xfonts
->ascent
+ 1;
3180 /* return (row * ytot); */
3184 x_fontwide ( int col
)
3188 if (0 == xinit
) return 0;
3190 xtot
= xfonts
->max_bounds
.rbearing
- xfonts
->min_bounds
.lbearing
;
3192 return (col
* xtot
);
3195 /* set font for text display. this doesn't try to revert to "fixed" font */
3196 /* return is 0 if font was not loaded and nz if font was loaded */
3199 x_setfont ( int w
, int h
)
3203 if (0 == xinit
) return 0;
3205 snprintf(fn
, sizeof(fn
), "%dx%d", w
, h
);
3208 // if (0 != arg_xmsg) WHOAMI;
3210 /* set the user requested font, if it fails fall back to tiny fixed */
3211 xfonts
= XLoadQueryFont(xdisplay
, fn
);
3212 if ( xfonts
!= (XFontStruct
*)NULL
) {
3213 XSetFont(xdisplay
, xgc
, xfonts
->fid
);
3221 /* set font for text display. caller must have already set bg and fg */
3224 x_set_font ( char *fn
)
3226 if (0 == xinit
) return;
3228 // if (0 != arg_xmsg) WHOAMI;
3230 /* set the user requested font, if it fails fall back to tiny fixed */
3231 xfonts
= XLoadQueryFont(xdisplay
, fn
); /* "fixed" or "10x20" */
3232 if ( xfonts
== (XFontStruct
*)NULL
){
3233 fprintf(stdout
,"can't load %s font trying fixed\n", fn
);
3234 xfonts
= XLoadQueryFont(xdisplay
,"fixed");
3235 if ( xfonts
== (XFontStruct
*)NULL
) {
3236 fprintf(stdout
,"can't load fixed font\n");
3239 xprintf(stdout
,"X font %s loaded\n", fn
);
3240 /* should probably abort here */
3245 XSetFont(xdisplay
, xgc
, xfonts
->fid
);
3246 // xprintf(stdout,"X font %s selected\n", fn);
3255 if (0 != arg_xmsg
) WHOAMI
;
3257 if (0 == xinit
) return;
3259 /* tell window manager about this window: title, icon, hw, xy */
3260 /* XSetWMProperties is more hassle than is needed here */
3261 XSetStandardProperties( xdisplay
,
3273 /* this draws tc color text without background fill */
3276 x_draw_text_line ( unsigned int tc
, int x
, int y
, char *t
)
3278 if (0 == xinit
) return;
3279 XSetForeground( xdisplay
, xgc
, tc
);
3280 /* XDrawString draws fg only */
3281 XDrawString( xdisplay
, xwindow
, xgc
, x
, y
, t
, strlen(t
) );
3282 XSetForeground( xdisplay
, xgc
, xfg
);
3286 /* this draws tc color text on current background color fill */
3289 x_draw_text_line_bg ( unsigned int tc
, int x
, int y
, char *t
)
3291 if (0 == xinit
) return;
3292 XSetForeground( xdisplay
, xgc
, tc
);
3293 /* XDrawImageString draws fg + bg */
3294 XDrawImageString( xdisplay
, xwindow
, xgc
, x
, y
, t
, strlen(t
) );
3295 XSetForeground( xdisplay
, xgc
, xfg
);
3298 /* this draws tc color text with transparent background fill */
3301 x_draw_text_block ( unsigned int tc
, int x
, int y
, char *s
)
3306 if (0 == *s
) return;
3313 if ( (t
== e
) || ('\n' == (*t
++ = *s
++)) ) {
3317 XSetForeground( xdisplay
, xgc
, tc
);
3318 XDrawString( xdisplay
, xwindow
, xgc
,
3319 x
, y
, b
, strlen(b
) );
3320 XSetForeground( xdisplay
, xgc
, xfg
);
3321 // x_draw_text_line(fgc, x, y, b);
3322 // fprintf(stdout, "@%dx%d %s\n", x, y, b);
3328 /* find the longest line in p, either NL term or NULL term */
3331 find_longest_line ( char *p
)
3348 /* count the NL chars */
3351 find_line_count ( char *p
)
3355 while (0 != *p
) if ('\n' == *p
++) i
++;
3359 /* find a font that will fit inside the x, y pixel box
3360 with text chars w wide and int h tall
3362 returns index to xpcf or -1 if can't fit the text
3366 x_find_quad_font ( int x
, int y
, int w
, int h
)
3368 int i
, j
, w1
, h1
, x1
, y1
;
3371 /* 5x8 font is bare minimum, get out now if that won't fit */
3372 if (x
< (5*w
)) return -1;
3373 if (y
< (8*h
)) return -1;
3375 /* largest font is index 0, keep going until something fits */
3376 for (i
= 0; i
< xpcf_idx
; i
++) {
3378 j
= x_setfont( f
->w
, f
->h
);
3380 dprintf(stdout
, "%s could not find font %s\n", WHO
, f
->name
);
3388 dprintf( stdout
, "%s %s i %d x %d y %d w %d h %d x1 %d y1 %d w1 %d h1 %d\n",
3389 WHO
, f
->name
, i
, x
, y
, h
, w
, x1
, y1
, w1
, h1
);
3390 if (x1
> x
) continue; // 1px left/right?
3391 if (y1
> y
) continue; // 1px top/bottom?
3400 x_draw_keyhelp ( void )
3402 int i
, j
, x
, y
, w
, h
, m
;
3406 if (1 != xhelp
) return;
3408 // fprintf(stdout, "%s\n", WHO);
3411 build_keyhelp( s
, sizeof(keyhelp
), 0 );
3412 w
= find_longest_line( s
); // for centering
3413 j
= h
= find_line_count( s
);
3420 /* which font will fit the keyhelp text in the video area */
3421 i
= x_find_quad_font( x
+ m
, y
+ m
, w
, h
);
3423 /* no font will fit, nop */
3425 fprintf(stdout
,"No font small enough for keyhelp\n");
3439 x_draw_quad_gradientv( COLOR_MAGENTA0
, x
, y
, w
+ m
, h
+ m
, -0.66 );
3441 x_draw_text_block( COLOR_WHITE
, x
+ m
, y
+ m
, s
);
3446 x_draw_cfghelp( void )
3448 int i
, j
, x
, y
, w
, h
, m
;
3452 // fprintf(stdout, "%s\n", WHO);
3456 if (2 != xhelp
) return;
3458 build_cfghelp( s
, sizeof(cfghelp
), 0 );
3459 w
= find_longest_line( s
); // for centering
3460 j
= h
= find_line_count( s
);
3467 /* which font will fit the keyhelp text in the video area? */
3468 i
= x_find_quad_font( x
+ m
, y
+ m
, w
, h
);
3470 /* no font will fit, nop */
3472 fprintf(stdout
,"No font small enough for cfghelp\n");
3486 x_draw_quad_gradientv( COLOR_GREEN0
, x
, y
, w
+ m
, h
+ m
, -0.66 );
3488 x_draw_text_block( COLOR_WHITE
, x
+ m
, y
+ m
, s
);
3493 x_draw_cutlist( void )
3495 int i
, j
, x
, y
, w
, h
, m
;
3502 // if (0 == *s) return;
3504 if (3 != xhelp
) return;
3506 build_cutlist( s
, sizeof(cutlist
) );
3508 // fprintf(stdout, "%s\n", s);
3510 w
= find_longest_line( s
); // for centering
3511 j
= h
= find_line_count( s
);
3518 /* which font will fit the keyhelp text in the video area? */
3519 i
= x_find_quad_font( x
+ m
, y
+ m
, w
, h
);
3521 /* no font will fit, nop */
3523 fprintf(stdout
,"No font small enough for cutlist\n");
3537 x_draw_quad_gradientv( COLOR_BLUE
, x
, y
, w
+ m
, h
+ m
, -0.66 );
3539 x_draw_text_block( COLOR_WHITE
, x
+ m
, y
+ m
, s
);
3545 x_draw_outlist( void )
3547 int i
, j
, x
, y
, w
, h
, m
;
3555 // if (0 == *s) return;
3557 if (4 != xhelp
) return;
3559 w
= find_longest_line( s
); // for centering
3560 j
= h
= find_line_count( s
);
3567 /* which font will fit the keyhelp text in the video area? */
3568 i
= x_find_quad_font( x
+ m
, y
+ m
, w
, h
);
3570 /* no font will fit, nop */
3572 fprintf(stdout
,"No font small enough for cut write list\n");
3587 if (0 != scut_done
) c
= COLOR_GREEN
;
3589 x_draw_quad_gradientv( c
, x
, y
, w
+ m
, h
+ m
, 0.50 );
3591 x_draw_text_block( COLOR_WHITE
, x
+ m
, y
+ m
, s
);
3596 /* Draw frame timecode at the top of the histogram.
3597 Even cuts have brighter text than odd cuts.
3599 x is horizontal offset
3601 c is 0 even cut, 1 odd cut
3605 x_draw_timecode ( int x
, int f
, int c
)
3608 unsigned int fg
, bg
, yoffset
;
3611 if (0 == xinit
) return;
3613 // if (0 != arg_xmsg) WHOAMI;
3615 /* this seems to work well. use it for show_frame_timecode too */
3623 snprintf( xtext
, sizeof(xtext
), "%d", f
);
3625 snprintf( xtext
, sizeof(xtext
), "%02d:%02d:%02d", h
, m
, s
);
3628 x_setfont( 10, 20 );
3630 yoffset
= x_fontline(0)-1; /* text origin is font baseline */
3637 // bg = COLOR_WHITE;
3640 // XSetBackground(xdisplay, xgc, bg);
3642 x_draw_text_line( fg
, x
+2, yoffset
, xtext
);
3644 // XSetBackground(xdisplay, xgc, xbg);
3648 /* TESTME: These may need re-ordering to reduce display flicker */
3649 /* Time goes at top of line, cut number goes at bottom of line */
3650 /* These are drawn to left of line so following lines do not overwrite it */
3653 x_draw_cutnum ( int x
, int n
)
3658 if (0 == xinit
) return;
3660 // if (0 != arg_xmsg) WHOAMI;
3662 /* draw cut number n at bottom of red vertical line at x */
3663 /* use spaces to draw black background to left and right of this */
3664 snprintf( t
, sizeof(t
)-1, "%d", n
);
3666 /* want this to fit within the new eye candy area */
3667 x_setfont( 10, 20 );
3670 /* 10-12 cuts is most common for 60m, 6-8 for 30m, 16-20 for 2h */
3672 /* FIXME: need char spacing for monospace or lookup proportional */
3674 /* 10x20 font is more readable than fixed */
3677 /* 10-12 cuts is most common for 60 minutes */
3678 if (n
> 9) x1
= x_fontwide(2);
3680 /* won't see 100 cuts unless -i autocut gets it wrong */
3681 if (n
> 99) x1
= x_fontwide(3);
3684 x_draw_text_line_bg( COLOR_WHITE
, x
-x1
, y
, t
);
3686 x_draw_text_line( COLOR_WHITE
, x
-x1
, y
, t
);
3691 /* draw the IPB legend */
3694 x_draw_legend ( void )
3697 char t
[8] = "I P B";
3699 if (0 == xinit
) return;
3700 if (0 == arg_lb
) return;
3702 // if (0 != arg_xmsg) WHOAMI;
3704 x_set_font( "fixed" );
3706 // fprintf(stdout, "xfw %d xfh %d\n", x_fontwide(1), x_fontline(0));
3708 h
= x_fontline(0); /* query text height */
3710 /* first the text */
3712 /* FIXME: center needs to have a better algorithm reflecting legend width */
3713 x
= (vw
.w
/2)-15; /* 10 pixels per cell * 3 cells, center of it */
3719 /* blank the area used for buttons, bottom center */
3721 // x_draw_quad_fill( 0, x-8, y - ( 4 * h ), 44, 4 * h );
3722 x_draw_quad_gradientv( COLOR_GREY1
,
3723 x
-8, y
- ( 4 * h
), 44, 4 * h
, 1.0 );
3727 tys
= y
- (3 * byh
);
3732 /* set button area region */
3740 /* now the blocks with colors, if enabled */
3742 if (0 == bis
) c
= 0;
3743 if (0 != arg_lb
) x_draw_quad_fill( c
, x
, y
-5, 8, 10);
3748 if (0 == bps
) c
= 0;
3749 if (0 != arg_lb
) x_draw_quad_fill( c
, x
, y
-5, 8, 10);
3754 if (0 == bbs
) c
= 0;
3755 if (0 != arg_lb
) x_draw_quad_fill( c
, x
, y
-5, 8, 10);
3760 if (0 != arg_lb
) x_draw_text_line( COLOR_WHITE
, x
-1, y
, t
);
3762 /* time-code or frame number */
3764 snprintf( t
, sizeof(t
), "%d", xlfd
);
3771 snprintf( t
, sizeof(t
), "%02d:%02d", m
, s
);
3774 x
-= (2 * strlen(t
));
3779 /* needs a background if ipb buttons are not displayed */
3781 x_draw_quad_fill( 0, x
-8, y
- h
, 44, h
+ 5 );
3783 x_draw_text_line( COLOR_WHITE
, x
, y
+1, t
);
3788 /* This shows up when histogram scale is set to 100%. */
3789 /* Disabled since it's not helpful for editing. */
3792 x_draw_reticle( void )
3800 x_set_font("fixed"); // 6x12
3804 for (y
= y1
; y
< vw
.h
; y
+= y1
) {
3805 snprintf(t
, sizeof(t
), "%d bits", 8 * TSZ
* y
);
3806 x_draw_line( COLOR_WHITE
, x
, y
, x1
, y
);
3807 x_draw_text_line_bg( COLOR_WHITE
, x
, y
+12, t
);
3813 #define USE_DRAW_SEGMENTS
3814 /* Visual graph of packet usage for each of I P and B frame types.
3815 NOTE: This does include the audio packets, but audio only adds
3816 around 10 to 20 packets per frame depending on audio resolution.
3818 ->vpn is misleading, should be ->spn for stream packet number.
3820 XDrawSegments can be optionally used to eliminate some ugly visual
3821 artifacting when changing histogram scale with B + P lines on.
3823 Apparently with individual XDrawLine()'s, the CRT refresh catches it.
3825 Artifact is a black right triangle with 90 degrees angle at top right.
3826 Theta increases proportional to the height of the histogram until
3827 it finally covers about top right third of the screen at hist scale 1.0.
3828 It goes away as soon as you stop changing the scale but it is annoying.
3830 Also, XDrawSegments draws the P and B lines seemingly a lot faster.
3835 x_draw_histogram ( void )
3837 int i
, j
, x
, y
, h
, w
, c
, r
, p
;
3841 #ifdef USE_DRAW_SEGMENTS
3842 #warning using XDrawSegment for histogram
3846 xseg
= icalloc( vw
.w
, sizeof(XSegment
), "xseg" );
3850 if (0 == xinit
) return;
3851 if (0 == xredraw
) return;
3853 if (0 != arg_xmsg
) WHOAMI
;
3856 /* build any possible cuts for display */
3857 // build_scut_frames();
3859 if (xoffset
< 0) xoffset
= 0;
3867 /* blank or shade top 16 lines for timecode redraw */
3868 /* TODO: should be font lines size? */
3871 y
= 20; /* font is 10x20 for timecode */
3872 // x_draw_quad_fill( 0, 0, 0, w, y);
3874 /* corners need to have color set to black so gradient shows up */
3875 x_draw_quad_fill( COLOR_BLACK
, 0, 0, 8, y
);
3876 x_draw_quad_fill( COLOR_BLACK
, w
- 8, 0, 8, y
);
3878 x_draw_quad_gradientv( COLOR_GREY1
, 0, 0, w
, y
, -1.0 );
3881 /* draw a shaded bar for the cut numbers to show up against */
3883 /* corners need to have color set to black so gradient shows up */
3884 x_draw_quad_fill( COLOR_BLACK
, 0, h
-20, 8, 20);
3885 x_draw_quad_fill( COLOR_BLACK
, w
- 8, h
-20, 8, 20);
3886 x_draw_quad_gradientv( COLOR_GREY1
, 0, h
-20, w
, 20, 1.0 );
3891 #ifdef USE_DRAW_SEGMENTS
3892 if ((0 != arg_hl
) && (0 != bps
)) {
3893 XSetForeground( xdisplay
, xgc
, COLOR_MAGENTA
);
3894 if (0 != arg_tc
) y
= 18;
3895 // x_draw_line( c, x, y, x, r + y );
3897 for (i
= 0; i
< w
; i
++) {
3901 if (2 != f
->pct
) continue;
3902 a
= hist_scale
* (double)f
->fpc
;
3903 /* frame packet count */
3906 xseg1
->x1
= 0xFFFF & x
;
3907 xseg1
->x2
= 0xFFFF & x
;
3908 xseg1
->y1
= 0xFFFF & y
;
3909 xseg1
->y2
= 0xFFFF & (r
+ y
);
3914 XDrawSegments(xdisplay
, xwindow
, xgc
, xseg
, k
);
3917 if ((0 != arg_hl
) && (0 != bbs
)) {
3918 XSetForeground( xdisplay
, xgc
, COLOR_YELLOW
);
3919 if (0 != arg_tc
) y
= 20;
3922 for (i
= 0; i
< w
; i
++) {
3926 if (3 != f
->pct
) continue;
3927 a
= hist_scale
* (double)f
->fpc
;
3928 /* frame packet count */
3931 xseg1
->x1
= 0xFFFF & x
;
3932 xseg1
->x2
= 0xFFFF & x
;
3933 xseg1
->y1
= 0xFFFF & y
;
3934 xseg1
->y2
= 0xFFFF & (r
+ y
);
3939 XDrawSegments(xdisplay
, xwindow
, xgc
, xseg
, k
);
3942 ifree( xseg
, "xseg" );
3945 for (i
= 0; i
< w
; i
++) {
3949 /* boundary limit */
3950 if (j
>= frame_idx
) break;
3954 a
= hist_scale
* (double)f
->fpc
;
3956 /* frame packet count */
3961 #ifdef USE_DRAW_SEGMENT
3962 if (1 != f
->pct
) continue;
3966 /* empty entry is a blank line. drawing a blank would erase the render */
3968 /* I frame lines stick above B frame lines by 4 pixels */
3969 /* but want pixels below lines 0-19 to represent scaled packet count */
3971 /* Except for some events on FOX or MNT, I frames aren't drawn enough
3972 to require the above XDrawSegments speed-up.
3976 /* most I frame lines start under the time */
3978 if (0 != arg_tc
) y
= 16;
3979 /* limit frame time display to every 60 frames at most */
3980 if ( (0 == i
) || (j
>= (p
+120)) ) {
3983 /* want the line extended upwards to be next to the time code */
3984 x_draw_line( COLOR_WHITE
, x
, 0, x
, y
- 1 );
3985 x_draw_timecode( i
, j
, f
->tcolor
);
3990 /* cut is indicated by red line */
3994 /* current preview overrides to white line */
3995 if (j
== xlfd
) c
= COLOR_WHITE
;
3997 /* draw line after drawing cutnum */
3998 x_draw_cutnum( x
, f
->cutnum
);
3999 // fprintf(stdout, "cut point y %d\n", y);
4000 x_draw_line( c
, x
, y
, x
, h
);
4004 /* current preview position is indicated by white line */
4007 // fprintf(stdout, "current preview y %d\n", y);
4008 x_draw_line( c
, x
, y
, x
, h
);
4012 /* non cut draws and colors the line if I frame histogram selected */
4013 if ((0 != arg_hl
) && (0 != bis
)) {
4015 // fprintf(stdout, "normal y %d\n", y);
4016 x_draw_line( c
, x
, y
, x
, r
+ y
);
4020 /* whoops missing, caused P to show up as I */
4023 /* X Draw Segments should be faster when these are enabled */
4024 #ifndef USE_DRAW_SEGMENTS
4025 /* P frame lines are 2 pixels lower than I frame lines */
4027 if ((0 != arg_hl
) && (0 != bps
)) {
4029 if (0 != arg_tc
) y
= 18;
4030 x_draw_line( c
, x
, y
, x
, r
+ y
);
4034 /* B frame lines are 2 pixels lower than P frame lines */
4036 if ((0 != arg_hl
) && (0 != bbs
)) {
4038 if (0 != arg_tc
) y
= 20;
4039 x_draw_line( c
, x
, y
, x
, r
+ y
);
4052 if ( 1.0 == hist_scale
) x_draw_reticle();
4055 /* This has some odd problems with scaling and colors */
4056 #ifdef USE_IMLIB2_DRAW
4058 imlib_render_image_on_drawable_at_size( 0, 0, vw
.w
, vw
.h
);
4063 /* Frame slider bead, visual indication of stream position.
4064 TODO: "button1 on slider" replaces "button2 anywhere"
4068 x_draw_slider ( void )
4070 int i
, x
, y
, w
, x1
, x2
;
4072 if (frame_idx
< 1) return;
4073 if (0 == arg_sb
) return;
4082 /* if no cuts, use dark grey bead line across screen */
4083 if (0 == scut_idx
) {
4084 x_draw_line( COLOR_GREY0
, 0, y
, vw
.w
, y
);
4087 /* if there are cuts, try to represent them by a long broken bead line */
4090 The problem with color selection is a visual refraction issue which can
4091 make red and green and blue lines appear at slightly different heights.
4092 The refraction issue may be reduced somewhat when using CMY on grey.
4094 for (i
= 0; i
< scut_idx
; i
++) {
4096 /* what is the x endpoint for this segment? */
4097 x2
= iframes
[scuts
[i
]];
4101 /* cut[0] is first cut, is odd numbered cut */
4103 x_draw_line( COLOR_CYAN
, x1
, y
, x2
, y
);
4105 x_draw_line( COLOR_GREY0
, x1
, y
, x2
, y
);
4110 if (x1
< frame_idx
) {
4111 x_draw_line( COLOR_GREY0
, x1
, y
, vw
.w
, y
);
4115 /* A visual problem is: viewpoint bead is 3 wasted draws on short streams. */
4116 if (frame_idx
> vw
.w
)
4119 /* one extra black background line above when eye candy enabled */
4121 x_draw_line( COLOR_BLACK
, 0, y
, vw
.w
, y
);
4123 /* top line of of viewpoint bead is light grey */
4124 x_draw_line( COLOR_GREY1
, x
, y
, x
+ w
, y
);
4126 /* middle line of viewpoint bead is white */
4127 x_draw_line( COLOR_WHITE
, x
, y
, x
+ w
, y
);
4129 /* bottom line of viewpoint bead is light grey */
4131 /* one extra black background line below when eye candy enabled */
4133 x_draw_line( COLOR_BLACK
, 0, y
, vw
.w
, y
);
4134 x_draw_line( COLOR_GREY1
, x
, y
, x
+ w
, y
);
4141 x_draw_image ( char *caller
)
4145 if (0 == xinit
) return;
4147 xprintf(stdout
, "%s->%s\n", caller
, WHO
);
4149 if (0 != arg_xmsg
) WHOAMI
;
4151 /* create new image from current middle position */
4153 o
= xoffset
+ (vw
.w
>>1);
4155 /* E) short stream starts preview at frame 0 */
4156 if (frame_idx
<= vw
.w
) {
4157 // if (o > frame_idx)
4161 /* start at first iframe after offset */
4162 xcfd
= find_nearest_iframe( o
, WHO
);
4164 /* decode the Intra frame to an image buffer */
4165 parse_intra_frame( sequences
[frames
[ xcfd
].num
], WHO
);
4172 if (NULL
!= xoutput
->draw
) {
4173 if (NULL
!= m2inf
->display_fbuf
->id
) {
4174 if (NULL
!= m2inf
->display_fbuf
->buf
) {
4175 xoutput
->draw( xoutput
,
4176 m2inf
->display_fbuf
->buf
,
4177 m2inf
->display_fbuf
->id
);
4185 /* imlib handles the video resizing */
4187 if (NULL
!= image
) {
4188 imlib_render_image_on_drawable_at_size( 0, 0, vw
.w
, vw
.h
);
4192 if (NULL
!= image
) {
4193 Imlib_apply_image( idata
, image
, xwindow
);
4201 x_redraw_display ( void )
4203 if (0 == xinit
) return;
4204 if (0 == xredraw
) return;
4206 if (0 != arg_xmsg
) WHOAMI
;
4209 /* YUV overlay frame can't serve as histogram clear like imlib frame does */
4210 // x_clear_display();
4212 x_draw_image( WHO
);
4226 /* shorthand, everyone knows which display window it is */
4229 x_check_twe( int event_type
, XEvent
*event_return
)
4231 // if (0 != arg_xmsg) WHOAMI;
4233 XCheckTypedWindowEvent( xdisplay
, xwindow
, event_type
, event_return
);
4236 /***********************************************************************
4237 * start of main block of X window support functions
4238 * FIXME: all of these functions need some kind of error handling
4239 ***********************************************************************/
4241 /* this one is still buggy, eventually crashes the display */
4245 x_resize_display ( void )
4247 XSetWindowAttributes nxswa
;
4248 unsigned long nxswamask
;
4251 if (0 == xinit
) return;
4253 if (0 != arg_xmsg
) WHOAMI
;
4256 nxswamask
= CWOverrideRedirect
;
4260 /* unmap the window so we can change attributes */
4261 /* loses focus? unmap sent back to previous window? */
4263 XUnmapWindow( xdisplay
, xwindow
);
4265 /* wait and eat notify events until we get UnmapNotify */
4266 do {} while ( False
== x_check_twe( UnmapNotify
, &xev
) );
4268 /* turn off window manager dressing */
4269 nxswa
.override_redirect
= True
;
4270 XChangeWindowAttributes( xdisplay
,
4276 /* map the window back onscreen with new attributes */
4277 XMapWindow( xdisplay
, xwindow
);
4279 /* wait and eat notify events until we get MapNotify */
4280 do {} while ( False
== x_check_twe( MapNotify
, &xev
) );
4283 /* set focus to the fullscreen window so xinput works.
4284 NOTE: doesn't seem to work if it's in x_init only
4286 XSetInputFocus( xdisplay
,
4294 /* maximize the window */
4295 XMoveResizeWindow( xdisplay
, xwindow
,
4297 xattr
.width
, xattr
.height
4299 /* wait for ConfigureNotify event
4300 have to do this because otherwise xevent() will see
4301 the resize and try to change the aspect ratio
4305 do {} while ( False
== x_check_twe( ConfigureNotify
, &xev
) );
4307 /* save current window settings for later restore,
4308 update current overlay w/ root window attribs, full-screen
4317 vw
.w
= xattr
.width
; /* root window size */
4318 vw
.h
= xattr
.height
; /* root window size */
4320 /* Sequence parse should have been done, so this should work by this point. */
4321 vw
.h
= calc_aspect_h( vw
.w
); /* aspect height fix */
4323 if (vw
.h
> xattr
.height
) {
4324 fprintf( stdout
, "\nNeeds calc_aspect attribs fix\n");
4328 xprintf(stdout
,"\tMaximized window x%d y%d w%d h%d\n",
4329 xattr
.x
, xattr
.y
, xattr
.width
, xattr
.height
);
4330 xprintf(stdout
,"\tOverlay window x%d y%d w%d h%d\n",
4331 vw
.x
, vw
.y
, vw
.w
, vw
.h
);
4334 /* restore overlay and window to previous saved.
4335 have to adjust for titlebar height again too */
4341 /* unmap the window so we can change its attributes */
4342 XUnmapWindow( xdisplay
, xwindow
);
4344 /* wait and eat notify events until we get UnmapNotify */
4345 do {} while ( False
== x_check_twe( UnmapNotify
, &xev
) );
4347 /* turn on window manager dressing */
4348 nxswa
.override_redirect
= False
;
4349 XChangeWindowAttributes( xdisplay
,
4356 /* put cursor back to normal for windowed */
4357 // XUndefineCursor( xdisplay, xwindow);
4359 /* map the window back onscreen with restored attributes */
4360 XMapWindow( xdisplay
, xwindow
);
4362 /* wait and eat notify events until MapNotify */
4363 do {} while ( False
== x_check_twe( MapNotify
, &xev
) );
4365 /* restore window size to previously saved size, minus the titlebar height */
4366 XMoveResizeWindow( xdisplay
,
4374 /* wait for and eat the ConfigureNotify event.
4375 have to do this because otherwise x events will see the resize
4376 and try to change the aspect ratio [?old cruft?]
4378 do {} while ( False
== x_check_twe( ConfigureNotify
, &xev
) );
4380 xprintf(stdout
,"\tRestored x%d y%d w%d h%d\n",
4381 vw
.x
, vw
.y
, vw
.w
, vw
.h
);
4386 /* initialize libmpeg2 and load the first Sequence
4387 also set the default video window size from the first sequence data
4397 accel
= mpeg2_accel( arg_mx
);
4406 t
= votypes
[2 + arg_vo
];
4409 if ((accel
>= 0) && (accel
< 8))
4410 fprintf( stdout
, "\n%s using x86 FPU (%d) %s; %s\n",
4411 WHO
, accel
, x86mx
[accel
], t
);
4413 m2dec
= mpeg2_init();
4414 if ( NULL
== m2dec
) {
4415 fprintf( stdout
, "%s could not initialize decoder.\n", WHO
);
4419 m2inf
= mpeg2_info( m2dec
);
4420 if ( NULL
== m2inf
) {
4421 fprintf( stdout
, "%s could not initialize info.\n", WHO
);
4426 /* parse first I frame to load frame size, frame rate and bitrate info */
4427 parse_intra_frame( sequences
[0], WHO
);
4428 fprintf(stdout
, "%s parse_intra_frame %09llX\n", WHO
, sequences
[0] );
4430 /* anyone that reports 1080 must be stupid. clamp it to 16 pixel macroblock */
4431 if (1080 == frameh
) frameh
= 1088;
4433 /* -w specifies a divisor for the window frame, otherwise uses 960x544 */
4434 /* TODO: better: automatically find which divisor will fit display */
4436 vw
.w
= framew
/ arg_wd
;
4437 vw
.h
= frameh
/ arg_wd
;
4440 fprintf(stdout
, "Picture %dx%d, aspect %3.2f, div %d, hints %dx%d\n",
4441 framew
, frameh
, aspectf
, arg_wd
, vw
.w
, vw
.h
);
4445 /* This is done after the X display has been initialized */
4446 /* initialize imlib and put the splash on screen. */
4449 x_init_imlib ( void )
4451 char ft
[256], *t0
, *t1
, *t2
, *t3
;
4452 int th
, y0
, y1
, y2
, y3
, x0
, x1
, x2
, x3
;
4454 if (0 == xinit
) return;
4456 if (0 != arg_xmsg
) WHOAMI
;
4458 memset(ft
, 0, sizeof(ft
));
4465 snprintf( t0
, 63, "%s %s-%s ", NAME
, VERSION
, LASTEDIT
);
4466 snprintf( t1
, 63, "%s by %s", COPYRIGHT
, EMAIL
);
4467 snprintf( t2
, 63, "This software is licensed to you under the ");
4468 snprintf( t3
, 63, "%s", LICENSE
);
4470 x_set_font( "fixed" );
4471 th
= x_fontline(0) + 2;
4473 /* compute half of text width as offset from centerline */
4474 x0
= (6 * strlen(t0
)) >> 1;
4475 x1
= (6 * strlen(t1
)) >> 1;
4476 x2
= (6 * strlen(t2
)) >> 1;
4477 x3
= (6 * strlen(t3
)) >> 1;
4485 xprintf(stdout
, "%s using imlib2\n", WHO
);
4487 /* low memory footprint: no cache for dynamic mpeg2 frames or banners */
4488 imlib_set_cache_size( 0 );
4489 imlib_set_font_cache_size( 0 );
4491 /* don't need it to be smoothly rendered for cutting, only fast */
4492 imlib_context_set_dither( 0 );
4493 imlib_context_set_anti_alias( 0 );
4494 imlib_context_set_blend( 0 );
4496 /* TESTME: will 16 bit visual on 24 bit display render faster? on 32 bit? */
4497 imlib_context_set_display( xdisplay
);
4498 imlib_context_set_visual( xvisual
);
4499 imlib_context_set_drawable( xwindow
);
4501 /* load generic banner */
4502 image
= imlib_load_image( PNG_PATH
"xtscut-begin.png" );
4503 if (NULL
!= image
) {
4504 imlib_context_set_image( image
);
4505 imlib_render_image_on_drawable_at_size(0, 0, vw
.w
, vw
.h
);
4509 xprintf(stdout
, "%s using imlib1\n", WHO
);
4510 idata
= Imlib_init( xdisplay
);
4511 if (NULL
!= idata
) {
4512 image
= Imlib_load_image( idata
, PNG_PATH
"xtscut-begin.png");
4513 if (NULL
!= image
) Imlib_apply_image( idata
, image
, xwindow
);
4517 /* draw the license on the X window */
4518 x_draw_text_line( COLOR_WHITE
, (vw
.w
>>1) - x0
, vw
.h
- y0
, t0
);
4519 x_draw_text_line( COLOR_WHITE
, (vw
.w
>>1) - x1
, vw
.h
- y1
, t1
);
4520 x_draw_text_line( COLOR_WHITE
, (vw
.w
>>1) - x2
, vw
.h
- y2
, t2
);
4521 x_draw_text_line( COLOR_WHITE
, (vw
.w
>>1) - x3
, vw
.h
- y3
, t3
);
4523 XSync( xdisplay
, True
); /* display it NOW */
4524 nanosleep( &splash_sleep
, NULL
);
4526 #endif /* !USE_LIBVO */
4528 /* connect to server, create and map window */
4531 x_init_display ( void )
4536 if (0 != arg_xmsg
) WHOAMI
;
4538 /* if the window already exists, return immediately */
4540 fprintf(stdout
, "X display already open!\n");
4544 /* setup X window hints for initial size and position */
4549 xhint
.height
= vw
.h
;
4551 xhint
.flags
= PPosition
| PSize
;
4553 /* set X window title text */
4554 filebase( n
, in_name
, F_TFILE
);
4555 snprintf( o
, sizeof(o
)-1, "%s.%s", n
, fxp
);
4557 /* stored in X structure, global xtitle isn't really needed */
4558 snprintf( xtitle
, sizeof(xtitle
)-1, NAME
" - %s", o
);
4560 /* environment variable DISPLAY will override above definition */
4561 if ( NULL
!= getenv("DISPLAY")) dispname
= getenv("DISPLAY");
4564 /******************************************************************** LIBVO */
4567 xoutput
= vo_x11_open(); /* XShm RGB as fast as imlib2 */
4570 xoutput
= vo_xv_open(); /* XVideo YV12 renders the quickest */
4573 xoutput
= vo_xv2_open(); /* XVideo YUYV as fast as imlib2 */
4579 if ( xoutput
->setup( xoutput
,
4580 m2seq
->width
, m2seq
->height
,
4581 m2seq
->chroma_width
, m2seq
->chroma_height
,
4584 fprintf(stdout
, "output setup failed\n");
4589 xprintf(stdout
, "LIBVO %s display open\n", votypes
[2+arg_vo
]);
4591 /* select the event types for normal operation */
4592 XSelectInput( xdisplay
,
4599 #warning using X Expose events
4604 XSetBackground( xdisplay
, xgc
, xbg
);
4605 XSetForeground( xdisplay
, xgc
, xfg
);
4607 x_set_font( "fixed" );
4613 /******************************************************************** LIBVO */
4616 /******************************************************************** Imlib */
4617 /* open the x display for imlib1/2 */
4618 xdisplay
= XOpenDisplay( dispname
);
4620 if (xdisplay
== NULL
) {
4621 perror("opening X display");
4625 /* get default screen number from XOpenDisplay */
4626 xscreen
= DefaultScreen( xdisplay
);
4627 xvisual
= DefaultVisual( xdisplay
, xscreen
);
4630 /* set foreground and background colors */
4631 xbg
= BlackPixel( xdisplay
, xscreen
);
4632 xfg
= WhitePixel( xdisplay
, xscreen
);
4634 XGetWindowAttributes( xdisplay
,
4635 DefaultRootWindow( xdisplay
),
4640 xprintf( stdout
, "X bpp %d\n", xbpp
);
4641 xcolors
= xcolors565
;
4642 if (24 == xbpp
) xcolors
= xcolors888
;
4643 if (32 == xbpp
) xcolors
= xcolors888
;
4645 /* find a matching visual for the display */
4646 XMatchVisualInfo( xdisplay
,
4653 xprintf( stdout
, "X visual %lx\n",vinfo
.visualid
);
4655 /* create the color map for the window */
4656 xcmap
= XCreateColormap( xdisplay
,
4657 RootWindow( xdisplay
, xscreen
),
4662 sattr
.background_pixmap
= None
;
4663 sattr
.background_pixel
= xbg
;
4664 sattr
.border_pixel
= xfg
;
4665 sattr
.colormap
= xcmap
;
4667 /* create window with background pixel, border pixel and colormap */
4675 /* create the window on the display */
4676 xwindow
= XCreateWindow( xdisplay
,
4677 RootWindow( xdisplay
, xscreen
),
4684 InputOutput
, /* CopyFromParent, */
4690 /* create a graphic context */
4691 xgc
= XCreateGC(xdisplay
, xwindow
, 0L, &xgcv
);
4693 /* NOTE: Select ConfigureNotify events before mapping window
4694 to parse Configure/Map events.
4697 /* select the event types for window setup */
4698 XSelectInput( xdisplay
,
4700 StructureNotifyMask
);
4702 /* put the window on the display */
4703 XMapWindow(xdisplay
, xwindow
);
4705 /* Wait for Map event to indicate the map finished. You can't use the window
4706 it until it has finished mapping. it will happen after 2 configure events,
4707 Configure size and Configure position.
4711 /* wait for window structure events in overlay window */
4712 XWindowEvent( xdisplay
,
4714 StructureNotifyMask
,
4717 /* looking for configure notify events to set size/position */
4718 if (xev
.type
== ConfigureNotify
)
4721 /* The Window Manager may change the actual dimension/position from our hints,
4722 so get where WM actually put window. That's why they're called 'hints'.
4724 xprintf(stdout
,"X Event:\tConfigureNotify\n");
4726 /* send_event indicates window moved, so udpate x,y */
4727 if (xev
.xconfigure
.send_event
) {
4729 /* FIXME: This is asking for trouble? Different WM's have other methods? */
4730 /* get the titlebar height */
4731 xtbh
= xev
.xconfigure
.y
- 1;
4733 vw
.x
= xev
.xconfigure
.x
;
4734 vw
.y
= xev
.xconfigure
.y
;
4735 vw
.w
= xev
.xconfigure
.width
;
4736 vw
.h
= xev
.xconfigure
.height
;
4739 "\tSend Event x%d y%d w%d h%d th%d\n",
4740 vw
.x
, vw
.y
, vw
.w
, vw
.h
, xtbh
);
4742 /* otherwise it's a window resize, so update w,h */
4743 vw
.w
= xev
.xconfigure
.width
;
4744 vw
.h
= xev
.xconfigure
.height
;
4746 xprintf(stdout
,"\tUser w%d h%d\n",
4750 /* keep checking events until MapNotify event says window is mapped */
4751 } while (xev
.type
!= MapNotify
);
4753 xprintf(stdout
,"X Event:\tMapNotify\n");
4755 XSetBackground( xdisplay
, xgc
, xbg
);
4756 XSetForeground( xdisplay
, xgc
, xfg
);
4758 x_set_font( "fixed" );
4760 /* show the results of what the overlay and X window were set to */
4761 xprintf(stdout
,"\n%s %0dx%0d @ %0d,%0d\n",
4762 WHO
, vw
.w
, vw
.h
, vw
.x
, vw
.y
);
4764 /* indicate the window is now initialized */
4769 /* Set the type of events we will allow in this screen:
4770 ConfigureNotify window setup
4771 UnmapNotify minimize/shutter
4772 MapNotify unminimize/unshutter
4773 KeyPress stream navigation and control
4774 ButtonPress1 place a cut
4775 ButtonPress2 preview a cut point
4776 ButtonPress3 jump to stream % (invisible slider)
4777 ButtonPress4 stream navigation backwards
4778 ButtonPress5 stream navigation forwards
4780 NOTE: Select ConfigureNotify events before mapping window
4781 to parse Configure/Map events.
4784 /* Undefine USE_EXPOSE for remote X session to reduce Expose event redraws.
4785 2 or 3 expose events with different serial numbers will pop up on move,
4786 along with a bunch of other expose events with the same serial number.
4787 FocusIn event will trigger redraw when Expose events are disabled.
4791 /* select the event types for normal operation */
4792 XSelectInput( xdisplay
,
4799 #warning using X Expose events
4804 /* set focus so xinput works when in fullscreen mode */
4805 XSetInputFocus( xdisplay
,
4813 fprintf(stdout
,"going full screen\n");
4818 /******************************************************************** Imlib */
4822 /* Scan 184 chars for GOP start code. Set the broken link if not closed. */
4823 /* This only works right if GOP doesn't straddle p packet boundary. */
4827 test_gop_set_broken ( unsigned char *r
, unsigned int z
, int n
)
4832 if (z
< 1) return 0;
4833 if (0 == arg_gop
) return ~0; /* pretend it's done if no -g option */
4838 for (i
=0; i
< z
; i
++) {
4844 if (0x00000100 != (0xFFFFFF00 & sc
)) continue;
4846 /* keep looping until first GOP found, or out of bytes */
4847 if (MPEG_GOP
!= b
) continue;
4849 /* enough bytes left in current packet to find the GOP data? */
4852 /* check for marker bit at 13th bit in 25 bit time code */
4853 if (0 == (8 & r
[1])) continue;
4855 /* Set broken_link if not a closed Group of Pictures. See 13818-2 Sect 6.3.8
4856 GOP broken_link on first GOP at start of cut to dump first B Pictures.
4857 GOP closed_gop on last GOP at end of cut to not request last B Pictures.
4860 /* if closed_gop set, no need to set broken_link on the closed GOP */
4861 if (0 == (0x40 & r
[3])) {
4864 "GOP broken @ pkt %d\n", n
);
4865 /* GOP broken_link set */
4870 iprintf(stdout
, "GOP closed @ pkt %d\n", n
);
4873 /* stop looping after first GOP found */
4877 /* GOP broken_link was not set */
4881 /* in_file and out_file already open, c is count of bytes, s is seek or -1 */
4884 write_block ( long long s
, long long c
)
4889 unsigned int skip_pkts
;
4892 if ( (s
< 0) || (c
< 1) ) return;
4894 if ((NULL
== es_buf
) || (NULL
== ts_buf
)) return;
4898 z
= lseek( in_file
, s
, SEEK_SET
);
4899 memset( psib
, 0, sizeof(psib
)); // reset PSI bit count list
4900 g
= 0; // reset GOP found test
4901 sc
= 0xFFFFFFFF; // reset GOP start code test
4908 "write will be short %lld bytes\n", c
% TSZ
);
4911 /* clear the payload start indicator flags before doing the cut */
4912 memset(psib
, 0, sizeof(psib
));
4914 j
= -1; /* packet counter */
4919 if ((c
- i
) < arg_mbz
) z
= c
- i
;
4922 /* 50MB w/ 4k arg_mbz */
4923 if ( (0 != arg_delay
) && (0 == (j
% 12894)) ) {
4924 dprintf(stdout
, "%s sleep\n", WHO
);
4925 nanosleep( &write_sleep
, NULL
);
4929 a
= read( in_file
, ts_buf
, z
);
4935 "error %d reading %s\n", a
, in_name
);
4944 dprintf( stdout
, "%s:%d EOB %s\n",
4945 WHO
, WHERE
, in_name
);
4949 /* ES cut writes last partial packet data, TS cut discards it */
4952 dprintf( stdout
, "%s:%d EOB %s\n",
4953 WHO
, WHERE
, in_name
);
4958 /* ES cut doesn't use TS sync and header bits, only copies the data */
4960 b
= write( out_file
, ts_buf
, a
);
4964 "error %d writing %s\n",
4974 b
= write( out_file
, ts_buf
, a
);
4977 fprintf( stdout
, "error %d writing %s\n", b
, out_name
);
4986 #warning using extra TS processing (slower)
4987 /* TS processing to remove initial PSI0 cruft, sets first GOP broken_link. */
4988 if ((0 == arg_nulls
) || (0 != arg_gop
)) {
4993 for (k
= 0; k
< a
; k
+= TSZ
) {
4995 n
= ((j
* arg_mbz
) + k
) / TSZ
;
4997 x
= parse_ts_header( r
, h
, 0 );
4999 fprintf(stdout
,"ERROR: Transport Sync Lost\n");
5003 #ifdef USE_NULL_STRIP
5004 /* skip NULL packets for smaller output files, -n enables NULL packets */
5006 if (MPEG_NULL
== h
->pid
)
5010 /* store count of payload start indicators by pid */
5011 psib
[ h
->pid
] += h
->psi
;
5013 /* NOTE: this should also be a generic fix to wait for start of any payload */
5014 /* Audio fix: if no payload start indicators for audio PID yet, skip packet */
5015 if (h
->pid
== arg_apid
) {
5016 if (0 == psib
[ h
->pid
]) {
5021 /* write the packet as a NULL packet to match show scuts file sizes */
5022 build_null_packet( r
);
5026 #ifdef USE_GOP_BROKEN_LINK
5027 #warning using GOP broken link test
5028 /* -g, video pid, no gop found yet, and has video payload bytes? */
5030 if ( (h
->pid
== arg_vpid
)
5033 g
= test_gop_set_broken( r
+ h
->pso
, x
, n
);
5036 #warning using one-packet write (slower)
5037 b
= write( out_file
, r
, TSZ
);
5041 "ERROR: writing %s, out%d != in%d\n",
5052 // fprintf(stdout, "skip_pkts %d\n", skip_pkts);
5055 Add an audio packet scavenger to write audio packets until
5056 first audio PSI1 after the end of the cut. This might cause
5057 some interesting, and/or bad, audio artifacting if the cut
5058 isn't done in the middle of the silent part of the fade out.
5062 /* Save the list of cuts to input base name + .[esc|tsc] */
5065 write_cutfile ( void )
5068 char n
[256], sc_name
[512];
5071 if (0 != arg_cut
) return;
5073 /* atscap 1.1rc9t fixed to handle the zero byte .tsc index.html status */
5074 if (0 == scut_idx
) return;
5079 filebase( n
, in_name
, F_PFILE
); /* keep path to keep .tsc w/ .ts */
5080 snprintf( sc_name
, sizeof(sc_name
)-1, "%s.%sc", n
, fxp
);
5081 dprintf( stdout
, "Write %d cuts to %s\n", scut_idx
-1, sc_name
);
5084 /* save cuts for next time */
5085 o
= fopen( sc_name
, "w" );
5087 fprintf( stdout
, "error opening %s\n", sc_name
);
5092 ok
= fwrite( &scuts
[1], sizeof(int), scut_idx
-1, o
);
5093 if (ok
!= (scut_idx
-1)) {
5094 fprintf( stdout
, "only wrote %d of %d to %s\n",
5095 ok
, scut_idx
, sc_name
);
5097 dprintf( stdout
, "wrote %d cuts to %s\n", ok
, sc_name
);
5103 /* [c] key removes all cuts */
5106 clear_scuts ( void )
5113 // for (i = 0; i < iframe_idx; i++) frames[ iframes[ i ] ].cut = 0;
5114 for (i
= 0; i
< frame_idx
; i
++) frames
[i
].cut
= 0;
5118 /* write PAT + PMT to filedes f, but only if input scan found anything */
5121 write_patpmt ( void )
5126 if (0x47 != *pat
) return;
5128 t
= write( out_file
, pat
, TSZ
);
5131 if (0x47 != *pmt
) return;
5132 if (pmt_pkts
< 1) return;
5134 t
= write( out_file
, pmt
, TSZ
* pmt_pkts
);
5139 /* Write one second of nulls to start of file at current byte rate,
5140 after PAT + PMT and before video start to give decoder some slack time.
5141 1s of NULLs based on bitrate, max will be about 10MB at 80Mbit.
5143 This is an attempt to deal with xine sync issue on uncached stream start,
5144 but it may not be enough to correct the problem. Tried this before?
5148 write_nulls ( void )
5150 char pktnull
[ TSZ
];
5153 if (0 == arg_nulls
) return;
5157 /* use last parsed SEQ header bit rate */
5158 calc_null_packets();
5160 if (0 == null_pkts
) return;
5164 if (0 != arg_fmsg
) fprintf(stdout
, "%s packets %d\n", WHO
, j
);
5166 build_null_packet( pktnull
);
5167 for (i
= 0; i
< j
; i
++) {
5169 /* FIXME: add error check here */
5170 t
= write( out_file
, pktnull
, TSZ
);
5177 /* create incremental filename (-1 thru -9) in d from basename in s */
5180 build_next_name ( char *d
, char *s
, int z
)
5188 memset( t
, 0, sizeof(t
) );
5191 for (i
= 0; i
< 9; i
++) {
5192 snprintf( d
, z
, "%s-%d", s
, i
);
5193 snprintf( t
, sizeof(t
)-1, "%s%s.%s", arg_path
, d
, fxp
);
5194 ok
= stat64( t
, &fs
);
5196 fprintf( stdout
, "Found %s\n", t
);
5200 snprintf( d
, z
, "%s-x", s
);
5201 snprintf( t
, sizeof(t
)-1, "%s%s.%s", arg_path
, d
, fxp
);
5202 fprintf( stdout
, "Gave up, using %s.%s\n", t
);
5207 /* build output basename, s is source basename, c is cut number */
5208 /* writing to one file truncates the input name to first . */
5209 /* writing to multiple files appends .nn */
5210 /* arg base was set by input filename or -o option */
5213 build_outname ( char *s
, int c
)
5221 snprintf(out_base
, sizeof(out_base
)-1, "%s", arg_base
);
5224 /* no -o option given uses source basename + ".NN.ts" */
5225 snprintf(out_base
, sizeof(out_base
)-1, "%s.%02d", s
, c
);
5227 snprintf(out_name
, sizeof(out_name
)-1, "%s%s.%s",
5228 arg_path
, out_base
, fxp
);
5230 ok
= stat64( out_name
, &fs
);
5231 if (0 != ok
) return 0;
5233 /* FIXME: This isn't enough. It could have an unintended inode collision on
5234 another filesystem, indicating an error where no error actually exists.
5235 It first needs to check if input and output are different file systems.
5237 out_inode
= fs
.st_ino
;
5238 if (in_inode
== out_inode
) {
5239 fprintf(stdout
, "ABORT: input %s same as output\n", in_name
);
5246 write sequence cuts from scuts array
5248 if cut type is EVEN or ODD (and option -1 not used):
5250 open file.00.ts and write all cuts to that file
5252 if cut type is EVEN [or ODD] (and option -1 is used):
5254 open file.00.ts and write all cuts to that file
5255 generate a new file every time sequences[ scuts[] ] boundary crossed
5256 open file.00.ts and write PATs and PMTs, then
5257 file.02[or 01].ts and write sequences of first EVEN [or ODD] cut
5258 file.04[or 03].ts and write sequences of next EVEN [or ODD] cut
5262 file.nn.ts and write sequences of last EVEN [or ODD] cut
5264 NOTE: The example above shows an even cut write. You may use the
5265 -r option for sequential numbering starting at 01.
5267 if cut type is ALL (and option -1 used), cuts write to:
5269 generate a new file every time sequences[ scuts[] ] boundary crossed
5270 open file.00.ts and write PATs and PMTs, then
5271 file.01.ts and write sequence of first cut until cut boundary
5272 file.02.ts and write sequence of second cut until cut boundary
5275 file.nnn.ts and write from last cut to last sequence.
5278 [write two packets [and null packets?], PAT and PMT, to start of file]
5283 write_scuts ( void )
5285 char n
[256]; /* file based name */
5286 unsigned int in
, out
;
5287 unsigned long long bytes_cut
, cpt
;
5288 int ok
, i
, j
, k
, cut_num
;
5289 char *t
, *p
; /* text pointers */
5290 double et
, tp
; /* elapsed time, through put */
5296 if ( 0 == scut_idx
) return;
5297 start_timer( &timer_cut
);
5301 filebase( n
, in_name
, F_PFILE
);
5307 z
= sizeof(outlist
);
5310 if (CUT_EVEN
== cut_type
) t
= "EVEN";
5311 if (CUT_ODD
== cut_type
) t
= "ODD";
5312 if (CUT_ALL
== cut_type
) t
= "ALL";
5314 snprintf( in_name
, sizeof(in_name
)-1, "%s.%s", n
, fxp
);
5315 p
= strrchr( in_name
, '/' );
5322 asnprintf(d
, z
, "Writing %s cuts From %s:\n", t
, p
);
5323 asnprintf(d
, z
, " To: %s\n", arg_path
);
5326 asnprintf(d
, z
, "Cuts will be renumbered sequentially\n");
5331 /* reset bytes counts and input/output block indices */
5336 /* cut type ALL first cut is file.00.ts, has multiple PATs and PMTs */
5337 /* cut type EVEN/ODD all cuts go to file.00.ts */
5339 filebase( n
, in_name
, F_TFILE
); /* write cuts to current dir */
5340 memset( out_base
, 0, sizeof(out_base
));
5342 /* term at first . to keep date and time in filename */
5343 p
= strchr( n
, '.' );
5344 if (NULL
!=p
) *p
= 0;
5346 strncpy( out_base
, n
, sizeof(out_base
)-1 );
5348 ok
= build_outname( n
, i
);
5351 fprintf(stdout
, "ERROR: Rename the input file\n");
5355 asnprintf(d
, z
, "Cut # SEQ # byte offset file size output name\n");
5356 asnprintf(d
, z
, "----- ------ ----------- ----------- -----------\n");
5360 out_file
= open( out_name
, FILE_WMODE
, FILE_PERMS
);
5362 fprintf( stdout
, "\n%s open %s error\n", WHO
, out_name
);
5371 /* counter-intuitive: cut 000 is not in scuts[]. cut 001 is scuts[0] */
5372 if (CUT_ALL
!= cut_type
) {
5374 if (CUT_EVEN
== cut_type
) {
5381 x_draw_quad_gradientv(COLOR_RED
, 0, 0, vw
.w
, vw
.h
, 0.5 );
5385 for ( i
= j
; i
< scut_idx
-1; i
+= k
) {
5388 /* -r option will renumber odd and even cuts to sequential starting at 01,
5389 but only if writing multiple files and only if not cutting all
5391 if ( (0 != arg_renum
) && (0 == arg_one
)
5392 && ( (CUT_ODD
== cut_type
) || (CUT_EVEN
== cut_type
)) ) {
5393 if (CUT_ODD
== cut_type
) cut_num
++;
5395 dprintf( stdout
, "cut %d renumbered to %d\n",
5399 /* -1 option is do not make one file, open a new one for each cut */
5402 if (out_file
> 2) close( out_file
);
5404 build_outname( n
, cut_num
+ 1 );
5405 if (in_inode
== out_inode
) return;
5407 out_file
= open( out_name
, FILE_WMODE
, FILE_PERMS
);
5409 fprintf( stdout
, "\n%s open %s error\n",
5416 bytes_cut
= sequences
[scuts
[ i
+ 1 ]] - sequences
[scuts
[ i
]];
5419 bytes_cut
= in_size
- sequences
[ scuts
[ i
] ];
5421 cpt
= TSZ
* (1 + pmt_pkts
);
5422 cpt
+= TSZ
* null_pkts
;
5424 p
= strrchr(out_name
, '/');
5430 asnprintf(d
, z
, "%3d @ %6d %11lld %11lld %s\n",
5431 i
+1, scuts
[i
], sequences
[scuts
[i
]], bytes_cut
+cpt
, p
);
5433 dprintf(stdout
, "%3d @ %6d %11lld %11lld %s\n",
5434 i
+1, scuts
[i
], sequences
[scuts
[i
]], bytes_cut
+cpt
, p
);
5442 /* write any PAT + PMT found by input scan and optional NULL packets */
5445 write_block( sequences
[scuts
[ i
]], bytes_cut
);
5452 bytes_total
= bytes_in
+ bytes_out
;
5454 stop_timer( &timer_cut
);
5455 diff_timer( &timer_cut
);
5457 et
= 0.000000001 * (double)timer_cut
.diff
.tv_nsec
;
5458 et
+= 1.0 * (double)timer_cut
.diff
.tv_sec
;
5460 asnprintf(d
, z
, "Cut reads/writes %lld MegaBytes in %.2f seconds, ",
5461 bytes_total
>> 20, et
);
5464 asnprintf(d
, z
, "\nInput %11lld", bytes_in
);
5465 asnprintf(d
, z
, "\nOutput %11lld", bytes_out
);
5466 asnprintf(d
, z
, "\nTotal %11lld, ", bytes_total
);
5469 tp
= (double)bytes_total
/ et
;
5472 asnprintf(d
, z
, "I/O MB/s %.2f\n", tp
);
5476 x_draw_quad_gradientv(COLOR_GREEN
, 0, 0, vw
.w
, vw
.h
, 0.5 );
5482 /* find I-frame before current frame that has cut set, or iframe 0 */
5485 find_prev_cut ( void )
5491 for (i
= (xcfd
- 1); i
> 0; i
-- )
5492 if (0 != frames
[i
].cut
)
5498 /* find I-frame after current frame that has cut set */
5501 find_next_cut ( void )
5507 for (i
= (xcfd
+ 1); i
< frame_idx
; i
++ )
5508 if (0 != frames
[i
].cut
)
5514 /* return nz if bx,by is within quadrangle defined by x,y,w,h */
5517 x_button_quad ( int x
, int y
, int w
, int h
, int bx
, int by
)
5519 if (0 != arg_xmsg
) WHOAMI
;
5521 if ( (bx
>= x
) && (bx
<= (x
+w
)) )
5522 if ( (by
>= y
) && (by
<= y
+h
) )
5528 /* check for the following events:
5529 resize/move = update window
5530 iconify/restore = disable/enable window
5531 keypress = user navigation and control
5532 maximize on/off (broken)
5534 xumove is set if start frame offset changes
5535 xredraw is set if display needs to be redrawn
5537 caller handles redrawing display in X scan loop
5547 // if (0 != arg_xmsg) WHOAMI;
5551 coffset
= xoffset
; /* current offset */
5553 /* look for UnmapNotify for minimized/disable overlay */
5554 if ( x_check_twe( UnmapNotify
, &xev
) )
5555 xprintf(stdout
,"\nX Event:\tUnmapNotifyEvent");
5557 /* look for ConfigureNotify for resize or move */
5558 if ( x_check_twe( ConfigureNotify
, &xev
))
5560 xprintf(stdout
,"\nX Event:\tConfigureNotify\n");
5562 /* ConfigureNotify event indicates size/position change */
5563 if (xev
.type
== ConfigureNotify
)
5566 /* send_event indicates window manager move/resize,
5567 so udpate w,h,x,y with new location */
5569 if (xev
.xconfigure
.send_event
) {
5570 vw
.x
= xev
.xconfigure
.x
;
5571 vw
.y
= xev
.xconfigure
.y
;
5573 vw.w = xev.xconfigure.height;
5575 vw
.w
= xev
.xconfigure
.width
;
5576 vw
.h
= xev
.xconfigure
.height
;
5578 xprintf(stdout
,"\tSend Event x%d y%d w%d h%d\n",
5579 vw
.x
, vw
.y
, vw
.w
, vw
.h
);
5581 /* otherwise it's a user resize, so update w,h */
5582 vw
.w
= xev
.xconfigure
.width
;
5584 vw.w = xev.xconfigure.height;
5586 vw
.h
= xev
.xconfigure
.height
;
5588 xprintf(stdout
,"\tUser w%d h%d\n",
5594 // x_clear_display();
5596 /* eat the FocusIn after ConfigureNotify */
5597 x_check_twe( FocusIn
, &xev
);
5602 /* it's ordered like this so the ConfigureNotify above puts overlay
5603 back in right location BEFORE enabling it. looks smoother.
5604 NOTE: from old mzplay.c; leave this comment in place, in case want
5605 to use overlay instead of rgb colorspace for the preview window
5608 /* look for MapNotify for maximized/enable overlay */
5609 if ( x_check_twe( MapNotify
, &xev
) ) {
5610 xprintf(stdout
,"\nX Event:\tMapNotify\n");
5614 /* FocusOut does nothing except empty the queue of the focus out event */
5615 if ( x_check_twe( FocusOut
, &xev
) ) {
5616 xprintf( stdout
, "\nX Event:\tFocusOut\n");
5618 /* but should instead copy the image to backstore for later Expose events */
5621 /* FocusIn needs to eat the following ButtonPress event so it doesn't pass
5622 through. Also, this should go before the ButtonPress event test.
5624 if ( x_check_twe( FocusIn
, &xev
) ) {
5625 xprintf( stdout
, "\nX Event:\tFocusIn\n");
5627 x_check_twe( ButtonPress
, &xev
);
5630 /* look for button events */
5631 while ( x_check_twe( ButtonPress
, &xev
) ) {
5635 xprintf(stdout
,"\nX Event:\tButtonPress button %d\n",
5636 xev
.xbutton
.button
);
5638 /* check for scrollwheel down, forward in stream vww/2 frames */
5639 if (5 == xev
.xbutton
.button
) {
5640 xprintf(stdout
,"\nX Event:\tScrollDown ButtonPress\n");
5641 uoffset
= 1 * (vw
.w
>> 1);
5645 /* check for scrollwheel up, backward in stream vww/2 frames */
5646 if (4 == xev
.xbutton
.button
) {
5647 xprintf(stdout
,"\nX Event:\tScrollUp ButtonPress\n");
5648 uoffset
= -1 * (vw
.w
>> 1);
5652 /* Right Mouse Button is button 3 and performs an I-frame preview */
5653 if (3 == xev
.xbutton
.button
) {
5655 i
= x_button_quad(bax
, bay
, baw
, bah
, bx
, by
);
5657 "\nX Event:\tRight ButtonPress x %d y %d, i %d\n",
5661 /* not in IPB area, is preview select */
5664 /* if user selection is within valid frame range */
5665 xlfd
= find_nearest_iframe( xoffset
+ bx
, WHO
);
5668 sequences
[frames
[xlfd
].num
], WHO
);
5675 /* check middle mouse button for percentage position in sequence list. */
5676 if (2 == xev
.xbutton
.button
) {
5678 /* no need if all frames fit in display window, use RMB button3 instead */
5679 if ((frame_idx
> vw
.w
) && (vw
.w
> 1)) {
5680 // i = bx * iframe_idx;
5683 // j = iframes[i] - (vw.w>>1);
5685 xoffset
= find_next_iframe( j
);
5687 iprintf( stdout
, "Position near I-frame %d\n", i
);
5692 /* Left Mouse Button is button 1, toggles cut select or legend area buttons */
5693 if (1 == xev
.xbutton
.button
) {
5695 /* test for time-code/frame-number toggle area, same x as I button uses */
5696 i
= x_button_quad( bxi
, tys
, 30, 12, bx
, by
);
5698 arg_ft
= 1 & ~arg_ft
;
5704 /* test for IPB button area */
5705 i
= x_button_quad(bax
, bay
, baw
, bah
, bx
, by
);
5707 "\nX Event:\tLeft ButtonPress x %d y %d i %d\n",
5711 /* no buttons to trigger with legend buttons disabled */
5712 if (0 == arg_lb
) i
= 0;
5714 /* not in IPB buttons, or IPB buttons disabled, is cut select */
5717 ni
= find_nearest_iframe( xoffset
+ bx
, WHO
);
5721 /* disable frame 0 cut selection, otherwise toggle cut indication */
5723 frames
[ ni
].cut
= 1 & ~frames
[ ni
].cut
;
5724 build_scut_frames();
5730 /* test for IPB button toggle areas */
5731 i
= x_button_quad( bxi
, bys
, 9, byh
, bx
, by
);
5738 i
= x_button_quad( bxp
+1, bys
, 8, byh
, bx
, by
);
5745 i
= x_button_quad( bxb
+1, bys
, 9, byh
, bx
, by
);
5753 if ((0 != bis
) || (0 != bps
) || (0 != bbs
))
5757 break; /* only one loop */
5761 /* look for events to indicate screen needs redraw */
5762 if ( x_check_twe( Expose
, &xev
)) {
5763 XExposeEvent
*expose
= (XExposeEvent
*)&xev
;
5764 xprintf( stdout
, "X Event:\tExpose type %d %s\n",
5765 xev
.type
, xevtypes
[xev
.type
]);
5767 "\t Serial %lu Send %s x %d y %d w %d h %d c %d\n",
5769 (0 == expose
->send_event
) ? "FALSE":"TRUE",
5776 /* Wait for serial number to change in case dragging window because
5777 WindowMaker is set to draw x,y in middle of window and this causes
5778 the multiple Expose events that pile up in the queue.
5780 Now you might get two or three re-draws on move with the same serial number,
5781 but shouldn't be any more. There can be dozens of redraws for certain areas
5782 of the screen, but they generally have the same serial number and can be
5783 ignored because partial redraws will not be done any time soon.
5785 It entails keeping a copy of the last drawn frame + lines, using bits and
5786 pieces of that to refresh the parts the Expose event wants to refresh.
5788 For local, the 3 redraws are acceptable. For remote, 1 redraw is enough.
5790 Redraw on FocusIn may actually be all it needs to get it done.
5792 if (expose
->serial
!= expose_serial
) {
5793 if (0 == xredraw
) xredraw
= ~0;
5795 xprintf( stdout
, "X Event:\tExpose type %d %s\n",
5796 xev
.type
, xevtypes
[xev
.type
] );
5798 "\t Serial %lu Send %s x %d y %d w %d h %d c %d\n",
5800 (0 == expose
->send_event
) ? "FALSE":"TRUE",
5808 expose_serial
= expose
->serial
;
5812 /* look for keypress events */
5813 if ( x_check_twe( KeyPress
, &xev
) )
5817 XComposeStatus compose
;
5819 unsigned short keyval
;
5822 /* try to convert key event into ASCII */
5823 keylen
= XLookupString( (XKeyEvent
*)&xev
,
5824 keybuf
, keymax
, &keysym
, &compose
);
5829 /* NOTE: ADD MORE KEYS HERE, OR REMOVE UNUSED ONES :> */
5832 /* check for special keys and use negative value for testing below */
5833 // xprintf(stdout, "X KeySym %04lX\n", 0xFFFF & keysym );
5834 keyval
= 0xFFFF & keysym
;
5839 "\nX Event:\tKeyPress %04X %d\n", keyval
, keyval
);
5843 /*********************************************************** stream control */
5846 /* alpha control, only works for imlib2? */
5850 if (hist_scale
<= 0.0) {
5852 iprintf(stdout
, "Histogram scale at 0%%\n");
5859 if (hist_scale
>= 1.0) {
5861 iprintf(stdout
, "Histogram scale at 100%%\n");
5866 /* toggle single or multi-file write */
5868 arg_one
= 1 & ~arg_one
;
5869 fprintf(stdout
, "Writing cuts to %s\n",
5870 (arg_one
)?"one file":"multiple files");
5875 /* toggle single or multi-file write */
5877 arg_renum
= 1 & ~arg_renum
;
5878 fprintf(stdout
, "Cut renumber: %s\n",
5879 (arg_renum
)?"YES":"NO");
5884 /* NULL packet at start of each cut toggle */
5886 arg_nulls
= 1 & ~arg_nulls
;
5887 fprintf(stdout
, "NULL packets: %s\n",
5888 (arg_nulls
)?"YES":"NO");
5893 /* clear all cuts */
5900 /* write all cuts to separate files */
5907 /* write even cuts, [e] or [ENTER] keys (enter is CR not NL in X) */
5910 cut_type
= CUT_EVEN
;
5915 /* write odd cuts */
5922 /* display cuts to stdout */
5925 fprintf(stdout
, "xhelp %d\n", xhelp
);
5932 write_cutfile(); // redundant with write at cut toggle
5935 /* display allocated memory usage */
5937 arg_gop
= 1 & ~arg_gop
;
5938 fprintf(stdout
, "GOP broken_link %s\n",
5939 (arg_gop
)?"YES":"NO");
5944 /* show current config */
5946 // show_keys(0); // TODO: move to console scan
5953 /* List allocations */
5959 -66 F1 toggles hide histogram lines
5960 -65 F2 toggles hide IPB legend and buttons
5961 -64 F2 toggles hide timecodes at top
5962 -63 F3 toggles hide visual position slider bead
5965 /* F1 toggles histogram lines */
5967 arg_hl
= 1 & ~arg_hl
;
5972 /* F2 toggles IPB legend and buttons */
5974 arg_lb
= 1 & ~arg_lb
;
5979 /* F3 toggles timecode at top */
5981 arg_tc
= 1 & ~arg_tc
;
5986 /* F4 toggles slider bar */
5988 arg_sb
= 1 & ~arg_sb
;
5993 /* F5 toggles eye candy, cornering, shading */
5995 arg_ec
= 1 & ~arg_ec
;
6000 /* toggle frame number/time-code display */
6002 arg_ft
= 1 & ~arg_ft
;
6007 /* quit. NOTE: does not save cuts in case you hit [c]lear cuts by accident */
6014 /* ESC forces redraw of display */
6020 /* jump to previous cut */
6022 i
= find_prev_cut();
6023 // fprintf(stdout, "find prev cut %d\n", i);
6025 xoffset
= i
- (vw
.w
>> 1);
6030 /* jump to next cut */
6032 i
= find_next_cut();
6033 // fprintf(stdout, "find next cut %d\n", i);
6035 xoffset
= i
- (vw
.w
>> 1);
6041 /* jump to previous i-frame if below last button3 preview packet count */
6043 for (i
=xoffset
; i
>= 0; i
--) {
6044 if (frames
[i
].pct
!= 1) continue;
6045 if (frames
[i
].fpc
> frame_threshold
) continue;
6046 xoffset
= i
- (vw
.w
>>1);
6051 /* jump to next i-frame if below last button3 preview packet count */
6053 for (i
=xoffset
; i
< frame_idx
; i
++) {
6054 if (frames
[i
].pct
!= 1) continue;
6055 if (frames
[i
].fpc
> frame_threshold
) continue;
6056 xoffset
= i
- (vw
.w
>>1);
6063 /******************************************************* stream navigation */
6064 /* jump to start of stream */
6072 /* jump to end of stream */
6077 xoffset
= frame_idx
- vw
.w
;
6083 uoffset
= -2 * vw
.w
;
6089 case XK_KP_Page_Down
:
6094 /* skip greater than or equal to window width */
6098 uoffset
= -1 * vw
.w
;
6109 /* Skip to previous or next sequence, I-frame single-step.
6110 This may be very slow on certain I-frame heavy FOX broadcasts.
6115 i
= find_prev_iframe( xlfd
);
6117 iprintf(stdout
, "lfd %d i %d u %d\n", xlfd
, i
, uoffset
);
6118 if (uoffset
< 0) xumove
= ~0;
6124 i
= find_next_iframe( xlfd
);
6126 iprintf(stdout
, "lfd %d i %d u %d\n", xlfd
, i
, uoffset
);
6127 if (uoffset
> 0) xumove
= ~0;
6131 /* FIXME: full screen toggle, has eventual fatal bug in X events,
6132 and the aspect ratio is really ugly except on 1920x1200 displays.
6151 /* viewpoint move request? */
6154 toffset
= xoffset
+ uoffset
; /* target offset */
6156 /* is frame count less than window width? */
6157 if (frame_idx
<= vw
.w
) {
6159 /* yes, xoffset will stay at 0 */
6162 /* no, clamp xoffset to last window width frames, plus 1 */
6164 if (toffset
< 0) toffset
= 0;
6166 if ((toffset
+ vw
.w
) > frame_idx
) {
6167 xoffset
= frame_idx
- vw
.w
;
6173 /* no change in offset disables redraw */
6174 if (coffset
== xoffset
) xredraw
= 0;
6178 if (0 != save
) save_config();
6180 /* any events left on the queue means a mistake was made above? */
6181 //#define USE_X_DEBUG
6183 #warning Using X debug code
6184 /* debug. don't leave this in because it empties the X event queue. */
6185 if ( XCheckWindowEvent( xdisplay
, xwindow
, 0x01FFFFFF, &xev
)) {
6187 if (xev
.type
< 36) xet
= xevtypes
[xev
.type
];
6188 xprintf( stdout
, "\nX Event type %d not handled: %s\n",
6195 /**************************************************** xterm console control */
6198 console_scan( unsigned char *k
)
6201 console_getch_fn( k
);
6202 if (0 == *k
) return;
6206 /* stream control, no navigation keys */
6209 /* toggle single or multi-file write */
6211 arg_one
= 1 & ~arg_one
;
6212 fprintf(stdout
, "Writing cuts to %s\n",
6213 (arg_one
)?"one file":"multiple files");
6216 /* toggle NULL blank padding packets at start of each cut */
6218 arg_nulls
= 1 & ~arg_nulls
;
6219 fprintf(stdout
, "Writing NULL blanks to cuts: %s\n",
6220 (arg_nulls
)?"YES":"NO");
6223 /* clear all cuts */
6230 /* write all cuts to separate files */
6238 /* write even cuts, [e] or [ENTER] keys */
6241 cut_type
= CUT_EVEN
;
6246 /* write odd cuts */
6253 /* display cuts to stdout */
6260 /* toggle frame number/time-code display */
6262 arg_ft
= 1 & ~arg_ft
;
6269 /* goes to c_exit after fixing console */
6273 fprintf(stdout
, "Key 0x%02X not handled\n", *k
);
6277 /***************************************************** xterm console control */
6281 /* fill out frames[].num from input data */
6284 process_frames ( void )
6286 int k
, n
, i
, p
, b
, s
, h
;
6287 double ft
, fi
, fp
, fb
;
6292 if (frame_idx
< 2) return;
6294 i
= p
= b
= h
= s
= 0;
6296 for (n
= 0; n
< frame_idx
; n
++) {
6302 f
->fpc
= e
->vpn
- f
->vpn
;
6304 /* last frame in list is always wrong even if it's OK */
6305 if ( f
->fpc
< 0 ) f
->fpc
= 0;
6309 if (h
< f
->fpc
) h
= f
->fpc
;
6327 /* -1 to not count the fake I frame placeholder at the end */
6328 ft
= (double) frame_idx
- 1;
6329 fi
= (100.0 * (double) (i
-1)) / ft
;
6330 fp
= (100.0 * (double) p
) / ft
;
6331 fb
= (100.0 * (double) b
) / ft
;
6333 fprintf( stdout
, "\nFrame totals %d: "
6334 "I %d (%4.2f%%), P %d (%4.2f%%), B %d (%4.2f%%)\n\n",
6335 frame_idx
-1, i
-1, fi
, p
, fp
, b
, fb
);
6337 // fprintf( stdout, "I-frame packets, high %d avg %d\n", h, s / i );
6339 // hist_hpn = s / (i-1);
6342 /* output frame sequence file from previous input file */
6345 output_frame_sequence ( void )
6348 char n
[256], fs_name
[ 256 ];
6350 struct in_frame_s in_frame
;
6355 filebase( n
, in_name
, F_PFILE
);
6356 memset( fs_name
, 0, sizeof(fs_name
) );
6357 snprintf( fs_name
, sizeof(fs_name
)-1, "%s.%sx", n
, fxp
);
6358 dprintf( stdout
, "Writing %s\n", fs_name
);
6360 f
= fopen( fs_name
, "wb" );
6362 for (i
= 0; i
< sequence_idx
; i
++) {
6363 in_seq
= sequences
[i
];
6364 ok
= fwrite( &in_seq
, sizeof(in_seq
), 1, f
);
6367 ok
= fwrite( &in_seq
, sizeof(in_seq
), 1, f
);
6369 for (i
= 0; i
<frame_idx
; i
++) {
6370 in_frame
.pct
= frames
[i
].pct
;
6371 in_frame
.vpn
= frames
[i
].vpn
;
6372 ok
= fwrite( &in_frame
, sizeof(struct in_frame_s
), 1, f
);
6377 /* Build .esx file for Elementary Stream
6378 specified with -y option (should be .es extension specifies it?) */
6385 unsigned char t
, *p
, c
;
6386 long long o
, r
, s
, z
;
6392 /* allocate mpeg2 ES buffer */
6394 if (NULL
== es_buf
) {
6395 fprintf(stdout
, "%s es_buf not allocated\n", WHO
);
6399 memset(ipbx
, 0, sizeof(ipbx
));
6413 /* reset at start of stream only */
6416 /* MPEG ES stream has 00 00 01 for Start Code.
6417 Next byte is table type
6419 00 Start of Picture (with picture type)
6420 Don't need to track it much further than that.
6423 bc
= read( in_file
, es_buf
, arg_mbz
);
6425 dprintf( stdout
, "\n%s EOF %s\n", WHO
, in_name
);
6428 if (arg_mbz
!= bc
) {
6429 dprintf( stdout
, "\n%s EOF %s\n", WHO
, in_name
);
6430 // no, let it finish the last bit of parsing */
6435 while (p
< (es_buf
+ bc
))
6442 /* MPEG Start Code? */
6443 if (0x00000100 != (0xFFFFFF00 & b
)) continue;
6446 // fprintf(stdout, "1%02X @ %9llX p %02X\n", c, o -4, (p - es_buf));
6449 /* bunch of branch prediction performance hits for no good reason */
6450 /* reset start code test after each start code: NO */
6453 /* Skip MPEG 0xE0-0xEF PES header */
6454 if (MPEG_PES
== (0xF0 & c
)) continue;
6456 /* Skip ATSC User Data header */
6457 if (MPEG_UPS
== c
) continue;
6459 /* Skip MPEG2 Sequence or Picture Extensions */
6460 if (MPEG_EXT
== c
) continue;
6462 /* Skip MPEG Slices */
6463 if ((c
> 0x01) && (c
< 0xB0)) continue;
6466 /* MPEG Sequence Start? */
6467 if (MPEG_SEQ
== c
) {
6470 fprintf(stderr
, "\r%s %2lld%% ", WHO
, s
);
6472 "Frames %06d SEQ %06d I %06d P %06d B %06d",
6473 frame_idx
, sequence_idx
,
6474 ipbx
[1], ipbx
[2], ipbx
[3]);
6477 // fprintf( stdout, "1%02X @ %09llX SEQ #%d\n", c, o - 4, ss++ );
6478 sequences
[sequence_idx
++] = o
- 4;
6480 snprintf(n
, sizeof(n
), "sequences[%d]", sequence_idx
);
6481 sequences
= irealloc(sequences
, sizeof(long long), n
);
6483 f
= &frames
[frame_idx
];
6489 snprintf(n
, sizeof(n
), "frames[%d]", frame_idx
);
6490 frames
= irealloc(frames
, sizeof(frame_t
), n
);
6495 /* MPEG Picture Start? */
6496 if (MPEG_PIC
== c
) {
6498 /* Can't get picture_coding_type if start code is at end of packet,
6499 or at end of packet -1. End of packet -2 is ok.
6501 /* Need 2 bytes after Picture Start code for picture_coding_type */
6502 if ( (p
+1) >= (es_buf
+ arg_mbz
) ) {
6504 /* Which is why es_buf has + 2 bytes */
6505 // fprintf(stdout,"2 byte adjust\n");
6506 bd
= read( in_file
, es_buf
+ arg_mbz
, 2 );
6513 if ((t
> 1) && (t
< 4)) {
6514 f
= &frames
[frame_idx
];
6520 snprintf(n
, sizeof(n
), "frames[%d]", frame_idx
);
6521 frames
= irealloc(frames
, sizeof(frame_t
), n
);
6525 /* Undefine PIC_DEBUG for B & P parsing errors. It won't affect cutting. */
6529 "\nBad PIC type %d @ %9llX "
6530 "es_buf[0x%02X] sc %08X:\n",
6531 t
, o
, (int) (p
- es_buf
), b
);
6532 dump_packet( o
, es_buf
, TSZ
, p
- es_buf
);
6539 /* add more start codes here if needed for private streams */
6542 dprintf(stdout
, "%s SEQ %d I %d P %d B %d\n",
6543 WHO
, sequence_idx
, ipbx
[1], ipbx
[2], ipbx
[3]);
6547 /* Build .tsx file for Packetized Elementary Stream.
6548 This should be quite a bit faster than calling atscut -s.
6549 If it's at least as reliable, will enable it.
6556 unsigned char *p
, *r
, *e
, b
;
6557 long long o
, c
, s
, z
;
6564 if (NULL
== ts_buf
) {
6565 fprintf(stdout
, "%s ts_buf not allocated\n", WHO
);
6571 memset( ipbx
, 0, sizeof(ipbx
));
6577 c
= o
= s
= 0; /* % done status counters */
6578 z
= in_size
; /* input size */
6579 z
/= 100; /* want % of as int */
6580 if (0 == z
) z
= 1; /* avoid /0 */
6584 /* MPEG ES stream has 00 00 01 for Start Code.
6585 Next byte is table type
6587 00 Start of Picture (with picture type)
6588 Don't need to track it much further than that.
6590 start_timer( &timer_fsx
);
6592 /* reset this at start of main loop */
6597 ok
= read( in_file
, ts_buf
, arg_mbz
);
6599 fprintf( stdout
, "\n%s ERROR %s\n", WHO
, in_name
);
6605 fprintf( stdout
, "\n%s EOF %d %s\n", WHO
, ok
, in_name
);
6610 for (i
= 0; i
< ok
; i
+= TSZ
) {
6611 /* current byte offset for this packet */
6616 if (MPEG_SYN
!= *r
) {
6618 /* Any error in the read_buffer code not handling EOF properly? */
6621 "\nread_buffer didn't see EOF @ %9llX!\n",
6625 fprintf( stdout
, "\nTS Sync lost @ %9llX:\n", o
);
6626 dump_packet( o
, ts_buf
, 0, 188 );
6631 parse_ts_header( p
, h
, arg_vpid
);
6633 if (0 == z
) continue;
6635 /* FIXME: this shouldn't be needed but it is */
6636 if (h
->pid
!= arg_vpid
) continue;
6649 /* MPEG Start Code? */
6650 if (0x00000100 != (0xFFFFFF00 & sc
)) continue;
6651 // if (sc > 0x1FF) continue;
6654 /* Skip MPEG 0xE0-0xEF PES header */
6655 if (MPEG_PES
== (0xF0 & b
)) continue;
6657 /* Skip ATSC User Data header */
6658 if (MPEG_UPS
== b
) continue;
6660 /* Skip MPEG2 Sequence or Picture Extensions */
6661 if (MPEG_EXT
== b
) continue;
6663 /* Skip MPEG Slices */
6664 if ((b
> 1) && (b
< 0xB0)) continue;
6667 /* MPEG Sequence Start Code? */
6668 if (MPEG_SEQ
== b
) {
6670 /* TODO: can check Sequence marker bits for extra validation. */
6673 fprintf(stderr
, "\r%s %2lld%% ", WHO
, s
);
6675 "Frames %06d SEQ %06d I %06d P %06d B %06d",
6676 frame_idx
, sequence_idx
,
6677 ipbx
[1], ipbx
[2], ipbx
[3]);
6680 sequences
[sequence_idx
++] = o
;
6681 snprintf(n
, sizeof(n
), "sequences[%d]", sequence_idx
);
6682 sequences
= irealloc( sequences
, sizeof(long long), n
);
6684 /* Can't find Intra frame start in 1st SEQ packet if SEQ data > 1 packet.
6685 This can happen if all the SEQ quantizer tables are specified.
6686 Better to assume all Sequences have an implied Intra frame?
6688 f
= &frames
[frame_idx
];
6694 snprintf(n
, sizeof(n
), "frames[%d]", frame_idx
);
6695 frames
= irealloc( frames
, sizeof(frame_t
), n
);
6700 /* Picture Start Code? */
6701 if (MPEG_PIC
== b
) {
6703 /* Can't get picture coding type if Picture Start Code at end of buffer.
6704 This should not happen when using a buffer size that is an even multiple
6705 of TSZ, but is known to occur with other buffer sizes.
6710 if ((t
> 1) && (t
< 4)) {
6711 f
= &frames
[frame_idx
];
6717 snprintf(n
, sizeof(n
), "frame[%d]", frame_idx
);
6718 frames
= irealloc( frames
, sizeof(frame_t
), n
);
6722 /* Undefine PIC_DEBUG for B & P parsing errors. It won't affect cutting. */
6727 "\n%s TEI%d PRI%d PSI%d PID %04X TSC%d AFC%d afb %3d ts[%4d)\n",
6728 WHO
, h
->tei
, h
->pri
, h
->psi
, h
->pid
,
6729 h
->tsc
, h
->afc
, h
->afb
, r
- p
);
6731 fprintf( stdout
, "Bad PIC type %d @ %9llX ts_buf[0x%02X] sc %08X:\n",
6732 t
, o
, (int) (r
- p
), b
);
6734 dump_packet( o
, p
, TSZ
, r
- p
- 4);
6739 fprintf(stdout
,"\nPIC type not in PSI1 packet!\n");
6746 dprintf(stdout
, "%s SEQ %d I %d P %d B %d\n",
6747 WHO
, sequence_idx
, ipbx
[1], ipbx
[2], ipbx
[3]);
6750 /* catch all for building .tsx or .esx files */
6759 start_timer( &timer_fsx
);
6760 /* use atscut to create the .tsx file */
6764 if (sequence_idx
!= ipbx
[1]) {
6766 fprintf( stdout
, "Sequences %d do not match Iframes %d\n",
6767 sequence_idx
, ipbx
[1]);
6768 fprintf( stdout
, "Trying again with \042atscut -s %s\042\n",
6770 fprintf( stdout
, "system( atscut -s %s )\n", in_name
);
6771 snprintf( s
, sizeof(s
), "atscut -s %s\n", in_name
);
6776 /* create the .esx file with internal function */
6779 stop_timer( &timer_fsx
);
6780 diff_timer( &timer_fsx
);
6781 et
= 1.0 * (double) timer_fsx
.diff
.tv_sec
;
6782 et
+= .000000001 * (double) timer_fsx
.diff
.tv_nsec
;
6783 rate
= (double) in_size
;
6789 "\n%s %s SEQ %d Frames %d in %.2f s @ %.2f MB/s\n",
6790 WHO
, in_name
, sequence_idx
, frame_idx
, et
, rate
);
6793 fprintf( stdout
, "\nCreated %sx in %.2f seconds @ %.2f MB/s\n",
6796 output_frame_sequence();
6798 fprintf(stdout
, "%s:%d ", WHO
, WHERE
);
6800 /* free frames and sequences so input frames can rebuild it? */
6803 /* load .tsx file. append one fake intra frame and sequence to the end */
6806 input_frame_sequence ( void )
6808 struct in_frame_s in_frame
;
6814 int ok
, i
, li
, fi
, lf
;
6820 filebase( fb
, in_name
, F_PFILE
);
6822 snprintf( m
, sizeof(m
), "%s.%sx", fb
, fxp
);
6828 fprintf( stdout
, "Opening frame sequence %s\n", m
);
6829 sf
= fopen( m
, "rb" );
6833 "\nCreating frame sequence file %s, please wait\n", m
);
6836 dprintf( stdout
, "Opening new frame sequence %s\n", m
);
6837 sf
= fopen( m
, "rb" );
6840 fprintf( stdout
, "FATAL: File %s not created\n", m
);
6843 dprintf( stdout
, "Opened %s\n", m
);
6846 /* reset the list made by build_fsx, tests what build_fsx wrote */
6851 /* start over again with a new list */
6854 /* should set all pointers to NULL */
6857 dprintf( stdout
, "Opened %s, seq %d, frame %d, iframe %d\n",
6858 m
, sequence_idx
, frame_idx
, iframe_idx
);
6860 memset(ipbx
, 0, sizeof(ipbx
));
6862 /* input sequences */
6864 ok
= fread( &in_seq
, sizeof(in_seq
), 1, sf
);
6866 fprintf( stdout
, "%s EOF %d @ Sequence %d\n",
6867 WHO
, ok
, sequence_idx
);
6870 dprintf( stdout
, "Sequence %5d @ %llX\n", sequence_idx
, in_seq
);
6872 if (-1LL == in_seq
) {
6873 dprintf( stdout
, "%s End of Sequences @ %d\n",
6878 sequences
[ sequence_idx
] = in_seq
;
6879 dprintf( stdout
, "Sequence %5d @ %llX\n", sequence_idx
, in_seq
);
6881 snprintf( n
, sizeof(n
), "sequences[%d]", sequence_idx
);
6883 sequences
= irealloc( sequences
, sizeof(long long), n
);
6884 if (NULL
== sequences
) {
6886 "\n FATAL: can't realloc for sequence %d\n",
6894 /* don't bother with frames if no sequences */
6895 if (0 == sequence_idx
) {
6896 fprintf( stdout
, "\nFATAL: no Sequences in %s\n", m
);
6901 /* no found iframe */
6905 if (sequence_idx
> 0)
6907 ok
= fread( &in_frame
, sizeof(in_frame
), 1, sf
);
6908 dprintf( stdout
, "fread frame[%d] PIC%d vpn %5d\n",
6909 frame_idx
, in_frame
.pct
, in_frame
.vpn
);
6913 dprintf( stdout
, "%s End of Frames @ %d\n",
6918 /* skip initial partial frames up to first Sequence */
6919 if ((0 == fi
) && (1 != in_frame
.pct
)) continue;
6923 if (in_frame
.pct
> 0) ipbx
[in_frame
.pct
]++;
6925 f
= &frames
[frame_idx
];
6927 f
->pct
= in_frame
.pct
;
6928 f
->vpn
= in_frame
.vpn
;
6930 /* is it an Intra frame? */
6931 if (1 == in_frame
.pct
) {
6932 iframes
[ iframe_idx
] = frame_idx
;
6933 dprintf( stdout
, "iframes[%d] = %d\n",
6934 iframe_idx
, frame_idx
);
6936 snprintf( n
, sizeof(n
), "iframes[%d]", iframe_idx
);
6938 iframes
= irealloc( iframes
, sizeof(int), n
);
6939 if (NULL
== frames
) {
6941 "FATAL: can't realloc for iframe %d\n",
6950 snprintf( n
, sizeof(n
), "frames[%d]", frame_idx
);
6952 frames
= irealloc( frames
, sizeof(frame_t
), n
);
6953 if (NULL
== frames
) {
6954 fprintf( stdout
,"FATAL: can't realloc for frame %d\n",
6963 if ((0 == fi
) || (0 == iframe_idx
)) {
6964 fprintf( stdout
, "\nFATAL: no Intra frames in %s\n", m
);
6968 /* -k option will append a fake Sequence after the last real Sequence,
6969 otherwise default to truncating at (i.e. removing) the last Sequence.
6971 This last Sequence is always a partial Sequence from capture. because
6972 no capture programs currently in use employ any method to stop the capture
6973 at the end of the Sequence (or start of next Sequence). Even atscap stops
6974 the capture 'somewhere' in the last Sequence. This partial Sequence can
6975 crash the libmpeg2 decoder for a small percentage of captures.
6977 The situation is different for files made with Xtscut. They should
6978 all manage to have a complete last Sequence to avoid crashing libmpeg2.
6980 This may be related to broken_link AND closed GOP flags needing to
6981 be set on the last Sequence, but the MPEG spec is a bit obtuse about it.
6983 If broken link isn't set on the last Sequence, the decoder will try
6984 to parse the two frames sequentially after the Intra frame, but which
6985 are rendered before the Intra frame by the Picture temporal field.
6987 If closed GOP is set on the last Sequence, that should tell the
6988 decoder to not check the NEXT Sequence for the last two frames of the
6989 CURRENT Sequence. This should be irrelevant becase B and P's are not
6990 being displayed, but does libmpeg2 decode the P & B frames anyway?
6992 Also, if the last Sequence + Intra frame is short to the point that
6993 the Intra frame itself is cut off by EOF, it might crash libmpeg2.
6994 Right now it says 'EOF reached', but it may yet still crash.
6996 None of this will address libmpeg2 crashing in the middle of the
6997 stream, that seems caused by missing data from reception drop-outs.
6998 Statistically speaking, that is a special cause that can be ignored.
7001 /* See how many frames are after the last Intra frame. If there are
7002 at least 2 frames (IBB), it might not crash libmpeg2.
7005 iprintf( stdout
, "Found %d sequences, %d frames\n",
7006 sequence_idx
, frame_idx
);
7011 li
= fi
- iframes
[ iframe_idx
- 1 ];
7012 lf
= frame_idx
- li
;
7014 iprintf(stdout
, "frames %d li %d lf %d\n", fi
, li
, lf
);
7016 /* show last Sequence */
7017 for (i
= lf
; i
< frame_idx
; i
++ ) {
7019 f1
= &frames
[i
+ 1];
7020 iprintf(stdout
, "frames[%d] %c, packets %d\n",
7021 i
, pictypes
[f
->pct
], f1
->vpn
- f
->vpn
);
7024 /* default is truncate last partial Sequence if not enough frames */
7026 for (i
= lf
; i
< frame_idx
; i
++) {
7028 iprintf( stdout
, "Truncating %c frame # %d \n",
7029 pictypes
[3 & f
->pct
], i
);
7031 // f->num = iframe_idx - 1;
7034 iprintf( stdout
, "Truncate: SEQ[%d] = %09llX\n",
7035 sequence_idx
-1, sequences
[sequence_idx
-1]);
7037 /* non-reversible change, rest are only counter decrements */
7038 sequences
[ sequence_idx
] = in_size
;
7039 // iframes[ iframe_idx ] = 0;
7049 f
= &frames
[frame_idx
];
7051 /* non reversible if it truncates then appends */
7052 f
->pct
= 1; /* append a fake frame */
7054 /* possible type conversion issue? */
7055 f
->vpn
= in_size
/ TSZ
;
7057 /* TESTME: is this going to write outside the boundary? */
7058 f
= &frames
[frame_idx
+ 1];
7059 f
->vpn
= in_size
/ TSZ
;
7061 iframes
[ iframe_idx
] = frame_idx
; /* append a fake iframe */
7062 sequences
[ sequence_idx
] = in_size
; /* append a fake sequence */
7067 iprintf(stdout
, "FAKE LAST SEQ[%d] @ EOF %09llX\n",
7068 sequence_idx
- 1, in_size
);
7070 fprintf( stdout
, "Found %d Pictures in %d Sequences",
7071 frame_idx
- 1, sequence_idx
- 1);
7073 fprintf( stdout
, " (%d frames cut)", li
);
7075 fprintf( stdout
, "\n");
7078 for (i
= frame_idx
- 6; i
< frame_idx
; i
++ ) {
7080 f1
= &frames
[i
+ 1];
7081 dprintf(stdout
, "frames[%d] %c, packets %d\n",
7082 i
, pictypes
[f
->pct
], f1
->vpn
- f
->vpn
);
7085 dprintf(stdout
, "\n");
7087 for (i
= sequence_idx
- 4; i
< sequence_idx
; i
++ )
7088 dprintf(stdout
, "SEQNC[%d] %09llX\n", i
, sequences
[i
]);
7089 dprintf(stdout
, "\n");
7091 for (i
= iframe_idx
- 4; i
< iframe_idx
; i
++ )
7092 dprintf(stdout
, "INTRA[%d] %d\n", i
, iframes
[i
]);
7093 dprintf(stdout
, "\n\n");
7097 for (i
= 0; i
< frame_idx
; i
++ ) {
7099 f1
= &frames
[i
+ 1];
7100 fprintf(stdout
, "frames[%d] %c, packets %d\n",
7101 i
, pictypes
[f
->pct
], f1
->vpn
- f
->vpn
);
7107 /* read cut file into scuts, after input_frame_sequence so iframes[] loaded */
7114 char n
[256], sc_name
[256];
7118 filebase( n
, in_name
, F_PFILE
);
7119 snprintf( sc_name
, sizeof(in_name
), "%s.%sc", n
, fxp
);
7124 f
= fopen( sc_name
, "r");
7126 fprintf( stdout
, "%s %s not found\n", WHO
, sc_name
);
7131 ok
= fread( &scuts
[scut_idx
], sizeof(int), 1, f
);
7134 // FIXME: put this back, output redux for cl-usage, disabled for debug only
7137 "Cut %02d @ SEQ %5d I-Fframe[%5d] = %6d "
7138 "Picture Type %c-Frame (%d)\n",
7142 iframes
[scuts
[scut_idx
]],
7143 pictypes
[frames
[iframes
[scuts
[scut_idx
]]].pct
],
7144 frames
[iframes
[scuts
[scut_idx
]]].pct
7147 frames
[ iframes
[ scuts
[ scut_idx
]]].cut
= ~0;
7153 fprintf( stdout
, "%s file %s has %d cuts\n", WHO
, sc_name
, scut_idx
);
7157 /* scan input for PIDs, for 5000 packets. PID with highest packet count wins */
7158 /* sets arg_vpid to the PID that had the highest packet count, assumes video */
7159 /* TODO: add "00 00 01 Ex" test for video header at payload start */
7164 unsigned char buf
[ TSZ
], *p
, *r
;
7165 unsigned short pids
[ 0x2000 ];
7175 /* input scan is not needed when source is Video Elementary Stream */
7176 if (0 != arg_es
) return;
7182 memset( pids
, 0, sizeof( pids
));
7183 memset( pat
, 0, sizeof(pat
));
7184 memset( pmt
, 0, sizeof(pmt
));
7186 ok
= stat64( in_name
, &fs
);
7188 fprintf( stdout
, "%s stat %d input %s\n", WHO
, ok
, in_name
);
7191 in_size
= fs
.st_size
;
7194 /* about 1s of stream at 80 megabits/second */
7195 if (c
> 50000LL) c
= 50000LL;
7196 iprintf( stdout
, "\n%s will read %lld packets\n", WHO
, c
);
7199 f
= open( in_name
, FILE_RMODE
);
7201 fprintf( stdout
, "%s failed open %s\n", WHO
, in_name
);
7206 for (i
= 0; i
< c
; i
++) {
7207 ok
= read( f
, &buf
, TSZ
);
7211 fprintf( stdout
, "%s failed %d read %s\n", WHO
, ok
, in_name
);
7212 /* nothing to do, time to exit */
7218 /* arg_vpid has not yet been determind so use 0 for pid parameter */
7219 z
= parse_ts_header( p
, h
, 0 );
7222 fprintf(stdout
, "%s Transport Stream Sync lost.\n", WHO
);
7226 if (0 != h
->tei
) continue;
7227 if (0 != h
->tsc
) continue;
7228 if (2 == h
->afc
) continue;
7230 /* look for table type 0 PAT and table type 2 PMT,
7231 as well as video and audio stream start codes
7234 /* nulls wont count against the total loops */
7235 if (MPEG_NULL
== h
->pid
) {
7242 if ((MPEG_SYN
== *pat
) && (MPEG_SYN
== *pmt
)) {
7244 if ( (0 == r
[0]) && (0 == r
[1] && 1 == r
[2]) ) {
7245 if ( A52_PS1
== r
[3] ) arg_apid
= h
->pid
;
7246 if ( MPEG_PES
== (0xF0 & r
[3]) ) arg_vpid
= h
->pid
;
7248 /* If PAT + PMT + VID + AUD found, can exit the loop early, but
7249 it might break the two packet PMT copy. Disabled for now. */
7250 // if ((0 != arg_apid) && (0 != arg_vpid)) break;
7254 /* Only copy PAT if it hasn't yet been copied */
7255 if (MPEG_SYN
!= pat
[0]) {
7256 if (0 == h
->pid
) { /* always PID 0000 */
7257 dprintf(stdout
, "Copying PAT PID %04X\n", h
->pid
);
7258 memcpy( pat
, p
, TSZ
);
7262 /* Set PMT PID if payload start and table type at r[1] is 2 for PMT type
7263 AFC should never be in the PAT or PMT packets.
7266 if (MPEG_PMT
== p
[5]) { // r[1]
7268 dprintf(stdout
, "Found PMT PID %04X\n", h
->pid
);
7273 /* Handle KQED two packet PMTs if atscap didn't define USE_MPEG_REBUILD */
7274 if (pmt_pid
== h
->pid
) {
7275 if (MPEG_SYN
!= *pmt
) {
7276 dprintf(stdout
, "Copying PMT PID %04X\n", h
->pid
);
7277 if (0 != h
->psi
) memcpy( pmt
, p
, TSZ
);
7278 if (MPEG_SYN
== *pmt
) {
7280 memcpy( pmt
+ TSZ
, p
, TSZ
);
7290 dprintf( stdout
, "%s stopped at %lld iterations\n", WHO
, i
);
7292 /* debug: list the found packets */
7293 for (i
= 0; i
< 0x2000; i
++) {
7294 if (0 == pids
[i
]) continue;
7295 dprintf( stdout
, "TS PID %04X has packets %lld\n", pids
[i
], i
);
7298 /* Missing PAT + PMT is considered fatal because it's a malformed TS. */
7299 if (MPEG_SYN
!= *pat
) {
7300 fprintf( stdout
, "FATAL: TS Program Association PID was not found.\n");
7303 fprintf( stdout
, "TS PAT PID is %04X\n", 0);
7306 if (MPEG_SYN
!= *pmt
) {
7307 fprintf( stdout
, "FATAL: TS Program Map PID was not found.\n");
7310 fprintf( stdout
, "TS PMT PID is %04X %d packet%s\n",
7311 pmt_pid
, pmt_pkts
, (1==pmt_pkts
)?"":"s");
7314 /* Missing Video PID is considered fatal. Duh. */
7315 if (0 == arg_vpid
) {
7316 fprintf( stdout
, "FATAL: TS Video PID was not found.\n");
7319 fprintf( stdout
, "TS VID PID is %04X\n", arg_vpid
);
7320 fprintf( stdout
, "TS AUD PID is %04X\n\n", arg_apid
);
7327 input_files ( void )
7332 input_frame_sequence();
7338 /* wait for x key events to either zoom/move viewport or quit program */
7341 x_scan_loop ( void )
7343 if (0 != arg_xmsg
) WHOAMI
;
7345 /* only redraws if xredraw is not zero */
7350 /* is broken, eh wot? same code works fine in atscap. */
7351 /* this checks for sig_kill to try to gracefully exit via c_exit() */
7352 console_scan( &ckey
);
7354 fprintf( stdout
, "Console key 0x%02X\n", ckey
);
7357 /* reduce CPU usage during the loop */
7358 nanosleep( &event_loop_sleep
, NULL
);
7363 /* Put up a video window and step through Intra frames as fast as possible.
7364 libmpeg2 bug? int math used because -a6 makes all FPU ops return nan.
7365 It's OK with -a7. MMX disabled when in MMXEXT + 3DNOW only mode?
7366 It renders a little bit slower with -a7 for some reason.
7370 benchmark_framerate ( void )
7372 int c
, f
, g
, h
, i
, j
;
7376 c
= sequence_idx
- 2;
7377 if (c
>= arg_bfr
) c
= arg_bfr
;
7383 start_timer( &timer_bif
);
7385 for (i
= 0; i
< c
; i
++) {
7386 parse_intra_frame( sequences
[i
], WHO
);
7387 x_draw_image( WHO
);
7390 stop_timer( &timer_bif
);
7391 diff_timer( &timer_bif
);
7393 f
= timer_bif
.diff
.tv_sec
;
7394 g
= timer_bif
.diff
.tv_nsec
;
7400 i
= (10000 * c
) / h
;
7406 " %d Intra frames in %d.%02d seconds at %d.%02d FPS\n\n",
7414 usage ( char ** argv
)
7421 "\t%s [options] file.{ts|tsx}\n"
7425 " -h Help for %s %s %s\n"
7426 " -v Show xtscut banner and exit\n"
7428 " -1 Cut to multiple files, -r -cX modifies this\n"
7429 " -d Delays the writes to reduce loading on one drive\n"
7430 #ifdef USE_GOP_BROKEN_LINK
7431 " -g Sets broken_link flag on 1st GOP in each cut.\n"
7433 " -n Write NULLs at start of cuts (default: strip NULLs)\n"
7434 " -r Renumber -1 odd/even cuts to 01 02 03 ... 99.ts\n"
7436 " -f Function name verbosity (DEBUG)\n"
7437 " -i Intra frame processing verbosity (DEBUG)\n"
7438 " -x X function verbosity (DEBUG)\n"
7440 " -a FPUbits Override default MPEG acceleration (see mpeg2.h)\n"
7441 " -b n Benchmark frame render rate for n Intra-frames\n"
7442 " -c e|o|a If .tsc file exists, cut odd, even or all and exit\n"
7443 " -k n Keep Last Sequence Cruft is 1, Test Cruft is 0\n"
7445 " -m libvomode X video mode, 0 XShm RGB, 1 Xv YV12, 2 Xv YUYV\n"
7447 " -o name Basename. Appends cuts as .00.ts ... .99.ts\n"
7448 " -p path Output path for extracted content\n"
7449 " -s float Histogram scaling factor, floating point\n"
7450 " -w [1,2,4,8] Set video window to 1/divisor of source frame size\n"
7451 "\n", argv
[0], NAME
, VERSION
, LASTEDIT
);
7458 " Run this from an xterm if you wish to see the verbose detail.\n"
7459 " Eventually after you're expert with it, you won't need it.\n"
7460 " TODO: The console output still needs a -q option to squelch it.\n"
7461 " WIP: The console output is slowly being replaced with X output.\n"
7463 " The time-codes at the top are rough estimates based on frame number.\n"
7464 " 3:2 pulldown will cause the time-codes to be wrong. This is OK.\n"
7465 " They aren't used for anything, so correctness doesn't matter.\n"
7467 " Image is Intra frame nearest center at start and stream reposition.\n"
7468 " Use RMB for preview image to place a cut then LMB to place the cut.\n"
7470 " When you select an Intra frame with LMB, it changes the line color\n"
7471 " to red and puts a small number at the bottom of the red line. This\n"
7472 " is the cut number for the cut to the LEFT of the line.\n"
7474 " MMB can be used to jump quickly to any point in the stream.\n"
7475 " It acts as an invisible slider that is as wide as the video window,\n"
7476 " i.e. MMB in the middle puts you near 50%% of the stream, etc.\n"
7478 " Quick Example: Select start of cut and end of cut then hit [e]\n"
7479 " to write the file from the even numbered cut. Use [o] to write\n"
7480 " odd numbered cuts if stream starts without any lead-in cruft.\n"
7481 " Write even numbered cuts if there is a bit of lead-in cruft.\n"
7483 " Default is to make one file. Use option -1 to make separate files.\n"
7485 " Deselect the frame types with LMB from IPB legend at the bottom to\n"
7486 " see full image. If window is 960x544, it renders without artifacts.\n"
7487 " Use -w1 for SD material to keep aspect ratio correct.\n"
7489 " atscap -m option writes the .tsx sequence/frame index files.\n"
7490 " atscut -s option also writes a .tsx file if atscap did not.\n"
7491 " xtscut called without an existing .tsx file calls atscut -s.\n"
7493 " Try the new-style mouse scroll-wheel for navigation!\n"
7495 " If you cut to a different physical volume, it usually writes faster.\n"
7497 "SEE ALSO: xtscut(1)\n"
7507 parse_args ( int argc
, char ** argv
)
7510 char *p
, n
[256], s
[256]; /* temps for -o and -p and source */
7519 fprintf( stdout
, "Need Video .ts or .es file to process\n\n");
7523 memset( n
, 0, sizeof(n
));
7524 memset( arg_base
, 0, sizeof(arg_base
));
7528 fprintf( stdout
, "args:\n");
7529 for (i
= 0; i
< argc
; i
++)
7530 fprintf( stdout
, "arg[%d] %s\n", i
, argv
[i
]);
7533 while ( EOF
!= ( c
= getopt( argc
, argv
,
7537 #ifdef USE_GOP_BROKEN_LINK
7540 "a:b:c:k:j:o:p:s:u:w:z:"
7550 /* alpha-blending color, may require imlib to use it */
7552 arg_alpha
= 0xFF & atoi(optarg
);
7555 /* jump to this frame number */
7557 if (NULL
!= optarg
) arg_jump
= atoi(optarg
);
7560 /* -z 4096 sets input, output and mpeg es buffers to this size */
7562 if (NULL
!= optarg
) mbz
= atoi(optarg
);
7567 if (NULL
!= optarg
) arg_vo
= 3 & atoi(optarg
);
7571 /* toggle: nz sets GOP broken link flag on the first GOP in each cut */
7573 #ifndef USE_GOP_BROKEN_LINK
7574 fprintf( stdout
, "Recompile with USE_GOP_BROKEN_LINK defined.\n");
7577 arg_gop
= 1 & ~arg_gop
;
7581 /* benchmark frame render, 999 I frames max */
7585 arg_bfr
= atoi(optarg
);
7586 if (arg_bfr
< 0) arg_bfr
= 0;
7589 "Please specify how many frames to benchmark.\n\n");
7594 /* toggle keep last sequence */
7596 arg_kls
= 1 & atoi(optarg
);
7599 /* MPEG2 acceleration model override */
7602 arg_mx
= 7 & atoi(optarg
);
7605 /* toggle 1s NULL packet write to start of every cut */
7607 arg_nulls
= 1 & ~arg_nulls
;
7608 fprintf( stdout
, "Writing NULL packets: %s\n",
7609 (0 != arg_nulls
)?"YES":"No");
7612 /* toggle compiled delay */
7614 arg_delay
= 1 & ~arg_delay
;
7617 /* toggle compiled -r cut re-numbering default */
7619 arg_renum
= 1 & ~arg_renum
;
7622 /* specifiy cut type, all is default if no parm match */
7626 if (NULL
!= optarg
) {
7627 /* only first letter is checked */
7629 if ('e' == *p
) cut_type
= CUT_EVEN
;
7630 if ('o' == *p
) cut_type
= CUT_ODD
;
7631 if ('a' == *p
) cut_type
= CUT_ALL
;
7635 /* even/odd cut to one file toggle */
7637 arg_one
= 1 & ~arg_one
;
7640 /* function name debug verbosity */
7645 /* Intra frame debug verbosity */
7650 /* X debug verbosity */
7660 /* window divisor, 1 2 4 or 8. even divisor handles interlaced w/o banding */
7662 if (NULL
!= optarg
) {
7663 arg_wd
= atoi(optarg
);
7665 vw
.w
= 960; /* override config file */
7670 if ( (1 != arg_wd
) && (2 != arg_wd
)
7671 && (4 != arg_wd
) && (8 != arg_wd
) ) {
7673 "Invalid window divisor: use 1, 2, 4 or 8\n\n");
7679 /* show program banner (already done) and exit */
7681 fprintf( stdout
, "\n");
7685 /* histogram scale % of screen height */
7687 if (NULL
!= optarg
) hist_scale
= atof(optarg
);
7693 snprintf( arg_base
, sizeof(arg_base
),"%s", optarg
);
7699 snprintf(arg_path
, sizeof(arg_path
),"%s", optarg
);
7708 /* too small or too large values here will not make much sense */
7710 /* normalize the filename to .ts file and set path from it, if -p didn't */
7711 if (NULL
== argv
[optind
] ) {
7712 fprintf(stdout
, "Specify a source stream file (*.es or *.ts)\n\n");
7716 strncpy( s
, argv
[optind
], sizeof(s
) );
7720 /* see if input is .es file */
7722 p
= strrchr(s
, '.');
7725 if ('e' == *p
) arg_es
= ~0;
7728 /* FIXME: buffer code is broken under 1880 bytes? */
7729 //#define SMALL_BUFZ (1 * TSZ)
7730 #define SMALL_BUFZ (21 * TSZ)
7731 #define LARGE_BUFZ (1024 * TSZ)
7732 if (mbz
< SMALL_BUFZ
) mbz
= SMALL_BUFZ
;
7733 if (mbz
> LARGE_BUFZ
) mbz
= LARGE_BUFZ
;
7735 /* TS read alignment by shrinking mbz slightly to multiple of packet size.
7736 ES doesn't need any buffer alignment.
7738 if (0 == arg_es
) { mbz
/= TSZ
; mbz
*= TSZ
; }
7743 fprintf(stdout
, "TS Buffers are using %d bytes %d packets\n",
7746 fprintf(stdout
, "ES Buffers are using %d bytes\n", mbz
);
7749 if (CUT_ALL
== arg_cut
) arg_one
= 0;
7751 if (0 != arg_es
) fxp
= fxt
[1];
7753 filebase( n
, argv
[optind
], F_PFILE
);
7754 snprintf( in_name
, sizeof(in_name
), "%s.%s", n
, fxp
);
7756 filebase( arg_path
, argv
[optind
], F_PATH
);
7758 /* if arg path non blank, make sure it has / at end of string */
7761 p
+= strlen(arg_path
);
7764 /* don't overrun end of string */
7765 if (strlen(arg_path
) < (sizeof(arg_path
)-2) ) {
7773 fprintf( stdout
, "-p option too large, max is %d chars\n",
7774 sizeof(arg_path
)-2);
7779 /* user specified -o basename option overrides auto basename from input */
7780 /* out base is temp file name constructed from source arg base */
7781 filebase( n
, argv
[optind
], F_TFILE
);
7783 /* -o specified, use parameter without changing it */
7786 /* automatic will strip everything after first . including the . */
7787 strncpy(arg_base
, n
, sizeof(arg_base
));
7788 p
= strchr(arg_base
, '.');
7789 if (NULL
!= p
) *p
= 0;
7791 snprintf(out_base
, sizeof(out_base
)-1, "%s", arg_base
);
7794 fprintf( stdout
, "Input stream file name: %s\n", in_name
);
7796 fprintf( stdout
, "Output path & basename: %s%s\n",arg_path
, out_base
);
7798 if (0 != *in_name
) {
7799 ok
= stat64( in_name
, &fs
);
7801 fprintf( stdout
, "Could not stat %d input file %s\n",
7806 in_size
= fs
.st_size
;
7807 in_inode
= fs
.st_ino
;
7809 /* have permission to read the dirent, does it have permission to read file? */
7811 in_file
= open( in_name
, FILE_RMODE
);
7813 fprintf( stdout
, "Could not open input file %s\n", in_name
);
7816 fprintf(stdout
, "Opened %s, inode %d bufz %d\n",
7817 in_name
, (int)in_inode
, arg_mbz
);
7820 // arg_vo = 1; /* 0 and 2 are fixed? [temporarily out of service] */
7827 show_boilerplate ( void )
7832 "\n" NAME
" "VERSION
"-" LASTEDIT
" "COPYRIGHT
" "EMAIL
"\n"
7834 "Released under the "LICENSE
"\n"
7836 "This software is supplied AS-IS, with NO WARRANTY.\n"
7838 "Compiled on " __DATE__
" at " __TIME__
7840 " for " PLATFORM_NAME
"\n\n"
7845 /* input files, set up display and wait for interactive user input */
7847 main( int argc
, char ** argv
)
7854 /* Peter Knaggs found this work-around for Compiz/Beryl on Ubuntu,
7855 but he followed up later to say Compiz is too buggy to be useful.
7857 setenv( "XLIB_SKIP_ARGB_VISUALS", "1", 1);
7867 parse_args( argc
, argv
);
7884 build_scut_frames();
7886 /* will read first SEQ + I-Frame */
7889 /* make it draw the first iframe again, if needed */
7892 /* move this one around to inspect the allocations as you go */
7895 /* run the gui if no -c option given */
7898 fprintf( stdout
, "Benchmarking %d Intra frames in %s\n",
7901 fprintf( stdout
, "Previewing Intra frames in %s\n\n",
7905 x_init_display(); /* set up X display */
7908 x_init_imlib(); /* load splash image */
7911 x_draw_image( WHO
);
7913 benchmark_framerate(); /* never returns */
7915 x_scan_loop(); /* never returns */
7918 /* -c option falls through to here. do the cuts and exit. no gui */