Set the cut path properly when a non-default output path is specified
[atscap.git] / xtscut.c
blobfbb621eae538cc50cef3f491b751757cb94c4c36
1 #define NAME "Xtscut"
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"
9 /*
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
31 January 1, 2008
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 \
42 * -o xtscut xtscut.c
44 * Dependencies:
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
64 CREDITS:
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.
76 SYNOPSIS:
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):
126 Control:
127 w write all cuts
128 o write odd cuts
129 e write even cuts
130 c clear all cuts
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
141 Navigation:
142 a jump to previous cut
143 s jump to next 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.
161 /* NOTES:
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.
187 XVIDEO IS LOCAL ONLY
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.
195 CHANGELOG:
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:
292 imlib1 IFPS 8
293 imlib2 IFPS 13
294 XvYV12 IFPS 23 WOW! (-m1 is default)
296 changed back to [n]/[m] keys for Intra frame single-step
297 changed Makefile:
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
410 changed whitespace
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
507 20051222 ipb.c
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.
538 TODO:
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? ]
573 /* LIMITATIONS:
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
596 #define _GNU_SOURCE
598 /* always try to set GOP broken_link */
599 #define USE_GOP_BROKEN_LINK
600 #define USE_TS_FIX
602 #include <features.h>
604 #ifdef USE_PLATFORM_H
605 #ifndef PLATFORM_NAME
606 #include <platform.h>
607 #endif
608 #endif
610 #ifndef PLATFORM_NAME
611 #define PLATFORM_NAME "Linux"
612 #endif
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 */
622 #define PIC_DEBUG
624 #include <stdio.h>
625 #include <stdlib.h>
626 #include <unistd.h>
627 #include <stdarg.h>
628 #include <sys/types.h>
629 #include <sys/stat.h>
630 #include <fcntl.h>
631 #include <string.h>
632 #include <errno.h>
633 #include <getopt.h>
634 #include <sys/ioctl.h>
635 #include <inttypes.h>
636 #include <time.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>
645 #ifdef USE_LIBVO
646 /********************************************************** libmpeg2 libvo */
648 #include <inttypes.h>
649 #include <sys/ipc.h>
650 #include <sys/shm.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>
666 #ifdef USE_IMLIB2
667 #include <Imlib2.h>
668 #endif
670 #ifdef USE_IMLIB1
671 #include <Imlib.h>
672 #endif
674 /* libmpeg2 needs these */
675 #include <mpeg2dec/mpeg2.h>
676 #include <mpeg2dec/mpeg2convert.h>
678 /* statfs for volume free space */
679 #include <sys/vfs.h>
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
694 #define CUT_NONE 0
695 #define CUT_ALL 1
696 #define CUT_EVEN 2
697 #define CUT_ODD 3
699 #define MPEG_NULL 0x1FFF
700 #define MPEG_PAT 0
701 #define MPEG_PMT 2
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
710 #define A52_PS1 0xBD
711 #define ESZ 184
712 #define TSZ 188
713 //#define TSBZ (21 * TSZ)
716 /****************************************************************** globals */
718 /* reused for .tsx .tsc, .esx, .esc */
719 int in_file;
720 static char in_name[256] = "";
721 static ino_t in_inode;
723 /* used to save cuts */
724 int out_file;
725 static char out_name[256];
726 static char out_base[256];
727 static ino_t out_inode;
728 static long long in_size;
730 long long bytes_in;
731 long long bytes_out;
732 long long bytes_total;
734 static char *fxt[2] = { "ts", "es" };
735 static char *fxp = NULL;
737 static char pictypes[4] = "?IPB";
738 struct in_frame_s {
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; */
747 /* 16 bytes */
748 typedef struct {
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 */
757 } frame_t ;
759 //typedef long long sequence_t;
760 //typedef int iframe_t;
762 struct timer_s {
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;
774 static int *iframes;
775 static long long *sequences;
777 static int frame_idx;
778 static int iframe_idx;
779 static int sequence_idx;
781 static int ipbx[4];
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",
805 "imlib2 RGB",
806 "XShm RGB",
807 "XVideo YV12",
808 "XVideo YUYV"
811 typedef struct {
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 */
824 } tsh_t;
825 static tsh_t tsh;
827 /************************************************************ CLI arguments */
828 #ifdef USE_LIBVO
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];
861 #if 0
862 /* -i thresholds */
863 static int arg_ti = 265;
864 static int arg_tp = 64;
865 static int arg_tb = 64;
866 #endif
868 static char ** arg_v;
869 static int arg_c;
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;
885 #ifdef USE_LIBRT
886 //static clockid_t clock_method;
887 #endif
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 */
912 /* button area */
913 static int bax;
914 static int bay;
915 static int baw;
916 static int bah;
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 */
927 static int bis = ~0;
928 static int bps = ~0;
929 static int bbs = ~0;
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 */
937 static GC xgc;
939 static XWindowAttributes xattr;
940 static XEvent xev; /* generic event */
941 static unsigned long sattrmask;
942 static XSetWindowAttributes sattr;
944 #ifndef USE_LIBVO
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;
950 #endif
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 */
1007 struct vw_s {
1008 int x;
1009 int y;
1010 int w;
1011 int h;
1014 char *xevtypes[] = {
1015 "Event0",
1016 "Event1",
1017 "KeyPress",
1018 "KeyRelease",
1019 "ButtonPress",
1020 "ButtonRelease",
1021 "MotionNotify",
1022 "EnterNotify",
1023 "LeaveNotify",
1024 "FocusIn",
1025 "FocusOut",
1026 "KeymapNotify",
1027 "Expose",
1028 "GraphicsExpose",
1029 "NoExpose",
1030 "VisibilityNotify",
1031 "CreateNotify",
1032 "DestroyNotify",
1033 "UnmapNotify",
1034 "MapNotify",
1035 "MapRequest",
1036 "ReparentNotify",
1037 "ConfigureNotify",
1038 "ConfigureRequest",
1039 "GravityNotify",
1040 "ResizeRequest",
1041 "CirculateNotify",
1042 "CirculateRequest",
1043 "PropertyNotify",
1044 "SelectionClear",
1045 "SelectionRequest",
1046 "SelectionNotify",
1047 "ColormapNotify",
1048 "ClientMessage",
1049 "MappingNotify",
1050 "LASTEvent"
1054 typedef struct {
1055 int w;
1056 int h;
1057 char *name;
1058 } fnt_t;
1060 /* pcf fonts found in /usr/share/fonts/misc */
1061 fnt_t xpcf[] = {
1062 { 12, 24, "12x24" },
1063 { 10, 20, "10x20" },
1064 { 9, 18, "9x18" }, // 9x18 is broken here
1065 { 9, 15, "9x15" },
1066 { 8, 16, "8x16" },
1067 { 8, 13, "8x13" },
1068 { 7, 14, "7x14" },
1069 { 7, 13, "7x13" },
1070 { 6, 13, "6x13" },
1071 { 6, 12, "6x12" },
1072 { 6, 10, "6x10" },
1073 { 6, 9, "6x9" },
1074 { 5, 8, "5x8" }
1076 static int xpcf_idx = 13;
1077 static int xhelp = 0;
1079 #define MPEG2_STAT_MAX 12
1080 char *m2stat[] = {
1081 "BUFFER",
1082 "SEQUENCE",
1083 "SEQUENCE_REPEATED",
1084 "GOP",
1085 "PICTURE",
1086 "SLICE_1ST",
1087 "PICTURE_2ND",
1088 "SLICE",
1089 "END",
1090 "INVALID",
1091 "INVALID_END",
1092 "??"
1096 /* default window size, works equally well for 720p or 1080i w/o artifacts */
1097 static struct vw_s vw = { 0, 0, 960, 544 };
1099 #ifdef USE_MAXIMIZE
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 };
1104 #endif
1106 /* decoded Intra frame that is put in window before histogram drawn */
1107 #ifdef USE_IMLIB2
1108 #warning using Imlib version 2
1109 static Imlib_Image *image = NULL; /* temp mpeg render image */
1110 #endif
1112 #ifdef USE_IMLIB1
1113 #warning using Imlib version 1
1114 static ImlibImage *image = NULL;
1115 static ImlibData *idata = NULL;
1116 #endif
1117 /************************************************************ X globals END */
1119 /* libmpeg2 structures */
1120 mpeg2dec_t * m2dec;
1121 const mpeg2_info_t * m2inf;
1122 const mpeg2_sequence_t * m2seq;
1123 mpeg2_state_t m2sta;
1125 /* nanosleeps */
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;
1136 #ifdef USE_CONSOLE
1137 static unsigned char ckey; /* last cooked console key */
1138 #endif
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 */
1157 #include "common.h"
1159 static char date_now[64];
1162 static
1163 void
1164 get_time ( void )
1166 time_t tnow;
1167 struct tm tloc;
1168 tnow = time( NULL );
1169 localtime_r( &tnow, &tloc );
1170 asctime_r( &tloc, date_now );
1174 /* write configuration to a text file */
1175 static
1176 void
1177 save_config ( void )
1179 FILE *f;
1180 char *n = "/etc/atscap/xtscut.conf";
1181 char *t0 = "not ";
1182 char *t1 = "";
1184 WHOAMI;
1186 f = fopen(n, "w");
1187 if (NULL == f) {
1188 fprintf(stdout, "%s could not open %s\n", WHO, n);
1189 perror("");
1190 return;
1193 get_time();
1195 fprintf(f, "# "NAME"-"VERSION"-"LASTEDIT" configuration saved on %s",
1196 date_now);
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 );
1232 t0 = "OFF";
1233 t1 = "ON";
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);
1267 fprintf(f, "\n");
1269 fflush(f);
1271 fclose(f);
1275 static
1276 void
1277 load_config( void )
1279 FILE *f;
1280 char b[80];
1281 char *n = "/etc/atscap/xtscut.conf";
1282 char *r, *p;
1284 WHOAMI;
1286 f = fopen(n, "r");
1287 if (NULL == f) return;
1289 memset(b, 0, sizeof(b));
1290 r = n;
1291 while (1) {
1292 r = fgets( b, sizeof(b), f );
1293 if (NULL == r) break;
1294 if (0 == *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;
1303 p = strchr(r, '#');
1304 if (NULL != p) *p = 0;
1306 p = strchr(r, ' ');
1307 if (NULL != p) *p = 0;
1309 if (0 == *r) continue;
1311 p = r;
1312 p++;
1314 switch (*r) {
1316 /* 32 bits, top bit is auto-detect, rest of bits are force certain FPU type */
1317 case 'A':
1318 p += 2;
1319 sscanf( p, "%08X", &arg_mx);
1320 break;
1322 case 'B':
1323 bbs = atoi(p);
1324 break;
1326 case 'C': /* unused */
1327 break;
1329 case 'D':
1330 arg_delay = 1 & atoi(p);
1331 break;
1333 /* eye candy toggle, enabled uses more CPU, slows navigation a little bit */
1334 case 'E':
1335 arg_ec = 1 & atoi(p);
1336 break;
1338 case 'F':
1339 arg_ft = 1 & atoi(p);
1340 break;
1342 case 'G':
1343 arg_gop = 1 & atoi(p);
1344 break;
1346 case 'H':
1347 arg_hl = 1 & atoi(p);
1348 break;
1350 case 'I':
1351 bis = 1 & atoi(p);
1352 break;
1354 case 'J': /* broken */
1355 arg_jump = atoi(p);
1356 break;
1358 case 'K':
1359 arg_kls = 1 & atoi(p);
1360 break;
1362 case 'L':
1363 arg_lb = 1 & atoi(p);
1364 break;
1366 case 'M':
1367 arg_one = 1 & atoi(p);
1368 break;
1370 case 'N':
1371 arg_nulls = 1 & atoi(p);
1372 break;
1374 /* output path */
1375 case 'O':
1376 if (0 != *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;
1381 break;
1383 case 'P':
1384 bps = 1 & atoi(p);
1385 break;
1387 case 'Q':
1388 hist_scale = atof(p);
1389 break;
1391 case 'R':
1392 arg_renum = 1 & atoi(p);
1393 break;
1395 case 'S':
1396 arg_sb = 1 & atoi(p);
1397 break;
1399 case 'T':
1400 arg_tc = 1 & atoi(p);
1401 break;
1403 /* Alpha blend is currently not implemented. It may use too much CPU. */
1404 case 'U':
1405 arg_alpha = 0xFF & atoi(p);
1406 break;
1408 case 'V': /* unused */
1409 break;
1411 case 'W':
1412 arg_wd = 15 & atoi(p);
1413 if (0 == arg_wd) { vw.w = 960; vw.h = 544; }
1414 break;
1416 case 'X':
1417 vw.w = atoi(p);
1418 break;
1420 case 'Y':
1421 vw.h = atoi(p);
1422 break;
1424 case 'Z':
1425 arg_mbz = atoi(p);
1426 break;
1430 /* no divisor unless it's 1, 2, 4, or 8 */
1431 if ( (1 != arg_wd)
1432 && (2 != arg_wd)
1433 && (4 != arg_wd)
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)) {
1438 vw.w = 960;
1439 vw.h = 544;
1442 if ((vw.h < 90) || (vw.h > 1088)) {
1443 vw.w = 960;
1444 vw.h = 544;
1449 /* dump config to stdout in human readable form */
1450 //static
1451 void
1452 show_config ( void )
1454 FILE *f;
1455 char *t0 = "not ";
1456 char *t1 = "";
1457 char *s0 = "OFF";
1458 char *s1 = "ON";
1460 WHOAMI;
1462 f = stdout;
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",
1469 (arg_lb)? t1:t0);
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);
1487 if (0 != arg_wd)
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");
1495 fprintf(f, "\n");
1499 static
1500 void
1501 build_keyhelp ( char *d, int z, int s )
1503 char *t;
1504 t = "\n";
1505 if (0 == s) t = "";
1507 memset(d, 0, z);
1509 asnprintf(d, z,
1510 "KEY CONTROLS:\n"
1511 "%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"
1516 "%s"
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"
1522 "\n"
1523 "KEY NAVIGATION: W is window width in frames (same as pixel width)\n"
1524 "%s"
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"
1530 "\n"
1531 "MOUSE CONTROLS:\n"
1532 "%s"
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 );
1542 static
1543 void
1544 build_cfghelp ( char *d, int z, int s )
1546 char *at = "n/a";
1547 char *dt = "n/a";
1548 char *t0 = "not ";
1549 char *t1 = "";
1550 // char *s0 = "OFF";
1551 // char *s1 = "ON";
1552 char *y0 = "NO ";
1553 char *y1 = "YES";
1554 char *pok;
1555 int ok;
1556 char path[512];
1558 WHOAMI;
1560 memset(d, 0, z);
1562 /* set the output path but truncate it after first 40 chars */
1563 memset(path, 0, sizeof(path));
1564 ok = 0;
1565 if (0 != *arg_path)
1566 ok = chdir(arg_path);
1568 if (0 != ok) {
1569 perror( WHO );
1570 c_exit(1);
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");
1579 at = "autodetect";
1580 if (0x80000000 != arg_mx) at = x86mx[7 & arg_mx];
1582 #ifdef USE_LIBVO
1583 dt = votypes[2 + arg_vo];
1584 #endif
1585 #ifdef USE_IMLIB1
1586 dt = votypes[0];
1587 #endif
1588 #ifdef USE_IMLIB2
1589 dt = votypes[1];
1590 #endif
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);
1595 if (0 != arg_wd)
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",
1604 (arg_lb)? t1:t0);
1605 if (arg_lb) {
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);
1620 #ifdef USE_TS_FIX
1621 asnprintf(d, z, " GOP broken %s", (arg_gop)? y1:y0);
1622 asnprintf(d, z, " Keep NULLs %s\n", (arg_nulls)? y1:y0);
1623 #endif
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 */
1636 static
1637 void
1638 build_scut_frames ( void )
1640 int i, c;
1641 frame_t *f;
1643 WHOAMI;
1645 memset( scuts, 0, sizeof(scuts));
1646 scut_idx = 0;
1647 scuts[scut_idx++] = 0; /* first is sequence 0 */
1649 c = 1;
1651 for (i = 0; i < frame_idx; i++) {
1652 f = &frames[i];
1654 f->tcolor = c;
1656 /* Intra frame type */
1657 if (1 == f->pct) {
1659 /* reset in case f->cut changed */
1660 f->cutnum = 0;
1662 if (0 != f->cut) {
1663 dprintf( stdout,
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;
1670 scut_idx++;
1671 c = 1 & scut_idx;
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.
1688 static
1689 void
1690 calc_null_packets ( void )
1692 WHOAMI;
1694 null_pkts = 0;
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 */
1702 static
1703 void
1704 build_frame_timecode ( char *d, int z, int i )
1706 int m, s, p;
1708 // WHOAMI;
1710 p = i * 100;
1711 p /= frame_rate;
1712 s = p % 60;
1713 m = p / 60;
1714 asnprintf(d, z, " %03d:%02d", m, s );
1719 static
1720 void
1721 build_cutlist( char *d, int z )
1723 int i, fce, fco, fca;
1724 long long cpt, cat, cet, cot, vbf;
1725 struct statfs fs;
1726 char anum[32];
1728 // FIXME: disabled for debug, but enable for output redux on cl-usage
1729 // if (0 != arg_cut) return;
1731 WHOAMI;
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");
1751 if (0 == arg_es) {
1752 asnprintf(d, z, " none none %16lld %16lld PAT+PMT%s pre-amble\n",
1753 0LL, cpt, (0 != arg_nulls) ?"+NULL":"");
1754 } else {
1755 asnprintf(d, z, "Elementary Stream write has no pre-amble\n");
1757 for (i = 0 ; i < scut_idx-1; i++) {
1758 long long cs, cz;
1759 int fcs, fcz;
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;
1772 /* cut all total */
1773 fca += fcz;
1774 cat += cz;
1775 cat += cpt;
1777 /* off by one, list starts with cut 1 */
1778 if (i&1) {
1779 /* cut even total */
1780 cet += cz;
1781 cet += cpt;
1782 fce += fcz;
1783 } else {
1784 /* cut odd total */
1785 cot += cz;
1786 cot += cpt;
1787 fco += fcz;
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");
1807 #if 0
1808 /* data after last cut not displayed because it's irrelevant */
1809 asnprintf(d, z, "%3d @ %6d %11lld %11lld End cruft\n",
1810 scut_idx,
1811 sequence_idx - 1,
1812 sequences[ sequence_idx - 1 ],
1813 in_size - sequences[ scuts[ scut_idx - 1 ]]
1815 #endif
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",
1820 vbf, arg_path);
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" );
1844 static
1845 void
1846 show_keys( int s )
1848 build_keyhelp( keyhelp, sizeof(keyhelp), s);
1849 fprintf( stdout, "%s", keyhelp);
1852 /* TODO: needs control c handler to give it up properly */
1853 static
1854 void
1855 free_frames ( void )
1857 WHOAMI;
1859 sequences = ifree(sequences, "sequences");
1860 iframes = ifree(iframes, "iframes");
1861 frames = ifree(frames, "frames");
1865 #if 0
1866 // error: dereferencing pointer to incomplete type
1867 static
1868 void
1869 free_mpeg2 ( mpeg2dec_t * mpeg2dec )
1871 if (NULL == mpeg2dec) return;
1873 if (mpeg2dec->sequence->width != (unsigned)-1) {
1874 int i;
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);
1894 #endif
1897 /* alloc frames, iframes and sequences arrays, one item until expanded */
1898 static
1899 void
1900 alloc_frames ( void )
1902 char n[256];
1904 /* frame_s, 2x int */
1906 WHOAMI;
1908 frames = NULL;
1909 iframes = NULL;
1910 sequences = NULL;
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);
1917 c_exit(1);
1920 /* iframes int */
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);
1925 c_exit(1);
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);
1933 c_exit(1);
1938 static
1939 void
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 );
1946 c_exit(1);
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 );
1953 c_exit(1);
1958 static
1959 void
1960 clear_arrays ( void )
1962 WHOAMI;
1964 #if 0
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));
1969 #endif
1971 memset( scuts, 0, sizeof(scuts));
1973 frame_idx = 0;
1974 sequence_idx = 0;
1975 scut_idx = 0;
1979 #ifdef USE_SIGNALS
1980 #include "xtc_signals.c"
1981 #endif
1983 #ifdef USE_LIBVO
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 */
1992 static
1993 void
1994 free_sbufs ( void )
1996 WHOAMI;
1998 es_buf = ifree(es_buf, "ES buffer");
1999 ts_buf = ifree(ts_buf, "TS buffer");
2004 #ifdef USE_MAXIMIZE
2005 /* 1920/1088 = x/y, same as 1088/1920 = y/x */
2006 /* y is returned after calc from width x */
2007 static
2008 unsigned int
2009 calc_aspect_h ( unsigned int w )
2011 int y;
2013 WHOAMI;
2015 y = 0;
2016 if ( (0 == w) || (0 == framew) || (0 == frameh) ) return 0;
2018 y = frameh * w;
2019 y /= framew;
2021 return y;
2024 /* 1920/1088 = x/y, same as 1088/1920 = y/x */
2025 /* y is returned after calc from width x */
2026 static
2027 unsigned int
2028 calc_aspect_w ( unsigned int h )
2030 int x;
2032 WHOAMI;
2034 x = 0;
2035 if ( (0 == h) || (0 == framew) || (0 == frameh) ) return 0;
2037 x = framew * h;
2038 x /= frameh;
2040 return x;
2042 #endif
2046 /* build a NULL packet filled with zeros at poiner p */
2047 static
2048 void
2049 build_null_packet ( char *p )
2051 WHOAMI;
2053 memset( p, 0, TSZ );
2054 *p = MPEG_SYN;
2055 p++;
2056 *p = 0x1F;
2057 p++;
2058 *p = 0xFF;
2059 p++;
2060 *p = 0x10;
2064 static
2065 void
2066 dump_packet ( long long o, unsigned char *p, int z, int e )
2068 int i;
2069 char *b;
2071 WHOAMI;
2073 for (i = 0; i < z; i++) {
2074 if (0 == (15 & i)) fprintf( stdout, "%9llX: ", o + i);
2075 b = " ";
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");
2085 #ifndef USE_LIBVO
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.
2089 inline
2090 void
2091 update_iframe ( int w, int h, unsigned char *b )
2093 WHOAMI;
2095 if (0 == xinit) return;
2098 #ifdef USE_IMLIB2
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 );
2103 #endif
2105 #ifdef USE_IMLIB1
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 );
2109 #endif
2112 #endif /* ~USE_LIBVO */
2114 /* Parses Transport Stream header and store result in tsh_t
2116 Arguments:
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
2121 Returns:
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
2127 Usage:
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.
2135 inline
2136 unsigned int
2137 parse_ts_header( unsigned char *p, tsh_t *h, unsigned short pid )
2139 unsigned char *r = NULL;
2140 unsigned int z = 0;
2142 // WHOAMI;
2143 r = p;
2145 h->afc = 0;
2146 h->afb = 0;
2147 h->pso = 4;
2148 h->psb = 0;
2149 h->pid = 0;
2151 if (MPEG_SYN != *r) { /* sync lost? */
2152 /* TODO: needs a way to resync */
2153 return -1;
2156 r++;
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 */
2163 r++;
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 */
2173 z = 0;
2175 /* Payload[0] after header is for Adaptation Field Control size if afc > 1 */
2176 if (h->afc > 0) {
2178 if (1 == h->afc) {
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 */
2186 r++;
2187 r += h->afb; /* skip AFC count + data */
2188 z = (ESZ - 1) - h->afb; /* subtract AFC len+data */
2189 h->pso = 0xFF & (r - p);
2193 h->psb = 0xFF & z;
2195 if (0 != h->tei) return 0;
2196 if (0 != pid) /* zero PID doesn't */
2197 if (pid != h->pid) /* check for Video PID */
2198 return 0;
2200 if (0 != h->tsc) return 0;
2202 return z;
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.
2210 //static
2211 inline
2213 parse_video_es ( void )
2215 unsigned char *r, *p, *d;
2216 int i, z, c;
2217 tsh_t *h;
2219 h = &tsh;
2221 // WHOAMI;
2223 /* -y or .es file specified only uses simple Elementary Stream data */
2224 if (0 != arg_es) {
2225 z = read( in_file, es_buf, arg_mbz );
2226 /* EOF or error? */
2227 if (z < 1) fprintf( stdout, "%s ES EOF %s\n", WHO, in_name );
2228 return z;
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 */
2239 d = es_buf;
2240 p = ts_buf;
2242 c = z = 0;
2243 for (i = 0; i < arg_mbz; i += TSZ) {
2245 r = p + i;
2246 z = parse_ts_header( r, h, arg_vpid );
2248 /* fatal abort on sync lost */
2249 if (-1 == z) {
2250 fprintf( stdout,
2251 "\nFATAL: %s Transport Stream Sync lost.\n\n", WHO);
2252 c_exit(1);
2255 // r += h->pso;
2257 dprintf(stdout,
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);
2261 pkt_num++;
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 */
2267 r += h->pso;
2268 memcpy(&d[c], r, z );
2270 /* counter should never exceed mbz */
2271 c += z; /* bump the byte counter */
2273 return c;
2279 static
2280 void
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;
2287 #ifdef USE_IMLIB2
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.
2294 int c1, c2, c3;
2296 c1 = 0xFF & (fgc >> 16);
2297 c2 = 0xFF & (fgc >> 8);
2298 c3 = 0xFF & fgc;
2300 imlib_context_set_color( arg_alpha, c1, c2, c3 );
2301 imlib_image_draw_line( xc1, yc1, xc2, yc2, 0 );
2302 return;
2303 #else
2304 XSetForeground( xdisplay, xgc, fgc );
2305 XDrawLine( xdisplay, xwindow, xgc, xc1, yc1, xc2, yc2);
2306 return;
2307 #endif
2308 #else
2309 XSetForeground( xdisplay, xgc, fgc );
2310 XDrawLine( xdisplay, xwindow, xgc, xc1, yc1, xc2, yc2);
2311 return;
2312 #endif
2318 static
2319 void
2320 x_clear_display ( void )
2322 if (0 == xinit) return;
2324 #ifndef USE_LIBVO
2325 return;
2326 #endif
2327 if (0 != arg_xmsg) WHOAMI;
2329 XClearWindow( xdisplay, xwindow );
2330 XFlush( xdisplay );
2333 /* Draw a filled quadrangle, many examples, buttons, backgrounds, etc.
2334 The biggest visual problem these suffer is they're flat-color quads.
2336 static
2337 void
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. */
2349 //static
2350 void
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
2367 static
2368 void
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;
2373 unsigned int c;
2374 float j, f, rf, gf, bf;
2376 int cx[8] = { 8, 6, 4, 3, 2, 2, 1, 1 };
2378 if (0 == xinit) return;
2379 if (0 == arg_ec) {
2380 x_draw_quad_fill( COLOR_BLACK, x, y, w, h );
2381 return;
2383 // if (0 != arg_xmsg) WHOAMI;
2385 /* ignore bogus text box limit */
2386 if (0 == h) return;
2387 if (0 == w) return;
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 */
2394 j = e / (float) h;
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));
2402 b = (0xFF & fgc);
2404 // fprintf( stdout, "gradient step j %f\n", j);
2405 for (i = 0; i < h; i++) {
2406 f = j * (float) i;
2407 if (f < 0.0) f = 1.0 + f;
2409 /* color change per color channel */
2410 rf = f * (float) r;
2411 gf = f * (float) g;
2412 bf = f * (float) b;
2414 r1 = (int) rf;
2415 g1 = (int) gf;
2416 b1 = (int) bf;
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 */
2429 x1 = x;
2430 w1 = w;
2431 if ( (i < 8) || (i > (h-9)) ) {
2432 if (i < 8) {
2433 k = i;
2434 } else {
2435 k = (h - 1) - i;
2437 x1 += cx[k];
2438 w1 -= 2 * cx[k];
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.
2458 static
2459 void
2460 parse_intra_frame ( long long offset, char *caller )
2462 int sz, pictype;
2463 long long ok;
2464 // uint8_t m2buf[ ESZ ];
2466 if (NULL == m2dec) {
2467 fprintf(stdout, "%s MPEG2 decoder not initialized.\n", WHO );
2468 exit(1);
2471 WHOAMI;
2473 pictype = 0;
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. */
2477 if (0 != xinit) {
2478 if (offset >= sequences[ sequence_idx-1 ]) {
2480 /* image is not freed after load so x draw_image can do render */
2482 #ifdef USE_LIBVO
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);
2485 #endif
2487 #ifdef USE_IMLIB2
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 );
2492 #endif
2494 #ifdef USE_IMLIB1
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");
2498 #endif
2499 return;
2503 ok = lseek( in_file, offset, SEEK_SET );
2504 if (ok != offset) {
2505 fprintf( stdout, "%s seek error %lld\n", WHO, ok );
2506 perror("");
2507 exit(1);
2509 iprintf( stdout, "%s->%s seek %09llX \n",
2510 caller, WHO, offset );
2512 sz = 0;
2514 /* reset decoder. 1 sets look for next Sequence (0 is next picture) */
2515 mpeg2_reset( m2dec, 1 );
2517 do {
2518 m2sta = mpeg2_parse( m2dec );
2519 dprintf( stdout, "m2sta %d %s\n", m2sta, m2stat[m2sta]);
2520 switch (m2sta) {
2522 /* m2buf needs more data */
2523 case STATE_BUFFER:
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);
2527 break;
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);
2534 #ifdef USE_LIBVO
2535 /* might set nb fbuf, convert format, stride */
2536 /* might set fbufs */
2537 if (0 != xinit) {
2538 iprintf( stdout, "Xoutput->setup Y %d %d UV %d %d\n",
2539 m2seq->width,
2540 m2seq->height,
2541 m2seq->chroma_width,
2542 m2seq->chroma_height);
2544 if ( xoutput->setup( xoutput,
2545 m2seq->width,
2546 m2seq->height,
2547 m2seq->chroma_width,
2548 m2seq->chroma_height,
2549 &xsetup) )
2551 fprintf(stdout, "Xoutput->setup failed\n");
2552 c_exit(1);
2555 #ifdef USE_LIBVO
2556 /* if this is disabled, XShm RGB and XVideo YUYV break */
2557 if (1 != arg_vo) {
2558 if (xsetup.convert
2559 && mpeg2_convert (m2dec, xsetup.convert, NULL))
2561 fprintf (stdout, "conversion setup failed\n");
2562 exit(1);
2565 #endif
2567 if (xoutput->set_fbuf) {
2568 uint8_t * buf[3];
2569 void * id;
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) {
2578 uint8_t * buf[3];
2579 void * id;
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));
2591 #else
2593 #ifdef USE_IMLIB2
2594 mpeg2_convert( m2dec, mpeg2convert_rgb32, NULL );
2595 #endif
2597 #ifdef USE_IMLIB1
2598 mpeg2_convert( m2dec, mpeg2convert_rgb24, NULL );
2599 #endif
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)
2607 fprintf(stdout,
2608 "Frame rate %u.%u, period %5.3f mS, "
2609 "Bit rate %u, Byte rate %u\n",
2610 frame_rate / 100,
2611 frame_rate % 100,
2612 (double)m2seq->frame_period
2613 / (double)27000.0,
2614 m2seq->byte_rate << 3,
2615 m2seq->byte_rate );
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;
2623 break;
2626 case STATE_PICTURE:
2627 /* might skip */
2628 /* might set fbuf */
2629 pictype = 0;
2630 if (0 != xinit) {
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++);
2641 #ifdef USE_LIBVO
2642 if (0 != xinit) {
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) {
2651 uint8_t * buf[3];
2652 void * id;
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 */
2664 break;
2666 /* normal end of picture or sequence end seen */
2667 case STATE_SLICE:
2668 case STATE_END:
2670 pictype = 0;
2671 if (m2inf->display_fbuf)
2672 pictype = 3 & m2inf->display_picture->flags;
2674 if (1 == pictype) {
2675 if (STATE_SLICE == m2sta)
2676 iprintf( stdout, " PICTURE %c SLICE\n",
2677 pictypes[pictype]);
2680 if (STATE_END == m2sta) {
2681 iprintf( stdout, " PICTURE %c END\n",
2682 pictypes[pictype]);
2683 sz = 0;
2684 break;
2687 /* make parse loop stop at the intra frame */
2688 if (0 != xinit) {
2689 if (m2inf->display_fbuf) {
2690 if (1 == pictype) {
2691 xredraw = ~0;
2692 sz = 0;
2693 #ifdef USE_LIBVO
2694 dprintf(stdout,
2695 "x_draw_image will render\n");
2696 /* draw has been moved to x_draw_image */
2698 #if 0
2699 /* draw has been moved to x_draw_image */
2700 /* draw current picture */
2701 /* might free frame buffer */
2702 if (xoutput->draw)
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);
2712 #endif
2715 #else
2716 /* filter keeps iframes */
2717 update_iframe(
2718 m2seq->width,
2719 m2seq->height,
2720 m2inf->display_fbuf->buf[0] );
2721 #endif /* USE_LIBVO */
2725 } else {
2727 /* X not init yet only scans for sequence header info */
2728 sz = 0;
2731 break;
2733 /* TESTME: why does STATE_INVALID trigger every time? */
2734 case STATE_INVALID:
2735 dprintf( stdout, "STATE_INVALID\n" );
2736 break;
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",
2742 WHO, offset);
2743 if (0 != xinit) {
2744 #ifdef USE_IMLIB2
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 );
2749 #endif
2750 #ifdef USE_IMLIB1
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");
2754 #endif
2757 break;
2759 default:
2760 iprintf( stdout, "Unhandled %d %s\n",
2761 m2sta, m2stat[m2sta]);
2762 break;
2766 } while( sz );
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 */
2776 static
2777 void
2778 x_close_display ( void )
2780 WHOAMI;
2782 #ifdef USE_LIBVO
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 );
2789 xinit = 0;
2790 return;
2791 #else
2793 #ifdef USE_IMLIB2
2794 if (NULL != image) imlib_free_image();
2795 #endif
2796 #ifdef USE_IMLIB1
2797 if (NULL != image) Imlib_kill_image( idata, image );
2798 #endif
2799 XFreeGC( xdisplay, xgc );
2800 XDestroyWindow( xdisplay, xwindow );
2801 XCloseDisplay( xdisplay );
2803 #endif /* USE_LIBVO */
2804 xinit = 0;
2808 /* exit, with X structure deallocate */
2809 static
2810 void
2811 c_exit ( int ev )
2813 WHOAMI;
2815 #ifdef USE_CONSOLE
2816 console_reset();
2817 console_reset();
2818 #endif
2819 dprintf(stdout, "%s:%d\n", WHO, WHERE);
2820 free_frames();
2822 dprintf(stdout, "%s:%d\n", WHO, WHERE);
2823 if (0 != m2init) {
2824 mpeg2_close( m2dec );
2827 dprintf(stdout, "%s:%d\n", WHO, WHERE);
2828 if (0 != xinit) {
2829 x_close_display();
2832 close( in_file );
2833 in_file = 0;
2834 close( out_file );
2835 out_file = 0;
2837 dprintf(stdout, "%s:%d\n", WHO, WHERE);
2838 free_sbufs();
2840 if (alloc_idx > 0) {
2841 fprintf(stdout,
2842 "alloc_t structures to be freed (%d):", alloc_idx);
2843 show_allocs();
2846 exit( ev );
2850 #if 0
2851 /* i is frame index, divide by frame rate to determine the time code */
2852 static
2853 void
2854 show_frame_timecode ( int i )
2856 int m, s, p;
2858 // WHOAMI;
2860 p = i * 100;
2861 p /= frame_rate;
2862 s = p % 60;
2863 m = p / 60;
2864 fprintf( stdout, " %03d:%02d", m, s );
2866 #endif
2869 static
2870 void
2871 start_timer ( struct timer_s *t )
2874 WHOAMI;
2876 if (NULL == t) return;
2877 memset( t, 0, sizeof(t));
2878 #ifdef USE_LIBRT
2879 clock_gettime( clock_method, &t->start);
2880 #else
2881 time( &t->start.tv_sec );
2882 #endif
2886 static
2887 void
2888 stop_timer ( struct timer_s *t )
2890 WHOAMI;
2892 if (NULL == t) return;
2893 #ifdef USE_LIBRT
2894 clock_gettime( clock_method, &t->stop );
2895 #else
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++;
2899 #endif
2903 static
2904 void
2905 diff_timer ( struct timer_s *t )
2907 WHOAMI;
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);
2927 #if 0
2928 /* show the byte offsets and total for cuts, check against free volspace */
2929 static
2930 void
2931 show_scuts ( void )
2933 int i, fce, fco, fca;
2934 long long cpt, cat, cet, cot, vbf;
2935 struct statfs fs;
2936 char anum[32];
2938 // FIXME: disabled for debug, but enable for output redux on cl-usage
2939 // if (0 != arg_cut) return;
2941 WHOAMI;
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");
2964 if (0 == arg_es) {
2965 fprintf( stdout, " none none %16lld %16lld PAT+PMT%s pre-amble\n",
2966 0LL, cpt, (0 != arg_nulls) ?"+NULL":"");
2967 } else {
2968 fprintf( stdout, "Elementary Stream write has no pre-amble\n");
2970 for (i = 0 ; i < scut_idx-1; i++) {
2971 long long cs, cz;
2972 int fcs, fcz;
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;
2985 /* cut all total */
2986 fca += fcz;
2987 cat += cz;
2988 cat += cpt;
2990 /* off by one, list starts with cut 1 */
2991 if (i&1) {
2992 /* cut even total */
2993 cet += cz;
2994 cet += cpt;
2995 fce += fcz;
2996 } else {
2997 /* cut odd total */
2998 cot += cz;
2999 cot += cpt;
3000 fco += fcz;
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");
3020 #if 0
3021 /* data after last cut not displayed because it's irrelevant */
3022 fprintf( stdout, "%3d @ %6d %11lld %11lld End cruft\n",
3023 scut_idx,
3024 sequence_idx - 1,
3025 sequences[ sequence_idx - 1 ],
3026 in_size - sequences[ scuts[ scut_idx - 1 ]]
3028 #endif
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",
3035 vbf, arg_path);
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" );
3057 #endif
3060 /* search forwards and backwards in frames[] and pick nearest I frame */
3061 /* f is the frame number from where to start looking, inclusive */
3062 static
3064 find_nearest_iframe ( int f, char *caller )
3066 int i, pi, ni;
3068 WHOAMI;
3070 pi = ni = -1;
3072 iprintf( stdout, "%s:%d %s %d\n\n", WHO, WHERE, caller, f);
3074 if (0 == f) return 0;
3077 if (f >= frame_idx) {
3078 f = frame_idx - 1;
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) {
3086 pi = i;
3087 break;
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) {
3094 ni = i;
3095 break;
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;
3109 i = 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);
3116 return i;
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 */
3122 static
3124 find_next_iframe ( int f )
3126 int i, j;
3128 WHOAMI;
3130 j = 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)
3135 return i;
3136 return j;
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 */
3142 static
3144 find_prev_iframe ( int f )
3146 int i, j;
3148 WHOAMI;
3150 j = 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)
3155 return i;
3156 return j;
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 */
3166 static
3168 x_fontline ( int row )
3170 int ytot;
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); */
3182 static
3184 x_fontwide ( int col )
3186 int xtot;
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 */
3197 static
3199 x_setfont ( int w, int h )
3201 char fn[64];
3203 if (0 == xinit) return 0;
3205 snprintf(fn, sizeof(fn), "%dx%d", w, h);
3206 fn[63] = 0;
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);
3214 return 1;
3217 return 0;
3221 /* set font for text display. caller must have already set bg and fg */
3222 static
3223 void
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");
3238 } else {
3239 xprintf(stdout,"X font %s loaded\n", fn);
3240 /* should probably abort here */
3244 } else {
3245 XSetFont(xdisplay, xgc, xfonts->fid);
3246 // xprintf(stdout,"X font %s selected\n", fn);
3251 static
3252 void
3253 x_title ( void )
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,
3262 xwindow,
3263 xtitle,
3264 xtitle,
3265 None,
3266 NULL,
3268 &xhint
3273 /* this draws tc color text without background fill */
3274 static
3275 void
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 */
3287 static
3288 void
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 */
3299 static
3300 void
3301 x_draw_text_block ( unsigned int tc, int x, int y, char *s )
3303 char b[132];
3304 char *t, *e;
3306 if (0 == *s) return;
3308 t = b;
3309 e = t + sizeof(b);
3310 e--;
3312 while( 0 != *s ) {
3313 if ( (t == e) || ('\n' == (*t++ = *s++)) ) {
3314 t--;
3315 *t = 0;
3316 y += x_fontline(0);
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);
3323 t = b;
3328 /* find the longest line in p, either NL term or NULL term */
3329 static
3331 find_longest_line ( char *p )
3333 int i, j;
3335 i = j = 0;
3337 while (0 != *p) {
3338 if ('\n' == *p++) {
3339 if (i > j) j = i;
3340 i = 0;
3341 } else {
3342 i++;
3345 return j;
3348 /* count the NL chars */
3349 static
3351 find_line_count ( char *p )
3353 int i;
3354 i = 0;
3355 while (0 != *p) if ('\n' == *p++) i++;
3356 return 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
3364 static
3366 x_find_quad_font ( int x, int y, int w, int h )
3368 int i, j, w1, h1, x1, y1;
3369 fnt_t *f;
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++) {
3377 f = &xpcf[i];
3378 j = x_setfont( f->w, f->h );
3379 if (0 == j) {
3380 dprintf(stdout, "%s could not find font %s\n", WHO, f->name);
3381 continue;
3384 w1 = x_fontwide(1);
3385 x1 = w * w1;
3386 h1 = x_fontline(0);
3387 y1 = h * h1;
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?
3392 return i;
3394 return -1;
3398 static
3399 void
3400 x_draw_keyhelp ( void )
3402 int i, j, x, y, w, h, m;
3403 fnt_t *f;
3404 char *s;
3406 if (1 != xhelp) return;
3408 // fprintf(stdout, "%s\n", WHO);
3410 s = keyhelp;
3411 build_keyhelp( s, sizeof(keyhelp), 0 );
3412 w = find_longest_line( s ); // for centering
3413 j = h = find_line_count( s );
3415 x = vw.w;
3416 y = vw.h;
3417 y -= 50;
3418 m = 8;
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 */
3424 if (-1 == i) {
3425 fprintf(stdout,"No font small enough for keyhelp\n");
3426 return;
3429 f = &xpcf[i];
3431 w *= x_fontwide(1);
3432 x = vw.w - (w + m);
3433 x >>= 1;
3435 h *= x_fontline(0);
3436 y = vw.h - (h + m);
3437 y >>= 1;
3439 x_draw_quad_gradientv( COLOR_MAGENTA0, x, y, w + m, h + m, -0.66 );
3440 m >>= 1;
3441 x_draw_text_block( COLOR_WHITE, x + m, y + m, s );
3444 static
3445 void
3446 x_draw_cfghelp( void )
3448 int i, j, x, y, w, h, m;
3449 fnt_t *f;
3450 char *s;
3452 // fprintf(stdout, "%s\n", WHO);
3454 s = cfghelp;
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 );
3462 x = vw.w;
3463 y = vw.h;
3464 y -= 50;
3465 m = 8;
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 */
3471 if (-1 == i) {
3472 fprintf(stdout,"No font small enough for cfghelp\n");
3473 return;
3476 f = &xpcf[i];
3478 w *= x_fontwide(1);
3479 x = vw.w - (w + m);
3480 x >>= 1;
3482 h *= x_fontline(0);
3483 y = vw.h - (h + m);
3484 y >>= 1;
3486 x_draw_quad_gradientv( COLOR_GREEN0, x, y, w + m, h + m, -0.66 );
3487 m >>= 1;
3488 x_draw_text_block( COLOR_WHITE, x + m, y + m, s );
3491 static
3492 void
3493 x_draw_cutlist( void )
3495 int i, j, x, y, w, h, m;
3496 fnt_t *f;
3497 char *s;
3499 WHOAMI;
3501 s = cutlist;
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 );
3513 x = vw.w;
3514 y = vw.h;
3515 y -= 50;
3516 m = 8;
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 */
3522 if (-1 == i) {
3523 fprintf(stdout,"No font small enough for cutlist\n");
3524 return;
3527 f = &xpcf[i];
3529 w *= x_fontwide(1);
3530 x = vw.w - (w + m);
3531 x >>= 1;
3533 h *= x_fontline(0);
3534 y = vw.h - (h + m);
3535 y >>= 1;
3537 x_draw_quad_gradientv( COLOR_BLUE, x, y, w + m, h + m, -0.66 );
3538 m >>= 1;
3539 x_draw_text_block( COLOR_WHITE, x + m, y + m, s );
3543 static
3544 void
3545 x_draw_outlist( void )
3547 int i, j, x, y, w, h, m;
3548 unsigned int c;
3549 fnt_t *f;
3550 char *s;
3552 WHOAMI;
3554 s = outlist;
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 );
3562 x = vw.w;
3563 y = vw.h;
3564 y -= 50;
3565 m = 8;
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 */
3571 if (-1 == i) {
3572 fprintf(stdout,"No font small enough for cut write list\n");
3573 return;
3576 f = &xpcf[i];
3578 w *= x_fontwide(1);
3579 x = vw.w - (w + m);
3580 x >>= 1;
3582 h *= x_fontline(0);
3583 y = vw.h - (h + m);
3584 y >>= 1;
3586 c = COLOR_RED;
3587 if (0 != scut_done) c = COLOR_GREEN;
3589 x_draw_quad_gradientv( c, x, y, w + m, h + m, 0.50 );
3590 m >>= 1;
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
3600 f is frame number
3601 c is 0 even cut, 1 odd cut
3603 static
3604 void
3605 x_draw_timecode ( int x, int f, int c )
3607 int p, h, m, s;
3608 unsigned int fg, bg, yoffset;
3609 char xtext[16];
3611 if (0 == xinit) return;
3613 // if (0 != arg_xmsg) WHOAMI;
3615 /* this seems to work well. use it for show_frame_timecode too */
3616 p = f * 100;
3617 p /= frame_rate;
3618 s = p % 60;
3619 m = p / 60;
3620 h = p / 3600;
3622 if (0 == arg_ft) {
3623 snprintf( xtext, sizeof(xtext), "%d", f );
3624 } else {
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 */
3632 fg = COLOR_WHITE;
3633 bg = COLOR_BLACK;
3635 if (0 != c) {
3636 fg = COLOR_RED;
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 */
3651 static
3652 void
3653 x_draw_cutnum ( int x, int n)
3655 int y, x1;
3656 char t[16];
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 );
3669 y = vw.h - 3;
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 */
3675 x1 = x_fontwide(1);
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);
3683 if (0 == arg_ec) {
3684 x_draw_text_line_bg( COLOR_WHITE, x-x1, y, t);
3685 } else {
3686 x_draw_text_line( COLOR_WHITE, x-x1, y, t);
3691 /* draw the IPB legend */
3692 static
3693 void
3694 x_draw_legend ( void )
3696 int x, y, h, c;
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 */
3714 y = vw.h;
3716 y -= 2 * h;
3717 y -= 4;
3719 /* blank the area used for buttons, bottom center */
3720 // if (0 != arg_lb)
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 );
3725 byh = 16;
3726 bys = y - byh;
3727 tys = y - (3 * byh);
3728 tys += 3;
3730 bxi = x;
3732 /* set button area region */
3733 bax = x - 8;
3734 bay = y - 4 * h;
3735 baw = 44;
3736 bah = 4 * h;
3738 y -= h;
3740 /* now the blocks with colors, if enabled */
3741 c = COLOR_CYAN;
3742 if (0 == bis) c = 0;
3743 if (0 != arg_lb) x_draw_quad_fill( c, x, y-5, 8, 10);
3745 x += 10;
3746 bxp = x;
3747 c = COLOR_MAGENTA;
3748 if (0 == bps) c = 0;
3749 if (0 != arg_lb) x_draw_quad_fill( c, x, y-5, 8, 10);
3751 x += 10;
3752 bxb = x;
3753 c = COLOR_YELLOW;
3754 if (0 == bbs) c = 0;
3755 if (0 != arg_lb) x_draw_quad_fill( c, x, y-5, 8, 10);
3757 /* button labels */
3758 x = bxi;
3759 y -= h - 3;
3760 if (0 != arg_lb) x_draw_text_line( COLOR_WHITE, x-1, y, t);
3762 /* time-code or frame number */
3763 if (0 == arg_ft) {
3764 snprintf( t, sizeof(t), "%d", xlfd);
3765 } else {
3766 int p, s, m;
3767 p = xlfd * 100;
3768 p /= frame_rate;
3769 s = p % 60;
3770 m = p / 60;
3771 snprintf( t, sizeof(t), "%02d:%02d", m, s );
3773 x = vw.w >> 1;
3774 x -= (2 * strlen(t));
3775 x -= 5;
3776 y -= h;
3777 y -= 3;
3779 /* needs a background if ipb buttons are not displayed */
3780 if (0 == arg_lb)
3781 x_draw_quad_fill( 0, x-8, y - h, 44, h + 5 );
3783 x_draw_text_line( COLOR_WHITE, x, y+1, t);
3787 #if 0
3788 /* This shows up when histogram scale is set to 100%. */
3789 /* Disabled since it's not helpful for editing. */
3790 static
3791 void
3792 x_draw_reticle( void )
3794 int x, y, x1, y1;
3795 char t[32];
3797 x = 0;
3798 x1 = vw.w;
3800 x_set_font("fixed"); // 6x12
3802 y1 = vw.h >> 3;
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 );
3810 #endif
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.
3833 static
3834 void
3835 x_draw_histogram ( void )
3837 int i, j, x, y, h, w, c, r, p;
3838 double a;
3839 frame_t *f;
3841 #ifdef USE_DRAW_SEGMENTS
3842 #warning using XDrawSegment for histogram
3843 int k;
3844 XSegment *xseg;
3845 XSegment *xseg1;
3846 xseg = icalloc( vw.w, sizeof(XSegment), "xseg" );
3847 #endif
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;
3861 a = 0.0;
3863 x = y = 0;
3864 h = vw.h;
3865 w = vw.w;
3867 /* blank or shade top 16 lines for timecode redraw */
3868 /* TODO: should be font lines size? */
3869 y = 0;
3870 if (0 != arg_tc) {
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 */
3882 if (0 != arg_ec) {
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 );
3889 p = 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 );
3896 k = 0;
3897 for (i = 0; i < w; i++) {
3898 j = xoffset + i;
3899 x = i;
3900 f = &frames[j];
3901 if (2 != f->pct) continue;
3902 a = hist_scale * (double)f->fpc;
3903 /* frame packet count */
3904 r = a;
3905 xseg1 = &xseg[k];
3906 xseg1->x1 = 0xFFFF & x;
3907 xseg1->x2 = 0xFFFF & x;
3908 xseg1->y1 = 0xFFFF & y;
3909 xseg1->y2 = 0xFFFF & (r + y);
3910 k++;
3913 if (k > 0)
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;
3921 k = 0;
3922 for (i = 0; i < w; i++) {
3923 j = xoffset + i;
3924 x = i;
3925 f = &frames[j];
3926 if (3 != f->pct) continue;
3927 a = hist_scale * (double)f->fpc;
3928 /* frame packet count */
3929 r = a;
3930 xseg1 = &xseg[k];
3931 xseg1->x1 = 0xFFFF & x;
3932 xseg1->x2 = 0xFFFF & x;
3933 xseg1->y1 = 0xFFFF & y;
3934 xseg1->y2 = 0xFFFF & (r + y);
3935 k++;
3938 if (k > 0)
3939 XDrawSegments(xdisplay, xwindow, xgc, xseg, k);
3942 ifree( xseg, "xseg" );
3943 #endif
3945 for (i = 0; i < w; i++) {
3946 j = xoffset + i;
3947 x = i;
3949 /* boundary limit */
3950 if (j >= frame_idx) break;
3952 f = &frames[j];
3954 a = hist_scale * (double)f->fpc;
3956 /* frame packet count */
3957 r = a;
3959 c = 0;
3961 #ifdef USE_DRAW_SEGMENT
3962 if (1 != f->pct) continue;
3963 #endif
3964 switch( f->pct ) {
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.
3974 case 1:
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)) ) {
3981 if (0 != arg_tc) {
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 );
3987 p = j;
3990 /* cut is indicated by red line */
3991 if (0 != f->cut) {
3992 c = COLOR_RED;
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 );
4001 break;
4004 /* current preview position is indicated by white line */
4005 if (j == xlfd) {
4006 c = COLOR_WHITE;
4007 // fprintf(stdout, "current preview y %d\n", y);
4008 x_draw_line( c, x, y, x, h );
4009 break;
4012 /* non cut draws and colors the line if I frame histogram selected */
4013 if ((0 != arg_hl) && (0 != bis)) {
4014 c = COLOR_CYAN;
4015 // fprintf(stdout, "normal y %d\n", y);
4016 x_draw_line( c, x, y, x, r + y );
4017 break;
4020 /* whoops missing, caused P to show up as I */
4021 break;
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 */
4026 case 2:
4027 if ((0 != arg_hl) && (0 != bps)) {
4028 c = COLOR_MAGENTA;
4029 if (0 != arg_tc) y = 18;
4030 x_draw_line( c, x, y, x, r + y );
4032 break;
4034 /* B frame lines are 2 pixels lower than P frame lines */
4035 case 3:
4036 if ((0 != arg_hl) && (0 != bbs)) {
4037 c = COLOR_YELLOW;
4038 if (0 != arg_tc) y = 20;
4039 x_draw_line( c, x, y, x, r + y );
4041 break;
4043 #endif
4045 default:
4046 break;
4051 #if USE_RETICLE
4052 if ( 1.0 == hist_scale ) x_draw_reticle();
4053 #endif
4055 /* This has some odd problems with scaling and colors */
4056 #ifdef USE_IMLIB2_DRAW
4057 if (NULL != image)
4058 imlib_render_image_on_drawable_at_size( 0, 0, vw.w, vw.h );
4059 #endif
4063 /* Frame slider bead, visual indication of stream position.
4064 TODO: "button1 on slider" replaces "button2 anywhere"
4066 static
4067 void
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;
4075 x = xoffset * vw.w;
4076 x /= frame_idx;
4077 w = vw.w * vw.w;
4078 w /= frame_idx;
4080 y = vw.h - 21;
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 );
4085 } else {
4087 /* if there are cuts, try to represent them by a long broken bead line */
4088 x1 = 0;
4089 /* NOTE:
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]];
4098 x2 *= vw.w;
4099 x2 /= frame_idx;
4101 /* cut[0] is first cut, is odd numbered cut */
4102 if (0 == (1 & i)) {
4103 x_draw_line( COLOR_CYAN, x1, y, x2, y );
4104 } else {
4105 x_draw_line( COLOR_GREY0, x1, y, x2, y );
4107 x1 = x2;
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)
4118 y--;
4119 /* one extra black background line above when eye candy enabled */
4120 if (0 != arg_ec)
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 );
4125 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 */
4130 y++;
4131 /* one extra black background line below when eye candy enabled */
4132 if (0 != arg_ec)
4133 x_draw_line( COLOR_BLACK, 0, y, vw.w, y );
4134 x_draw_line( COLOR_GREY1, x, y, x + w, y );
4139 static
4140 void
4141 x_draw_image ( char *caller )
4143 int o;
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 */
4152 if (0 != xumove) {
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)
4158 o = 0;
4161 /* start at first iframe after offset */
4162 xcfd = find_nearest_iframe( o, WHO );
4163 if (xcfd != xlfd) {
4164 /* decode the Intra frame to an image buffer */
4165 parse_intra_frame( sequences[frames[ xcfd ].num], WHO );
4166 xlfd = xcfd;
4168 xumove = 0;
4171 #ifdef USE_LIBVO
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 );
4181 #endif
4185 /* imlib handles the video resizing */
4186 #ifdef USE_IMLIB2
4187 if (NULL != image) {
4188 imlib_render_image_on_drawable_at_size( 0, 0, vw.w, vw.h );
4190 #endif
4191 #ifdef USE_IMLIB1
4192 if (NULL != image) {
4193 Imlib_apply_image( idata, image, xwindow );
4195 #endif
4199 static
4200 void
4201 x_redraw_display ( void )
4203 if (0 == xinit) return;
4204 if (0 == xredraw) return;
4206 if (0 != arg_xmsg) WHOAMI;
4208 //#ifdef USE_LIBVO
4209 /* YUV overlay frame can't serve as histogram clear like imlib frame does */
4210 // x_clear_display();
4211 //#endif
4212 x_draw_image( WHO );
4213 x_draw_histogram();
4214 x_draw_legend();
4215 x_draw_slider();
4216 x_draw_keyhelp();
4217 x_draw_cfghelp();
4218 x_draw_cutlist();
4219 x_draw_outlist();
4221 xumove = 0;
4222 xredraw = 0;
4226 /* shorthand, everyone knows which display window it is */
4227 static
4228 Bool
4229 x_check_twe( int event_type, XEvent *event_return )
4231 // if (0 != arg_xmsg) WHOAMI;
4232 return
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 */
4242 #ifdef USE_MAXIMIZE
4243 static
4244 void
4245 x_resize_display ( void )
4247 XSetWindowAttributes nxswa;
4248 unsigned long nxswamask;
4251 if (0 == xinit) return;
4253 if (0 != arg_xmsg) WHOAMI;
4255 xredraw = ~0;
4256 nxswamask = CWOverrideRedirect;
4258 if (0 != xmaxi) {
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,
4271 xwindow,
4272 nxswamask,
4273 &nxswa
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 ) );
4282 #if 1
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,
4287 xwindow,
4288 RevertToParent,
4289 CurrentTime
4291 #endif
4294 /* maximize the window */
4295 XMoveResizeWindow( xdisplay, xwindow,
4296 xattr.x, xattr.y,
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
4303 still true?
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
4310 vw0.x = vw.x;
4311 vw0.y = vw.y;
4312 vw0.w = vw.w;
4313 vw0.h = vw.h;
4315 vw.x = xattr.x;
4316 vw.y = xattr.y;
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");
4325 exit(1);
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);
4333 } else {
4334 /* restore overlay and window to previous saved.
4335 have to adjust for titlebar height again too */
4336 vw.x = vw0.x;
4337 vw.y = vw0.y;
4338 vw.w = vw0.w;
4339 vw.h = vw0.h;
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,
4350 xwindow,
4351 nxswamask,
4352 &nxswa
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,
4367 xwindow,
4368 vw.x,
4369 vw.y, /* +xtbh, */
4370 vw.w,
4371 vw.h
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);
4384 #endif
4386 /* initialize libmpeg2 and load the first Sequence
4387 also set the default video window size from the first sequence data
4389 static
4390 void
4391 init_mpeg2 ( void )
4393 char *t;
4395 WHOAMI;
4397 accel = mpeg2_accel( arg_mx );
4399 #ifdef USE_IMLIB1
4400 t = votypes[0];
4401 #endif
4402 #ifdef USE_IMLIB2
4403 t = votypes[1];
4404 #endif
4405 #ifdef USE_LIBVO
4406 t = votypes[2 + arg_vo];
4407 #endif
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 );
4416 exit (1);
4419 m2inf = mpeg2_info( m2dec );
4420 if ( NULL == m2inf ) {
4421 fprintf( stdout, "%s could not initialize info.\n", WHO );
4422 exit (1);
4424 m2init = ~0;
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 */
4435 if (0 != arg_wd) {
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 );
4444 #ifndef USE_LIBVO
4445 /* This is done after the X display has been initialized */
4446 /* initialize imlib and put the splash on screen. */
4447 static
4448 void
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));
4460 t0 = &ft[0];
4461 t1 = &ft[64];
4462 t2 = &ft[128];
4463 t3 = &ft[192];
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;
4479 y0 = 8 + (th * 3);
4480 y1 = 8 + (th * 2);
4481 y2 = 8 + (th * 1);
4482 y3 = 8 + (th * 0);
4484 #ifdef USE_IMLIB2
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);
4507 #endif
4508 #ifdef USE_IMLIB1
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 );
4515 #endif
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 */
4529 static
4530 void
4531 x_init_display ( void )
4533 char n[256];
4534 char o[256];
4536 if (0 != arg_xmsg) WHOAMI;
4538 /* if the window already exists, return immediately */
4539 if (0 != xinit) {
4540 fprintf(stdout, "X display already open!\n");
4541 return;
4544 /* setup X window hints for initial size and position */
4545 xhint.x = vw.x;
4546 xhint.y = vw.y;
4548 xhint.width = vw.w;
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");
4563 #ifdef USE_LIBVO
4564 /******************************************************************** LIBVO */
4565 switch(arg_vo) {
4566 case 0:
4567 xoutput = vo_x11_open(); /* XShm RGB as fast as imlib2 */
4568 break;
4569 case 1:
4570 xoutput = vo_xv_open(); /* XVideo YV12 renders the quickest */
4571 break;
4572 case 2:
4573 xoutput = vo_xv2_open(); /* XVideo YUYV as fast as imlib2 */
4574 break;
4575 default:
4576 break;
4579 if ( xoutput->setup( xoutput,
4580 m2seq->width, m2seq->height,
4581 m2seq->chroma_width, m2seq->chroma_height,
4582 &xsetup) )
4584 fprintf(stdout, "output setup failed\n");
4585 c_exit(1);
4589 xprintf(stdout, "LIBVO %s display open\n", votypes[2+arg_vo]);
4591 /* select the event types for normal operation */
4592 XSelectInput( xdisplay,
4593 xwindow,
4594 StructureNotifyMask
4595 | KeyPressMask
4596 | ButtonPressMask
4597 | FocusChangeMask
4598 #ifdef USE_EXPOSE
4599 #warning using X Expose events
4600 | ExposureMask
4601 #endif
4604 XSetBackground( xdisplay, xgc, xbg );
4605 XSetForeground( xdisplay, xgc, xfg );
4607 x_set_font( "fixed" );
4609 xinit = 1;
4610 x_title();
4611 return;
4613 /******************************************************************** LIBVO */
4614 #else
4616 /******************************************************************** Imlib */
4617 /* open the x display for imlib1/2 */
4618 xdisplay = XOpenDisplay( dispname );
4620 if (xdisplay == NULL) {
4621 perror("opening X display");
4622 exit(1);
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 ),
4636 &xattr
4639 xbpp = xattr.depth;
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,
4647 xscreen,
4648 xbpp,
4649 TrueColor,
4650 &vinfo
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 ),
4658 vinfo.visual,
4659 AllocNone
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 */
4668 sattrmask = 0
4669 | CWBackPixel
4670 | CWBackPixmap
4671 | CWBackingStore
4672 | CWBorderPixel
4673 | CWColormap;
4675 /* create the window on the display */
4676 xwindow = XCreateWindow( xdisplay,
4677 RootWindow( xdisplay, xscreen ),
4678 xhint.x,
4679 xhint.y,
4680 xhint.width,
4681 xhint.height,
4683 xbpp,
4684 InputOutput, /* CopyFromParent, */
4685 vinfo.visual,
4686 sattrmask,
4687 &sattr
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,
4699 xwindow,
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,
4713 xwindow,
4714 StructureNotifyMask,
4715 &xev);
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;
4738 xprintf(stdout,
4739 "\tSend Event x%d y%d w%d h%d th%d\n",
4740 vw.x, vw.y, vw.w, vw.h, xtbh);
4741 } else {
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",
4747 vw.w, vw.h);
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 */
4765 xinit = 1;
4767 x_title();
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,
4793 xwindow,
4794 StructureNotifyMask
4795 | KeyPressMask
4796 | ButtonPressMask
4797 | FocusChangeMask
4798 #ifdef USE_EXPOSE
4799 #warning using X Expose events
4800 | ExposureMask
4801 #endif
4804 /* set focus so xinput works when in fullscreen mode */
4805 XSetInputFocus( xdisplay,
4806 xwindow,
4807 RevertToNone,
4808 CurrentTime
4811 #ifdef USE_MAXIMIZE
4812 if (xmaxi) {
4813 fprintf(stdout,"going full screen\n");
4814 x_resize_display();
4816 #endif
4818 /******************************************************************** Imlib */
4819 #endif
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. */
4824 //static
4825 inline
4827 test_gop_set_broken ( unsigned char *r, unsigned int z, int n )
4829 unsigned int i;
4830 unsigned char b;
4832 if (z < 1) return 0;
4833 if (0 == arg_gop) return ~0; /* pretend it's done if no -g option */
4836 WHOAMI;
4838 for (i=0; i < z; i++) {
4839 sc <<= 8;
4840 b = *r;
4841 sc |= b;
4842 r++;
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? */
4850 if (i < (z-4)) {
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])) {
4862 if (0 != arg_gop)
4863 iprintf(stdout,
4864 "GOP broken @ pkt %d\n", n );
4865 /* GOP broken_link set */
4866 r[3] |= 0x20;
4867 return ~0;
4868 } else {
4869 if (0 != arg_gop)
4870 iprintf(stdout, "GOP closed @ pkt %d\n", n );
4873 /* stop looping after first GOP found */
4874 return ~0;
4877 /* GOP broken_link was not set */
4878 return 0;
4881 /* in_file and out_file already open, c is count of bytes, s is seek or -1 */
4882 static
4883 void
4884 write_block ( long long s, long long c )
4886 int a, b, g;
4887 long long i, j, z;
4888 unsigned char *p;
4889 unsigned int skip_pkts;
4890 tsh_t *h;
4892 if ( (s < 0) || (c < 1) ) return;
4894 if ((NULL == es_buf) || (NULL == ts_buf)) return;
4896 WHOAMI;
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
4902 skip_pkts = 0;
4903 h = &tsh;
4905 if (0 == arg_es)
4906 if (0 != (c % TSZ))
4907 fprintf( stdout,
4908 "write will be short %lld bytes\n", c % TSZ);
4909 p = ts_buf;
4911 /* clear the payload start indicator flags before doing the cut */
4912 memset(psib, 0, sizeof(psib));
4914 j = -1; /* packet counter */
4915 i = 0LL;
4917 while (i < c) {
4918 z = arg_mbz;
4919 if ((c - i) < arg_mbz) z = c - i;
4920 j++;
4921 if (0 != j) {
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 );
4930 bytes_in += a;
4932 /* error EOF */
4933 if (a < 0) {
4934 fprintf( stdout,
4935 "error %d reading %s\n", a, in_name );
4936 perror("");
4937 return;
4940 i += a;
4942 /* exact EOF */
4943 if (0 == a) {
4944 dprintf( stdout, "%s:%d EOB %s\n",
4945 WHO, WHERE, in_name);
4946 break;
4949 /* ES cut writes last partial packet data, TS cut discards it */
4950 if (a != arg_mbz) {
4951 if (0 == arg_es) {
4952 dprintf( stdout, "%s:%d EOB %s\n",
4953 WHO, WHERE, in_name);
4954 break;
4958 /* ES cut doesn't use TS sync and header bits, only copies the data */
4959 if (0 != arg_es) {
4960 b = write( out_file, ts_buf, a );
4961 bytes_out += b;
4962 if (a != b) {
4963 fprintf( stdout,
4964 "error %d writing %s\n",
4965 b, out_name);
4966 perror("");
4967 return;
4969 continue;
4973 #ifndef USE_TS_FIX
4974 b = write( out_file, ts_buf, a );
4975 bytes_out += b;
4976 if (a != b) {
4977 fprintf( stdout, "error %d writing %s\n", b, out_name);
4978 perror("");
4979 return;
4981 continue;
4982 #endif
4985 #ifdef USE_TS_FIX
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)) {
4989 int n;
4990 long long k, x;
4991 unsigned char *r;
4993 for (k = 0; k < a; k += TSZ) {
4995 n = ((j * arg_mbz) + k) / TSZ;
4996 r = p + k;
4997 x = parse_ts_header( r, h, 0 );
4998 if (x < 0) {
4999 fprintf(stdout,"ERROR: Transport Sync Lost\n");
5000 return;
5003 #ifdef USE_NULL_STRIP
5004 /* skip NULL packets for smaller output files, -n enables NULL packets */
5005 if (0 == arg_nulls)
5006 if (MPEG_NULL == h->pid)
5007 continue;
5008 #endif
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 ]) {
5017 skip_pkts++;
5018 // skip the packet
5019 // continue;
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? */
5029 if (0 != arg_gop)
5030 if ( (h->pid == arg_vpid)
5031 && (0 == g)
5032 && (x > 0) )
5033 g = test_gop_set_broken( r + h->pso, x, n );
5034 #endif
5036 #warning using one-packet write (slower)
5037 b = write( out_file, r, TSZ );
5038 bytes_out += b;
5039 if (b != TSZ) {
5040 fprintf( stdout,
5041 "ERROR: writing %s, out%d != in%d\n",
5042 out_name, b, a);
5043 perror("");
5044 break;
5048 #endif
5052 // fprintf(stdout, "skip_pkts %d\n", skip_pkts);
5054 /* TODO:
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] */
5063 static
5064 void
5065 write_cutfile ( void )
5067 FILE *o;
5068 char n[256], sc_name[512];
5069 int ok;
5071 if (0 != arg_cut) return;
5072 #if 0
5073 /* atscap 1.1rc9t fixed to handle the zero byte .tsc index.html status */
5074 if (0 == scut_idx) return;
5075 #endif
5077 WHOAMI;
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" );
5086 if ( NULL == o ) {
5087 fprintf( stdout, "error opening %s\n", sc_name );
5088 perror("");
5089 return;
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);
5096 } else {
5097 dprintf( stdout, "wrote %d cuts to %s\n", ok, sc_name);
5100 fclose( o );
5103 /* [c] key removes all cuts */
5104 static
5105 void
5106 clear_scuts ( void )
5108 int i;
5110 WHOAMI;
5112 /* faster? */
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;
5115 scut_idx = 0;
5118 /* write PAT + PMT to filedes f, but only if input scan found anything */
5119 static
5120 void
5121 write_patpmt ( void )
5123 int t;
5124 WHOAMI;
5126 if (0x47 != *pat) return;
5128 t = write( out_file, pat, TSZ );
5129 bytes_out += t;
5131 if (0x47 != *pmt) return;
5132 if (pmt_pkts < 1) return;
5134 t = write( out_file, pmt, TSZ * pmt_pkts );
5135 bytes_out += t;
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?
5146 static
5147 void
5148 write_nulls ( void )
5150 char pktnull[ TSZ ];
5151 int i, j, t;
5153 if (0 == arg_nulls) return;
5155 WHOAMI;
5157 /* use last parsed SEQ header bit rate */
5158 calc_null_packets();
5160 if (0 == null_pkts) return;
5162 j = null_pkts;
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 );
5171 bytes_out += t;
5173 return;
5176 #if 0
5177 /* create incremental filename (-1 thru -9) in d from basename in s */
5178 static
5179 void
5180 build_next_name ( char *d, char *s, int z )
5182 char t[256];
5183 struct stat64 fs;
5184 int i, ok;
5186 WHOAMI;
5188 memset( t, 0, sizeof(t) );
5190 ok = 0;
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 );
5195 if (0 != ok) break;
5196 fprintf( stdout, "Found %s\n", t );
5199 if (0 == ok) {
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 );
5205 #endif
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 */
5211 static
5213 build_outname ( char *s, int c )
5215 int ok;
5216 struct stat64 fs;
5218 WHOAMI;
5220 if (0 != arg_one) {
5221 snprintf(out_base, sizeof(out_base)-1, "%s", arg_base);
5222 } else {
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);
5240 return -1;
5242 return 0;
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.
5277 Maybe:
5278 [write two packets [and null packets?], PAT and PMT, to start of file]
5281 static
5282 void
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 */
5291 char *d;
5292 int z;
5294 WHOAMI;
5296 if ( 0 == scut_idx ) return;
5297 start_timer( &timer_cut );
5299 scut_done = 0;
5301 filebase( n, in_name, F_PFILE );
5303 write_cutfile();
5305 d = outlist;
5306 *d = 0;
5307 z = sizeof(outlist);
5309 t = "NONE";
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, '/' );
5316 if (NULL == p) {
5317 p = in_name;
5318 } else {
5319 p++;
5322 asnprintf(d, z, "Writing %s cuts From %s:\n", t, p);
5323 asnprintf(d, z, " To: %s\n", arg_path);
5325 if (0 != arg_renum)
5326 asnprintf(d, z, "Cuts will be renumbered sequentially\n");
5328 in = TSZ;
5329 out = 0;
5331 /* reset bytes counts and input/output block indices */
5332 bytes_in = 0;
5333 bytes_out = 0;
5334 bytes_total = 0;
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 */
5338 i = 0;
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 );
5350 if (0 != ok) {
5351 fprintf(stdout, "ERROR: Rename the input file\n");
5352 return;
5355 asnprintf(d, z, "Cut # SEQ # byte offset file size output name\n");
5356 asnprintf(d, z, "----- ------ ----------- ----------- -----------\n");
5358 if (0 != arg_one) {
5360 out_file = open( out_name, FILE_WMODE, FILE_PERMS);
5361 if (out_file < 3) {
5362 fprintf( stdout, "\n%s open %s error\n", WHO, out_name);
5363 perror("");
5364 return;
5368 k = 1;
5369 j = 0;
5371 /* counter-intuitive: cut 000 is not in scuts[]. cut 001 is scuts[0] */
5372 if (CUT_ALL != cut_type) {
5373 k = 2;
5374 if (CUT_EVEN == cut_type) {
5375 j = 1;
5379 if (0 != xinit) {
5380 xhelp = 4;
5381 x_draw_quad_gradientv(COLOR_RED, 0, 0, vw.w, vw.h, 0.5 );
5382 x_draw_outlist();
5385 for ( i = j; i < scut_idx-1; i += k ) {
5386 cut_num = i;
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++;
5394 cut_num >>= 1;
5395 dprintf( stdout, "cut %d renumbered to %d\n",
5396 i, cut_num+1);
5399 /* -1 option is do not make one file, open a new one for each cut */
5400 if (0 == arg_one) {
5402 if (out_file > 2) close( out_file );
5403 out_file = 0;
5404 build_outname( n, cut_num + 1 );
5405 if (in_inode == out_inode) return;
5407 out_file = open( out_name, FILE_WMODE, FILE_PERMS );
5408 if (out_file < 3) {
5409 fprintf( stdout, "\n%s open %s error\n",
5410 WHO, out_name);
5411 perror("");
5412 return;
5416 bytes_cut = sequences[scuts[ i + 1 ]] - sequences[scuts[ i ]];
5418 if (0 > bytes_cut)
5419 bytes_cut = in_size - sequences[ scuts[ i ] ];
5421 cpt = TSZ * (1 + pmt_pkts);
5422 cpt += TSZ * null_pkts;
5424 p = strrchr(out_name, '/');
5425 if (NULL == p) {
5426 p = out_name;
5427 } else {
5428 p++;
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);
5436 if (0 != xinit) {
5437 xhelp = 4;
5438 x_draw_outlist();
5439 XFlush(xdisplay);
5442 /* write any PAT + PMT found by input scan and optional NULL packets */
5443 write_patpmt();
5444 write_nulls();
5445 write_block( sequences[scuts[ i ]], bytes_cut );
5448 fsync( out_file );
5449 close( out_file );
5450 out_file = 0;
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);
5463 #if 0
5464 asnprintf(d, z, "\nInput %11lld", bytes_in );
5465 asnprintf(d, z, "\nOutput %11lld", bytes_out );
5466 asnprintf(d, z, "\nTotal %11lld, ", bytes_total);
5467 #endif
5469 tp = (double)bytes_total / et;
5470 tp /= 1000000.0;
5472 asnprintf(d, z, "I/O MB/s %.2f\n", tp);
5474 scut_done = ~0;
5475 if (0 != xinit) {
5476 x_draw_quad_gradientv(COLOR_GREEN, 0, 0, vw.w, vw.h, 0.5 );
5477 x_draw_outlist();
5482 /* find I-frame before current frame that has cut set, or iframe 0 */
5483 static
5485 find_prev_cut ( void )
5487 int i;
5489 WHOAMI;
5491 for (i = (xcfd - 1); i > 0; i-- )
5492 if (0 != frames[i].cut)
5493 return i;
5494 return 0;
5498 /* find I-frame after current frame that has cut set */
5499 static
5501 find_next_cut ( void )
5503 int i;
5505 WHOAMI;
5507 for (i = (xcfd + 1); i < frame_idx; i++ )
5508 if (0 != frames[i].cut)
5509 return i;
5510 return 0;
5514 /* return nz if bx,by is within quadrangle defined by x,y,w,h */
5515 static
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) )
5523 return ~0;
5524 return 0;
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
5539 static
5540 void
5541 x_event ( void )
5543 int i, j, bx, by;
5544 int save;
5545 save = 0;
5547 // if (0 != arg_xmsg) WHOAMI;
5549 xumove = 0;
5550 uoffset = 0;
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;
5572 /* not square
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);
5580 } else {
5581 /* otherwise it's a user resize, so update w,h */
5582 vw.w = xev.xconfigure.width;
5583 /* not square
5584 vw.w = xev.xconfigure.height;
5586 vw.h = xev.xconfigure.height;
5588 xprintf(stdout,"\tUser w%d h%d\n",
5589 vw.w, vw.h);
5592 save = ~0;
5593 xredraw = ~0;
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");
5611 xredraw = ~0;
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");
5617 // xredraw = ~0;
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");
5626 // xredraw = ~0;
5627 x_check_twe( ButtonPress, &xev);
5630 /* look for button events */
5631 while ( x_check_twe( ButtonPress, &xev) ) {
5632 bx = xev.xbutton.x;
5633 by = xev.xbutton.y;
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);
5642 xumove = ~0;
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);
5649 xumove = ~0;
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);
5656 xprintf(stdout,
5657 "\nX Event:\tRight ButtonPress x %d y %d, i %d\n",
5658 bx, by, i
5661 /* not in IPB area, is preview select */
5662 if (0 == i) {
5664 /* if user selection is within valid frame range */
5665 xlfd = find_nearest_iframe( xoffset + bx, WHO );
5667 parse_intra_frame(
5668 sequences[frames[xlfd].num], WHO);
5671 xredraw = ~0;
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;
5681 i = bx * frame_idx;
5682 i /= vw.w;
5683 // j = iframes[i] - (vw.w>>1);
5684 j = i - (vw.w>>1);
5685 xoffset = find_next_iframe( j );
5686 xumove = ~0;
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);
5697 if (0 != i) {
5698 arg_ft = 1 & ~arg_ft;
5699 save = ~0;
5700 xredraw = ~0;
5701 break;
5704 /* test for IPB button area */
5705 i = x_button_quad(bax, bay, baw, bah, bx, by);
5706 xprintf(stdout,
5707 "\nX Event:\tLeft ButtonPress x %d y %d i %d\n",
5708 bx, by, i
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 */
5715 if (0 == i) {
5716 int ni;
5717 ni = find_nearest_iframe( xoffset + bx, WHO );
5719 xredraw = ~0;
5721 /* disable frame 0 cut selection, otherwise toggle cut indication */
5722 if (ni > 0) {
5723 frames[ ni ].cut = 1 & ~frames[ ni ].cut;
5724 build_scut_frames();
5725 write_cutfile();
5727 break;
5730 /* test for IPB button toggle areas */
5731 i = x_button_quad( bxi, bys, 9, byh, bx, by);
5732 if (0 != i) {
5733 bis = 1 & ~bis;
5734 save = ~0;
5735 xredraw = ~0;
5738 i = x_button_quad( bxp+1, bys, 8, byh, bx, by);
5739 if (0 != i) {
5740 bps = 1 & ~bps;
5741 save = ~0;
5742 xredraw = ~0;
5745 i = x_button_quad( bxb+1, bys, 9, byh, bx, by);
5746 if (0 != i) {
5747 bbs = 1 & ~bbs;
5748 save = ~0;
5749 xredraw = ~0;
5752 arg_hl = 0;
5753 if ((0 != bis) || (0 != bps) || (0 != bbs))
5754 arg_hl = ~0;
5757 break; /* only one loop */
5760 #ifdef USE_EXPOSE
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]);
5766 xprintf( stdout,
5767 "\t Serial %lu Send %s x %d y %d w %d h %d c %d\n",
5768 expose->serial,
5769 (0 == expose->send_event) ? "FALSE":"TRUE",
5770 expose->x,
5771 expose->y,
5772 expose->width,
5773 expose->height,
5774 expose->count );
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;
5794 #if 0
5795 xprintf( stdout, "X Event:\tExpose type %d %s\n",
5796 xev.type, xevtypes[xev.type] );
5797 xprintf( stdout,
5798 "\t Serial %lu Send %s x %d y %d w %d h %d c %d\n",
5799 expose->serial,
5800 (0 == expose->send_event) ? "FALSE":"TRUE",
5801 expose->x,
5802 expose->y,
5803 expose->width,
5804 expose->height,
5805 expose->count );
5806 #endif
5808 expose_serial = expose->serial;
5810 #endif
5812 /* look for keypress events */
5813 if ( x_check_twe( KeyPress, &xev ) )
5815 int keymax, keylen;
5816 char keybuf[16];
5817 XComposeStatus compose;
5818 KeySym keysym;
5819 unsigned short keyval;
5821 keymax = 1;
5822 /* try to convert key event into ASCII */
5823 keylen = XLookupString( (XKeyEvent *)&xev,
5824 keybuf, keymax, &keysym, &compose );
5825 keybuf[keylen] = 0;
5826 keyval = keybuf[0];
5829 /* NOTE: ADD MORE KEYS HERE, OR REMOVE UNUSED ONES :> */
5830 if (0 == keyval) {
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;
5838 xprintf(stdout,
5839 "\nX Event:\tKeyPress %04X %d\n", keyval, keyval);
5841 switch(keyval) {
5843 /*********************************************************** stream control */
5846 /* alpha control, only works for imlib2? */
5847 case '-':
5848 hist_scale -= 0.01;
5849 xredraw = ~0;
5850 if (hist_scale <= 0.0) {
5851 hist_scale = 0.0;
5852 iprintf(stdout, "Histogram scale at 0%%\n");
5853 xredraw = 0;
5855 break;
5856 case '+':
5857 hist_scale += 0.01;
5858 xredraw = ~0;
5859 if (hist_scale >= 1.0) {
5860 hist_scale = 1.0;
5861 iprintf(stdout, "Histogram scale at 100%%\n");
5862 xredraw = 0;
5864 break;
5866 /* toggle single or multi-file write */
5867 case 'm':
5868 arg_one = 1 & ~arg_one;
5869 fprintf(stdout, "Writing cuts to %s\n",
5870 (arg_one)?"one file":"multiple files");
5871 xredraw = ~0;
5872 save = ~0;
5873 break;
5875 /* toggle single or multi-file write */
5876 case 'b':
5877 arg_renum = 1 & ~arg_renum;
5878 fprintf(stdout, "Cut renumber: %s\n",
5879 (arg_renum)?"YES":"NO");
5880 xredraw = ~0;
5881 save = ~0;
5882 break;
5884 /* NULL packet at start of each cut toggle */
5885 case 'n':
5886 arg_nulls = 1 & ~arg_nulls;
5887 fprintf(stdout, "NULL packets: %s\n",
5888 (arg_nulls)?"YES":"NO");
5889 save = ~0;
5890 xredraw = ~0;
5891 break;
5893 /* clear all cuts */
5894 case 'c':
5895 clear_scuts();
5896 // show_scuts();
5897 xredraw = ~0;
5898 break;
5900 /* write all cuts to separate files */
5901 case 'w':
5902 cut_type = CUT_ALL;
5903 // show_scuts();
5904 write_scuts();
5905 break;
5907 /* write even cuts, [e] or [ENTER] keys (enter is CR not NL in X) */
5908 // case 13:
5909 case 'e':
5910 cut_type = CUT_EVEN;
5911 // show_scuts();
5912 write_scuts();
5913 break;
5915 /* write odd cuts */
5916 case 'r':
5917 cut_type = CUT_ODD;
5918 // show_scuts();
5919 write_scuts();
5920 break;
5922 /* display cuts to stdout */
5923 case 'd':
5924 // show_scuts();
5925 fprintf(stdout, "xhelp %d\n", xhelp);
5926 if (3 != xhelp) {
5927 xhelp = 3;
5928 } else {
5929 xhelp = 0;
5931 xredraw = ~0;
5932 write_cutfile(); // redundant with write at cut toggle
5933 break;
5935 /* display allocated memory usage */
5936 case 'g':
5937 arg_gop = 1 & ~arg_gop;
5938 fprintf(stdout, "GOP broken_link %s\n",
5939 (arg_gop)?"YES":"NO");
5940 xredraw = ~0;
5941 save = ~0;
5942 break;
5944 /* show current config */
5945 case '?':
5946 // show_keys(0); // TODO: move to console scan
5948 xhelp++;
5949 xhelp %= 5;
5950 xredraw = ~0;
5951 break;
5953 /* List allocations */
5954 case 'l':
5955 show_allocs();
5956 break;
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 */
5966 case XK_F1:
5967 arg_hl = 1 & ~arg_hl;
5968 xredraw = ~0;
5969 save = ~0;
5970 break;
5972 /* F2 toggles IPB legend and buttons */
5973 case XK_F2:
5974 arg_lb = 1 & ~arg_lb;
5975 xredraw = ~0;
5976 save = ~0;
5977 break;
5979 /* F3 toggles timecode at top */
5980 case XK_F3:
5981 arg_tc = 1 & ~arg_tc;
5982 xredraw = ~0;
5983 save = ~0;
5984 break;
5986 /* F4 toggles slider bar */
5987 case XK_F4:
5988 arg_sb = 1 & ~arg_sb;
5989 xredraw = ~0;
5990 save = ~0;
5991 break;
5993 /* F5 toggles eye candy, cornering, shading */
5994 case XK_F5:
5995 arg_ec = 1 & ~arg_ec;
5996 xredraw = ~0;
5997 save = ~0;
5998 break;
6000 /* toggle frame number/time-code display */
6001 case 't':
6002 arg_ft = 1 & ~arg_ft;
6003 xredraw = ~0;
6004 save = ~0;
6005 break;
6007 /* quit. NOTE: does not save cuts in case you hit [c]lear cuts by accident */
6008 case 3:
6009 case 'q':
6010 save_config();
6011 c_exit(0);
6012 break;
6014 /* ESC forces redraw of display */
6015 case 27:
6016 x_clear_display();
6017 xredraw = ~0;
6018 break;
6020 /* jump to previous cut */
6021 case 'a':
6022 i = find_prev_cut();
6023 // fprintf(stdout, "find prev cut %d\n", i);
6024 if (0 != i) {
6025 xoffset = i - (vw.w >> 1);
6026 xumove = ~0;
6029 break;
6030 /* jump to next cut */
6031 case 's':
6032 i = find_next_cut();
6033 // fprintf(stdout, "find next cut %d\n", i);
6034 if (0 != i) {
6035 xoffset = i - (vw.w >> 1);
6036 xumove = ~0;
6038 break;
6040 #if 0
6041 /* jump to previous i-frame if below last button3 preview packet count */
6042 case 'z':
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);
6047 break;
6049 break;
6051 /* jump to next i-frame if below last button3 preview packet count */
6052 case 'x':
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);
6057 xumove = ~0;
6058 break;
6060 break;
6061 #endif
6063 /******************************************************* stream navigation */
6064 /* jump to start of stream */
6065 case '[':
6066 case XK_Home:
6067 case XK_KP_Home:
6068 xumove = ~0;
6069 xoffset = 0;
6070 break;
6072 /* jump to end of stream */
6073 case ']':
6074 case XK_End:
6075 case XK_KP_End:
6076 xumove = ~0;
6077 xoffset = frame_idx - vw.w;
6078 break;
6079 /* scroll keys */
6080 case 'y':
6081 case XK_Page_Up:
6082 case XK_KP_Page_Up:
6083 uoffset = -2 * vw.w;
6084 xumove = ~0;
6085 break;
6087 case 'o':
6088 case XK_Page_Down:
6089 case XK_KP_Page_Down:
6090 uoffset = 2 * vw.w;
6091 xumove = ~0;
6092 break;
6094 /* skip greater than or equal to window width */
6095 case 'u':
6096 case XK_Up:
6097 case XK_KP_Up:
6098 uoffset = -1 * vw.w;
6099 xumove = ~0;
6100 break;
6102 case 'i':
6103 case XK_Down:
6104 case XK_KP_Down:
6105 uoffset = 1 * vw.w;
6106 xumove = ~0;
6107 break;
6109 /* Skip to previous or next sequence, I-frame single-step.
6110 This may be very slow on certain I-frame heavy FOX broadcasts.
6112 case 'j':
6113 case XK_Left:
6114 case XK_KP_Left:
6115 i = find_prev_iframe( xlfd );
6116 uoffset = i - xlfd;
6117 iprintf(stdout, "lfd %d i %d u %d\n", xlfd, i, uoffset);
6118 if (uoffset < 0) xumove = ~0;
6119 break;
6121 case 'k':
6122 case XK_Right:
6123 case XK_KP_Right:
6124 i = find_next_iframe( xlfd );
6125 uoffset = i - xlfd;
6126 iprintf(stdout, "lfd %d i %d u %d\n", xlfd, i, uoffset);
6127 if (uoffset > 0) xumove = ~0;
6128 break;
6130 #ifdef USE_MAXIMIZE
6131 /* FIXME: full screen toggle, has eventual fatal bug in X events,
6132 and the aspect ratio is really ugly except on 1920x1200 displays.
6134 case 'F':
6135 case 'f':
6136 xmaxi = 1 & ~xmaxi;
6137 xumove = ~0;
6138 xredraw = ~0;
6139 xoffset = 0;
6140 x_resize_display();
6141 save = ~0;
6142 break;
6143 #endif
6145 default:
6146 break;
6148 } /* switch */
6149 } /* keypress */
6151 /* viewpoint move request? */
6152 if (0 != xumove) {
6153 xredraw = ~0;
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 */
6160 xoffset = 0;
6162 /* no, clamp xoffset to last window width frames, plus 1 */
6163 } else {
6164 if (toffset < 0) toffset = 0;
6166 if ((toffset + vw.w) > frame_idx) {
6167 xoffset = frame_idx - vw.w;
6168 } else {
6169 xoffset = toffset;
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
6182 #ifdef 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 )) {
6186 char *xet = "";
6187 if (xev.type < 36) xet = xevtypes[xev.type];
6188 xprintf( stdout, "\nX Event type %d not handled: %s\n",
6189 xev.type, xet);
6191 #endif
6194 #ifdef USE_CONSOLE
6195 /**************************************************** xterm console control */
6196 static
6197 void
6198 console_scan( unsigned char *k )
6201 console_getch_fn( k );
6202 if (0 == *k) return;
6204 // WHOAMI;
6206 /* stream control, no navigation keys */
6207 switch( *k ) {
6209 /* toggle single or multi-file write */
6210 case 'm':
6211 arg_one = 1 & ~arg_one;
6212 fprintf(stdout, "Writing cuts to %s\n",
6213 (arg_one)?"one file":"multiple files");
6214 break;
6216 /* toggle NULL blank padding packets at start of each cut */
6217 case 'b':
6218 arg_nulls = 1 & ~arg_nulls;
6219 fprintf(stdout, "Writing NULL blanks to cuts: %s\n",
6220 (arg_nulls)?"YES":"NO");
6221 break;
6223 /* clear all cuts */
6224 case 'c':
6225 clear_scuts();
6226 // show_scuts();
6227 xredraw = ~0;
6228 break;
6230 /* write all cuts to separate files */
6231 case 'w':
6232 arg_one = 0;
6233 cut_type = CUT_ALL;
6234 // show_scuts();
6235 write_scuts();
6236 break;
6238 /* write even cuts, [e] or [ENTER] keys */
6239 case 10:
6240 case 'e':
6241 cut_type = CUT_EVEN;
6242 // show_scuts();
6243 write_scuts();
6244 break;
6246 /* write odd cuts */
6247 case 'r':
6248 cut_type = CUT_ODD;
6249 // show_scuts();
6250 write_scuts();
6251 break;
6253 /* display cuts to stdout */
6254 case 'd':
6255 // show_scuts();
6256 write_cutfile();
6257 break;
6260 /* toggle frame number/time-code display */
6261 case 't':
6262 arg_ft = 1 & ~arg_ft;
6263 xredraw = ~0;
6264 break;
6266 /* quit */
6267 case 'q':
6269 /* goes to c_exit after fixing console */
6270 console_exit(0);
6271 break;
6272 default:
6273 fprintf(stdout, "Key 0x%02X not handled\n", *k);
6274 break;
6277 /***************************************************** xterm console control */
6278 #endif
6281 /* fill out frames[].num from input data */
6282 static
6283 void
6284 process_frames ( void )
6286 int k, n, i, p, b, s, h;
6287 double ft, fi, fp, fb;
6288 frame_t *e, *f;
6290 WHOAMI;
6292 if (frame_idx < 2) return;
6294 i = p = b = h = s = 0;
6296 for (n = 0; n < frame_idx; n++) {
6297 k = 0;
6299 f = &frames[n];
6300 e = &frames[n + 1];
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;
6307 switch( f->pct ) {
6308 case 1:
6309 if (h < f->fpc) h = f->fpc;
6310 s += f->fpc;
6311 f->num = i;
6312 i++;
6313 break;
6314 case 2:
6315 f->num = p;
6316 p++;
6317 break;
6318 case 3:
6319 f->num = b;
6320 b++;
6321 break;
6322 default:
6323 break;
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 );
6338 // hist_hpn = h;
6339 // hist_hpn = s / (i-1);
6342 /* output frame sequence file from previous input file */
6343 static
6344 void
6345 output_frame_sequence ( void )
6347 int i, ok;
6348 char n[256], fs_name[ 256 ];
6349 FILE *f;
6350 struct in_frame_s in_frame;
6351 long long in_seq;
6353 WHOAMI;
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);
6366 in_seq = -1LL;
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);
6374 fflush( f );
6375 fclose( f );
6377 /* Build .esx file for Elementary Stream
6378 specified with -y option (should be .es extension specifies it?) */
6379 static
6380 void
6381 build_esx ( void )
6383 int bc, bd;
6384 unsigned int b, ss;
6385 unsigned char t, *p, c;
6386 long long o, r, s, z;
6387 frame_t *f;
6388 char n[256];
6390 WHOAMI;
6392 /* allocate mpeg2 ES buffer */
6394 if (NULL == es_buf) {
6395 fprintf(stdout, "%s es_buf not allocated\n", WHO);
6396 c_exit(1);
6399 memset(ipbx, 0, sizeof(ipbx));
6401 ss = 0;
6402 bc = 0;
6403 frame_idx = 0;
6404 sequence_idx = 0;
6405 s = 0;
6406 r = -1;
6407 z = in_size;
6408 z /= 100;
6409 if (0 == z) z = 1;
6410 o = 0;
6411 // o--;
6413 /* reset at start of stream only */
6414 b = 0xFFFFFFFF;
6416 /* MPEG ES stream has 00 00 01 for Start Code.
6417 Next byte is table type
6418 B3 Sequence
6419 00 Start of Picture (with picture type)
6420 Don't need to track it much further than that.
6422 while (1) {
6423 bc = read( in_file, es_buf, arg_mbz );
6424 if (0 == bc) {
6425 dprintf( stdout, "\n%s EOF %s\n", WHO, in_name);
6426 break;
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 */
6431 // break;
6434 p = es_buf;
6435 while (p < (es_buf + bc))
6437 o++;
6438 b <<= 8;
6439 b |= *p;
6440 p++;
6442 /* MPEG Start Code? */
6443 if (0x00000100 != (0xFFFFFF00 & b)) continue;
6444 c = 0xFF & b;
6446 // fprintf(stdout, "1%02X @ %9llX p %02X\n", c, o -4, (p - es_buf));
6448 #if 0
6449 /* bunch of branch prediction performance hits for no good reason */
6450 /* reset start code test after each start code: NO */
6451 // b = 0xFFFFFFFF;
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;
6464 #endif
6466 /* MPEG Sequence Start? */
6467 if (MPEG_SEQ == c) {
6468 s = o / z;
6469 if (r != s) {
6470 fprintf(stderr, "\r%s %2lld%% ", WHO, s);
6471 fprintf(stderr,
6472 "Frames %06d SEQ %06d I %06d P %06d B %06d",
6473 frame_idx, sequence_idx,
6474 ipbx[1], ipbx[2], ipbx[3]);
6475 r = s;
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];
6484 ipbx[1]++;
6485 f->pct = 1;
6486 f->vpn = o / TSZ;
6487 frame_idx++;
6489 snprintf(n, sizeof(n), "frames[%d]", frame_idx);
6490 frames = irealloc(frames, sizeof(frame_t), n);
6492 continue;
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 );
6507 if (2 != bd) break;
6508 o += 2;
6511 /* P or B frame? */
6512 t = 7 & (p[1]>>3);
6513 if ((t > 1) && (t < 4)) {
6514 f = &frames[frame_idx];
6515 ipbx[t]++;
6516 f->pct = t;
6517 f->vpn = o / TSZ;
6518 frame_idx++;
6520 snprintf(n, sizeof(n), "frames[%d]", frame_idx);
6521 frames = irealloc(frames, sizeof(frame_t), n);
6523 } else {
6525 /* Undefine PIC_DEBUG for B & P parsing errors. It won't affect cutting. */
6526 #ifdef PIC_DEBUG
6527 if (1 != t) {
6528 fprintf( stdout,
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);
6534 #endif
6536 continue;
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.
6551 static
6552 void
6553 build_tsx ( void )
6555 int i, ok, t;
6556 unsigned char *p, *r, *e, b;
6557 long long o, c, s, z;
6558 frame_t *f;
6559 char n[256];
6560 tsh_t *h;
6562 WHOAMI;
6564 if (NULL == ts_buf) {
6565 fprintf(stdout, "%s ts_buf not allocated\n", WHO);
6566 c_exit(1);
6569 h = &tsh;
6571 memset( ipbx, 0, sizeof(ipbx));
6572 ok = 1;
6574 frame_idx = 0;
6575 sequence_idx = 0;
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 */
6582 sc = 0xFFFFFFFF;
6584 /* MPEG ES stream has 00 00 01 for Start Code.
6585 Next byte is table type
6586 B3 Sequence
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 */
6593 o = -TSZ;
6595 while (ok > 0) {
6597 ok = read( in_file, ts_buf, arg_mbz );
6598 if (ok < 0) {
6599 fprintf( stdout, "\n%s ERROR %s\n", WHO, in_name);
6600 perror("");
6601 break;
6604 if (0 == ok) {
6605 fprintf( stdout, "\n%s EOF %d %s\n", WHO, ok, in_name);
6606 break;
6610 for (i = 0; i < ok; i += TSZ) {
6611 /* current byte offset for this packet */
6612 o += TSZ;
6613 p = ts_buf + i;
6614 r = p;
6616 if (MPEG_SYN != *r) {
6618 /* Any error in the read_buffer code not handling EOF properly? */
6619 if (o >= in_size) {
6620 fprintf( stdout,
6621 "\nread_buffer didn't see EOF @ %9llX!\n",
6622 in_size );
6623 break;
6625 fprintf( stdout, "\nTS Sync lost @ %9llX:\n", o);
6626 dump_packet( o, ts_buf, 0, 188 );
6628 return;
6631 parse_ts_header( p, h, arg_vpid);
6632 if (z < 0) return;
6633 if (0 == z) continue;
6635 /* FIXME: this shouldn't be needed but it is */
6636 if (h->pid != arg_vpid) continue;
6638 r = p + h->pso;
6640 e = p + TSZ;
6642 sc = 0xFFFFFFFF;
6643 while (r < e) {
6644 sc <<= 8;
6645 b = *r;
6646 sc |= b;
6647 r++;
6649 /* MPEG Start Code? */
6650 if (0x00000100 != (0xFFFFFF00 & sc)) continue;
6651 // if (sc > 0x1FF) continue;
6653 #if 0
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;
6665 #endif
6667 /* MPEG Sequence Start Code? */
6668 if (MPEG_SEQ == b) {
6670 /* TODO: can check Sequence marker bits for extra validation. */
6671 s = o / z;
6672 if (c != s) {
6673 fprintf(stderr, "\r%s %2lld%% ", WHO, s);
6674 fprintf(stderr,
6675 "Frames %06d SEQ %06d I %06d P %06d B %06d",
6676 frame_idx, sequence_idx,
6677 ipbx[1], ipbx[2], ipbx[3]);
6678 c = s;
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];
6689 ipbx[1]++;
6690 f->pct = 1;
6691 f->vpn = o / TSZ;
6692 frame_idx++;
6694 snprintf(n, sizeof(n), "frames[%d]", frame_idx);
6695 frames = irealloc( frames, sizeof(frame_t), n);
6697 continue;
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.
6707 if ( (r+1) < e ) {
6708 t = 7 & (r[1]>>3);
6709 /* P or B frame? */
6710 if ((t > 1) && (t < 4)) {
6711 f = &frames[frame_idx];
6712 ipbx[t]++;
6714 f->pct = t;
6715 f->vpn = o / TSZ;
6716 frame_idx++;
6717 snprintf(n, sizeof(n), "frame[%d]", frame_idx);
6718 frames = irealloc( frames, sizeof(frame_t), n);
6720 } else {
6722 /* Undefine PIC_DEBUG for B & P parsing errors. It won't affect cutting. */
6723 #ifdef PIC_DEBUG
6724 if (1 != t) {
6726 fprintf( stdout,
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);
6736 #endif
6738 } else {
6739 fprintf(stdout,"\nPIC type not in PSI1 packet!\n");
6741 continue;
6744 } /* for i loop */
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 */
6751 static
6752 void
6753 build_fsx ( void )
6755 double et, rate;
6757 WHOAMI;
6759 start_timer( &timer_fsx );
6760 /* use atscut to create the .tsx file */
6761 if (0 == arg_es) {
6762 build_tsx();
6764 if (sequence_idx != ipbx[1]) {
6765 char s[512] = "";
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",
6769 in_name);
6770 fprintf( stdout, "system( atscut -s %s )\n", in_name);
6771 snprintf( s, sizeof(s), "atscut -s %s\n", in_name);
6772 system( s );
6773 return;
6775 } else {
6776 /* create the .esx file with internal function */
6777 build_esx();
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;
6784 rate /= et;
6785 rate /= 1000000.0;
6787 #if 0
6788 fprintf( stdout,
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 );
6791 #endif
6793 fprintf( stdout, "\nCreated %sx in %.2f seconds @ %.2f MB/s\n",
6794 in_name, et, rate);
6796 output_frame_sequence();
6798 fprintf(stdout, "%s:%d ", WHO, WHERE);
6799 show_allocs();
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 */
6804 static
6805 void
6806 input_frame_sequence ( void )
6808 struct in_frame_s in_frame;
6809 long long in_seq;
6810 char fb[256];
6811 char m[256];
6812 char n[256];
6813 FILE *sf;
6814 int ok, i, li, fi, lf;
6815 frame_t *f;
6816 frame_t *f1;
6818 WHOAMI;
6820 filebase( fb, in_name, F_PFILE );
6822 snprintf( m, sizeof(m), "%s.%sx", fb, fxp );
6824 sequence_idx = 0;
6825 frame_idx = 0;
6826 iframe_idx = 0;
6828 fprintf( stdout, "Opening frame sequence %s\n", m );
6829 sf = fopen( m, "rb" );
6830 if (NULL == sf) {
6832 fprintf( stdout,
6833 "\nCreating frame sequence file %s, please wait\n", m);
6834 build_fsx();
6836 dprintf( stdout, "Opening new frame sequence %s\n", m );
6837 sf = fopen( m, "rb" );
6838 if (NULL == sf) {
6840 fprintf( stdout, "FATAL: File %s not created\n", m );
6841 c_exit(1);
6843 dprintf( stdout, "Opened %s\n", m );
6846 /* reset the list made by build_fsx, tests what build_fsx wrote */
6847 sequence_idx = 0;
6848 frame_idx = 0;
6849 iframe_idx = 0;
6851 /* start over again with a new list */
6852 free_frames();
6854 /* should set all pointers to NULL */
6855 alloc_frames();
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 */
6863 while (1) {
6864 ok = fread( &in_seq, sizeof(in_seq), 1, sf);
6865 if (1 != ok) {
6866 fprintf( stdout, "%s EOF %d @ Sequence %d\n",
6867 WHO, ok, sequence_idx);
6868 break;
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",
6874 WHO, sequence_idx);
6875 break;
6878 sequences[ sequence_idx ] = in_seq;
6879 dprintf( stdout, "Sequence %5d @ %llX\n", sequence_idx, in_seq);
6880 sequence_idx++;
6881 snprintf( n, sizeof(n), "sequences[%d]", sequence_idx );
6883 sequences = irealloc( sequences, sizeof(long long), n );
6884 if (NULL == sequences) {
6885 fprintf( stdout,
6886 "\n FATAL: can't realloc for sequence %d\n",
6887 sequence_idx);
6889 fclose( sf );
6890 c_exit(1);
6894 /* don't bother with frames if no sequences */
6895 if (0 == sequence_idx) {
6896 fprintf( stdout, "\nFATAL: no Sequences in %s\n", m);
6897 fclose( sf );
6898 c_exit(1);
6901 /* no found iframe */
6902 fi = 0;
6904 /* input frames */
6905 if (sequence_idx > 0)
6906 while (1) {
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 );
6911 /* end of file */
6912 if (0 == ok) {
6913 dprintf( stdout, "%s End of Frames @ %d\n",
6914 WHO, frame_idx);
6915 break;
6918 /* skip initial partial frames up to first Sequence */
6919 if ((0 == fi) && (1 != in_frame.pct)) continue;
6921 fi = ~0;
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 );
6935 iframe_idx++;
6936 snprintf( n, sizeof(n), "iframes[%d]", iframe_idx );
6938 iframes = irealloc( iframes, sizeof(int), n );
6939 if (NULL == frames) {
6940 fprintf( stdout,
6941 "FATAL: can't realloc for iframe %d\n",
6942 iframe_idx);
6943 fclose( sf );
6944 c_exit(1);
6948 frame_idx++;
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",
6955 frame_idx);
6956 fclose( sf );
6957 c_exit(1);
6961 fclose( sf );
6963 if ((0 == fi) || (0 == iframe_idx)) {
6964 fprintf( stdout, "\nFATAL: no Intra frames in %s\n", m);
6965 c_exit(1);
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 );
7008 li = 0;
7009 if (0 == arg_kls) {
7010 fi = 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++ ) {
7018 f = &frames[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 */
7025 if (li < 4) {
7026 for (i = lf; i < frame_idx; i++) {
7027 f = &frames[i];
7028 iprintf( stdout, "Truncating %c frame # %d \n",
7029 pictypes[3 & f->pct], i);
7030 f->pct = 1;
7031 // f->num = iframe_idx - 1;
7032 // f->vpn = 0;
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;
7041 frame_idx -= li;
7042 iframe_idx--;
7043 sequence_idx--;
7044 } else {
7045 li = 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 */
7063 frame_idx++;
7064 iframe_idx++;
7065 sequence_idx++;
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);
7072 if (li > 0)
7073 fprintf( stdout, " (%d frames cut)", li);
7075 fprintf( stdout, "\n");
7077 #if 0
7078 for (i = frame_idx - 6; i < frame_idx; i++ ) {
7079 f = &frames[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");
7094 #endif
7096 #if 0
7097 for (i = 0; i < frame_idx; i++ ) {
7098 f = &frames[i];
7099 f1 = &frames[i + 1];
7100 fprintf(stdout, "frames[%d] %c, packets %d\n",
7101 i, pictypes[f->pct], f1->vpn - f->vpn);
7103 #endif
7107 /* read cut file into scuts, after input_frame_sequence so iframes[] loaded */
7108 static
7109 void
7110 input_cuts ( void )
7112 FILE *f;
7113 int ok;
7114 char n[256], sc_name[256];
7116 WHOAMI;
7118 filebase( n, in_name, F_PFILE);
7119 snprintf( sc_name, sizeof(in_name), "%s.%sc", n, fxp);
7121 scut_idx = 0;
7122 clear_scuts();
7124 f = fopen( sc_name, "r");
7125 if (NULL == f) {
7126 fprintf( stdout, "%s %s not found\n", WHO, sc_name);
7127 return;
7130 while(1) {
7131 ok = fread( &scuts[scut_idx], sizeof(int), 1, f);
7132 if (0 == ok) break;
7134 // FIXME: put this back, output redux for cl-usage, disabled for debug only
7135 if (0 == arg_cut)
7136 dprintf( stdout,
7137 "Cut %02d @ SEQ %5d I-Fframe[%5d] = %6d "
7138 "Picture Type %c-Frame (%d)\n",
7139 scut_idx + 1,
7140 scuts[scut_idx],
7141 scuts[scut_idx],
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;
7148 scut_idx++;
7151 fclose(f);
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 */
7160 static
7161 void
7162 input_scan ( void )
7164 unsigned char buf[ TSZ ], *p, *r;
7165 unsigned short pids[ 0x2000 ];
7166 int f, ok, z;
7167 struct stat64 fs;
7168 long long i, c;
7169 tsh_t *h;
7171 WHOAMI;
7173 h = &tsh;
7175 /* input scan is not needed when source is Video Elementary Stream */
7176 if (0 != arg_es) return;
7178 p = buf;
7179 pmt_pid = -1;
7180 pmt_pkts = 1;
7182 memset( pids, 0, sizeof( pids ));
7183 memset( pat, 0, sizeof(pat));
7184 memset( pmt, 0, sizeof(pmt));
7186 ok = stat64( in_name, &fs );
7187 if (0 != ok) {
7188 fprintf( stdout, "%s stat %d input %s\n", WHO, ok, in_name );
7189 c_exit(1);
7191 in_size = fs.st_size;
7192 c = in_size / TSZ;
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);
7198 /* open file */
7199 f = open( in_name, FILE_RMODE );
7200 if (f < 2) {
7201 fprintf( stdout, "%s failed open %s\n", WHO, in_name);
7202 perror("");
7203 c_exit(1);
7206 for (i = 0; i < c; i++) {
7207 ok = read( f, &buf, TSZ );
7208 if (0 == ok) break;
7210 if (TSZ != ok) {
7211 fprintf( stdout, "%s failed %d read %s\n", WHO, ok, in_name);
7212 /* nothing to do, time to exit */
7213 // close(f);
7214 // c_exit(1);
7215 break;
7218 /* arg_vpid has not yet been determind so use 0 for pid parameter */
7219 z = parse_ts_header( p, h, 0 );
7221 if (z < 0) {
7222 fprintf(stdout, "%s Transport Stream Sync lost.\n", WHO);
7223 c_exit(1);
7225 pids[ h->pid ]++;
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) {
7236 i--;
7237 continue;
7240 r = p + h->pso;
7242 if ((MPEG_SYN == *pat) && (MPEG_SYN == *pmt)) {
7243 if (0 != h->psi) {
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.
7265 if (0 != h->psi) {
7266 if (MPEG_PMT == p[5]) { // r[1]
7267 if (-1 == pmt_pid)
7268 dprintf(stdout, "Found PMT PID %04X\n", h->pid);
7269 pmt_pid = 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) {
7279 if (0 == h->psi) {
7280 memcpy( pmt + TSZ, p, TSZ );
7281 pmt_pkts = 2;
7288 close(f);
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");
7301 c_exit(1);
7302 } else {
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");
7308 c_exit(1);
7309 } else {
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");
7317 c_exit(1);
7319 fprintf( stdout, "TS VID PID is %04X\n", arg_vpid);
7320 fprintf( stdout, "TS AUD PID is %04X\n\n", arg_apid);
7322 return;
7325 static
7326 void
7327 input_files ( void )
7329 WHOAMI;
7331 input_scan();
7332 input_frame_sequence();
7333 process_frames();
7334 input_cuts();
7338 /* wait for x key events to either zoom/move viewport or quit program */
7339 static
7340 void
7341 x_scan_loop ( void )
7343 if (0 != arg_xmsg) WHOAMI;
7345 /* only redraws if xredraw is not zero */
7346 while(1) {
7347 x_event();
7348 x_redraw_display();
7349 #ifdef USE_CONSOLE
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 );
7353 if (0 != ckey)
7354 fprintf( stdout, "Console key 0x%02X\n", ckey);
7355 #endif
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.
7368 static
7369 void
7370 benchmark_framerate ( void )
7372 int c, f, g, h, i, j;
7374 WHOAMI;
7376 c = sequence_idx - 2;
7377 if (c >= arg_bfr) c = arg_bfr;
7379 xumove = 0;
7380 xoffset = 0;
7381 xlfd = -1;
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;
7396 g /= 10000000;
7398 h = (100 * f) + g;
7400 i = (10000 * c) / h;
7401 j = i % 100;
7403 i /= 100;
7405 fprintf(stdout,
7406 " %d Intra frames in %d.%02d seconds at %d.%02d FPS\n\n",
7407 c, f, g, i, j );
7408 c_exit(1);
7412 static
7413 void
7414 usage ( char ** argv )
7416 WHOAMI;
7418 fprintf( stdout,
7419 "\n"
7420 "USAGE:\n"
7421 "\t%s [options] file.{ts|tsx}\n"
7422 "\n"
7423 "OPTIONS:\n"
7424 "\n"
7425 " -h Help for %s %s %s\n"
7426 " -v Show xtscut banner and exit\n"
7427 "\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"
7432 #endif
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"
7435 "\n"
7436 " -f Function name verbosity (DEBUG)\n"
7437 " -i Intra frame processing verbosity (DEBUG)\n"
7438 " -x X function verbosity (DEBUG)\n"
7439 "\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"
7444 #ifdef USE_LIBVO
7445 " -m libvomode X video mode, 0 XShm RGB, 1 Xv YV12, 2 Xv YUYV\n"
7446 #endif
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);
7453 show_keys(1);
7455 fprintf(stdout,
7456 "NOTES:\n"
7457 "\n"
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"
7462 "\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"
7466 "\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"
7469 "\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"
7473 "\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"
7477 "\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"
7482 "\n"
7483 " Default is to make one file. Use option -1 to make separate files.\n"
7484 "\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"
7488 "\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"
7492 "\n"
7493 " Try the new-style mouse scroll-wheel for navigation!\n"
7494 "\n"
7495 " If you cut to a different physical volume, it usually writes faster.\n"
7496 "\n"
7497 "SEE ALSO: xtscut(1)\n"
7498 "\n"
7499 "\n");
7501 exit(0);
7505 static
7506 void
7507 parse_args ( int argc, char ** argv )
7509 int c, i, ok, mbz;
7510 char *p, n[256], s[256]; /* temps for -o and -p and source */
7511 struct stat64 fs;
7513 WHOAMI;
7515 mbz = 4096;
7517 if (1 == argc) {
7518 usage( argv );
7519 fprintf( stdout, "Need Video .ts or .es file to process\n\n");
7520 exit( 1 );
7523 memset( n, 0, sizeof(n));
7524 memset( arg_base, 0, sizeof(arg_base));
7526 i = 0;
7527 #ifdef DEBUG
7528 fprintf( stdout, "args:\n");
7529 for (i = 0; i < argc; i++)
7530 fprintf( stdout, "arg[%d] %s\n", i, argv[i]);
7531 #endif
7533 while ( EOF != ( c = getopt( argc, argv,
7535 "1dfhinrxv"
7537 #ifdef USE_GOP_BROKEN_LINK
7539 #endif
7540 "a:b:c:k:j:o:p:s:u:w:z:"
7542 #ifdef USE_LIBVO
7543 "m:"
7544 #endif
7545 ) ) )
7547 switch(c) {
7550 /* alpha-blending color, may require imlib to use it */
7551 case 'u':
7552 arg_alpha = 0xFF & atoi(optarg);
7553 break;
7555 /* jump to this frame number */
7556 case 'j':
7557 if (NULL != optarg) arg_jump = atoi(optarg);
7558 break;
7560 /* -z 4096 sets input, output and mpeg es buffers to this size */
7561 case 'z':
7562 if (NULL != optarg) mbz = atoi(optarg);
7563 break;
7565 #ifdef USE_LIBVO
7566 case 'm':
7567 if (NULL != optarg) arg_vo = 3 & atoi(optarg);
7568 break;
7569 #endif
7571 /* toggle: nz sets GOP broken link flag on the first GOP in each cut */
7572 case 'g':
7573 #ifndef USE_GOP_BROKEN_LINK
7574 fprintf( stdout, "Recompile with USE_GOP_BROKEN_LINK defined.\n");
7575 exit(1);
7576 #else
7577 arg_gop = 1 & ~arg_gop;
7578 #endif
7579 break;
7581 /* benchmark frame render, 999 I frames max */
7582 case 'b':
7583 arg_bfr = 0;
7584 if (NULL != optarg)
7585 arg_bfr = atoi(optarg);
7586 if (arg_bfr < 0) arg_bfr = 0;
7587 if (0 == arg_bfr) {
7588 fprintf(stdout,
7589 "Please specify how many frames to benchmark.\n\n");
7590 exit(1);
7592 break;
7594 /* toggle keep last sequence */
7595 case 'k':
7596 arg_kls = 1 & atoi(optarg);
7597 break;
7599 /* MPEG2 acceleration model override */
7600 case 'a':
7601 if (NULL != optarg)
7602 arg_mx = 7 & atoi(optarg);
7603 break;
7605 /* toggle 1s NULL packet write to start of every cut */
7606 case 'n':
7607 arg_nulls = 1 & ~arg_nulls;
7608 fprintf( stdout, "Writing NULL packets: %s\n",
7609 (0 != arg_nulls)?"YES":"No");
7610 break;
7612 /* toggle compiled delay */
7613 case 'd':
7614 arg_delay = 1 & ~arg_delay;
7615 break;
7617 /* toggle compiled -r cut re-numbering default */
7618 case 'r':
7619 arg_renum = 1 & ~arg_renum;
7620 break;
7622 /* specifiy cut type, all is default if no parm match */
7623 case 'c':
7624 cut_type = CUT_ALL;
7625 arg_cut = ~0;
7626 if (NULL != optarg) {
7627 /* only first letter is checked */
7628 p = optarg;
7629 if ('e' == *p) cut_type = CUT_EVEN;
7630 if ('o' == *p) cut_type = CUT_ODD;
7631 if ('a' == *p) cut_type = CUT_ALL;
7633 break;
7635 /* even/odd cut to one file toggle */
7636 case '1':
7637 arg_one = 1 & ~arg_one;
7638 break;
7640 /* function name debug verbosity */
7641 case 'f':
7642 arg_fmsg = ~0;
7643 break;
7645 /* Intra frame debug verbosity */
7646 case 'i':
7647 arg_imsg = ~0;
7648 break;
7650 /* X debug verbosity */
7651 case 'x':
7652 arg_xmsg = ~0;
7653 break;
7655 /* help on usage */
7656 case 'h':
7657 usage( argv );
7658 break;
7660 /* window divisor, 1 2 4 or 8. even divisor handles interlaced w/o banding */
7661 case 'w':
7662 if (NULL != optarg) {
7663 arg_wd = atoi(optarg);
7664 if (0 == arg_wd) {
7665 vw.w = 960; /* override config file */
7666 vw.h = 544;
7667 break;
7670 if ( (1 != arg_wd) && (2 != arg_wd)
7671 && (4 != arg_wd) && (8 != arg_wd) ) {
7672 fprintf(stdout,
7673 "Invalid window divisor: use 1, 2, 4 or 8\n\n");
7674 c_exit(1);
7677 break;
7679 /* show program banner (already done) and exit */
7680 case 'v':
7681 fprintf( stdout, "\n");
7682 exit(0);
7683 break;
7685 /* histogram scale % of screen height */
7686 case 's':
7687 if (NULL != optarg) hist_scale = atof(optarg);
7688 break;
7690 /* output name */
7691 case 'o':
7692 if (NULL != optarg)
7693 snprintf( arg_base, sizeof(arg_base),"%s", optarg);
7694 break;
7696 /* output path */
7697 case 'p':
7698 if (NULL != optarg)
7699 snprintf(arg_path, sizeof(arg_path),"%s", optarg);
7700 break;
7702 default:
7703 break;
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");
7713 exit(0);
7716 strncpy( s, argv[optind], sizeof(s) );
7717 s[255] = 0;
7720 /* see if input is .es file */
7722 p = strrchr(s, '.');
7723 if (NULL != p) {
7724 p++;
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; }
7740 arg_mbz = mbz;
7742 if (0 == arg_es) {
7743 fprintf(stdout, "TS Buffers are using %d bytes %d packets\n",
7744 mbz, mbz / TSZ);
7745 } else {
7746 fprintf(stdout, "ES Buffers are using %d bytes\n", mbz);
7749 if (CUT_ALL == arg_cut) arg_one = 0;
7750 fxp = fxt[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 );
7755 if (0 == *arg_path)
7756 filebase( arg_path, argv[optind], F_PATH);
7758 /* if arg path non blank, make sure it has / at end of string */
7759 p = arg_path;
7760 if (0 != *p) {
7761 p += strlen(arg_path);
7762 p--;
7764 /* don't overrun end of string */
7765 if (strlen(arg_path) < (sizeof(arg_path)-2) ) {
7766 if ('/' != *p) {
7767 p++;
7768 *p ='/';
7769 p++;
7770 *p = 0;
7772 } else {
7773 fprintf( stdout, "-p option too large, max is %d chars\n",
7774 sizeof(arg_path)-2);
7775 exit(1);
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 */
7784 p = arg_base;
7785 if (0 == *p) {
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);
7793 if (0 != *in_name)
7794 fprintf( stdout, "Input stream file name: %s\n", in_name);
7795 if (0 != *out_base)
7796 fprintf( stdout, "Output path & basename: %s%s\n",arg_path, out_base);
7798 if (0 != *in_name) {
7799 ok = stat64( in_name, &fs );
7800 if (0 != ok) {
7801 fprintf( stdout, "Could not stat %d input file %s\n",
7802 ok, in_name );
7803 perror("");
7804 c_exit(1);
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 );
7812 if (in_file < 3) {
7813 fprintf( stdout, "Could not open input file %s\n", in_name );
7814 exit (1);
7816 fprintf(stdout, "Opened %s, inode %d bufz %d\n",
7817 in_name, (int)in_inode, arg_mbz);
7819 #ifdef USE_LIBVO
7820 // arg_vo = 1; /* 0 and 2 are fixed? [temporarily out of service] */
7821 #endif
7825 static
7826 void
7827 show_boilerplate ( void )
7829 WHOAMI;
7831 fprintf(stdout,
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 )
7849 WHOAMI;
7851 arg_v = argv;
7852 arg_c = argc;
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);
7859 init_allocs();
7861 test_clock_res();
7863 show_boilerplate();
7865 load_config();
7867 parse_args( argc, argv );
7869 save_config();
7871 // show_config();
7873 #ifdef USE_SIGNALS
7874 signal_init();
7875 #endif
7876 alloc_frames();
7878 alloc_streams();
7880 clear_arrays();
7882 input_files();
7884 build_scut_frames();
7886 /* will read first SEQ + I-Frame */
7887 init_mpeg2();
7889 /* make it draw the first iframe again, if needed */
7890 xlfd = -1;
7892 /* move this one around to inspect the allocations as you go */
7893 // show_allocs();
7895 /* run the gui if no -c option given */
7896 if (0 == arg_cut) {
7897 if (0 != arg_bfr) {
7898 fprintf( stdout, "Benchmarking %d Intra frames in %s\n",
7899 arg_bfr, in_name);
7900 } else {
7901 fprintf( stdout, "Previewing Intra frames in %s\n\n",
7902 in_name);
7905 x_init_display(); /* set up X display */
7907 #ifndef USE_LIBVO
7908 x_init_imlib(); /* load splash image */
7909 #endif
7910 if (0 == arg_bfr) {
7911 x_draw_image( WHO );
7912 } else {
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 */
7920 write_scuts();
7921 free_frames();
7923 return 0;