#------------------------------------------------------------------------
#
# Copyright (c) 1997-1998 by Cornell University.
#
# See the file "license.txt" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
#------------------------------------------------------------------------
package require DvmBasic
package require DvmMpeg
package require DvmAvi
package require DvmColor
#---------------------------------------------------------------
# This script converts a MPEG Video Sequence to a series of ppm
# file.
#---------------------------------------------------------------
if {$argc != 3} {
puts "Usage: $argv0 input output.avi codec :"
exit
} else {
set inname [lindex $argv 0]
set outname [lindex $argv 1]
set codec [lindex $argv 2]
}
############################################################
# Notes: Make sure the avi_stream_close and avi_file_close
# get called, even in case of premature ending of the mpeg.
# Should put the mpeg_parse stuff is in a catch{}.
# iv32 is the fastest, but worst quality
# iv50 is the the best quality, but iv41 is the slowest
############################################################
proc swap_buffer {a b} {
upvar $a aa
upvar $b bb
set temp $aa
set aa $bb
set bb $temp
}
proc check_bitstream_underflow {bs bp chan size} {
set off [bitparser_tell $bp]
set left [bitstream_bytes_left $bs $off]
if {$left < $size} {
bitstream_shift $bs $off
bitstream_channel_read $bs $chan $left
bitparser_seek $bp 0
}
}
#----------------------------------------------------------------
# open file, create new bitparser, new bitstream, read first
# 65535 bytes from file into bitstream and attached the bitparser
# to the bitstream
#----------------------------------------------------------------
set bp [bitparser_new]
set bs [bitstream_new 65535]
set file [open "$inname" r]
fconfigure $file -translation binary -buffersize 65535
bitstream_channel_read $bs $file 0
bitparser_wrap $bp $bs
#----------------------------------------------------------------
# parse the mpeg sequence header and find out the dimension and
# size of the frames. care must be taken if frame is not multiple
# of 16
#----------------------------------------------------------------
set hdr [mpeg_seq_hdr_new]
mpeg_seq_hdr_find $bp
mpeg_seq_hdr_parse $bp $hdr
set seq_w [mpeg_seq_hdr_get_width $hdr]
set seq_h [mpeg_seq_hdr_get_height $hdr]
set pic_size [mpeg_seq_hdr_get_buffer_size $hdr]
set rem_w [expr $seq_w % 16]
set rem_h [expr $seq_h % 16]
if {$rem_w != 0} {
set w [expr $seq_w + 16 - $rem_w]
} else {
set w $seq_w
}
if {$rem_h != 0} {
set h [expr $seq_h + 16 - $rem_h]
} else {
set h $seq_h
}
set halfw [expr $w/2]
set halfh [expr $h/2]
#----------------------------------------------------------------
# allocate a bunch of byte buffer, sc buffer, mv buffer,
# mpeg_pic_hdr
#----------------------------------------------------------------
set y [byte_new $w $h]
set prevy [byte_new $w $h]
set futurey [byte_new $w $h]
set r [byte_new $seq_w $seq_h]
set g [byte_new $seq_w $seq_h]
set b [byte_new $seq_w $seq_h]
set b_r [byte_new $seq_w $seq_h]
set b_g [byte_new $seq_w $seq_h]
set b_b [byte_new $seq_w $seq_h]
set u [byte_new $halfw $halfh]
set prevu [byte_new $halfw $halfh]
set futureu [byte_new $halfw $halfh]
set v [byte_new $halfw $halfh]
set prevv [byte_new $halfw $halfh]
set futurev [byte_new $halfw $halfh]
set outy [byte_clip $y 0 0 $seq_w $seq_h]
set outu [byte_clip $u 0 0 [expr $seq_w/2] [expr $seq_h/2]]
set outv [byte_clip $v 0 0 [expr $seq_w/2] [expr $seq_h/2]]
set fwdmv [vector_new [expr $w/16] [expr $h/16]]
set bwdmv [vector_new [expr $w/16] [expr $h/16]]
set scy [sc_new [expr $w/8] [expr $h/8]]
set scu [sc_new [expr $w/16] [expr $h/16]]
set scv [sc_new [expr $w/16] [expr $h/16]]
set fh [mpeg_pic_hdr_new]
#----------------------------------------------------------------
# Initialize the AVI writing stuff
#----------------------------------------------------------------
set bnum 0
set togo 0
set a_hdr [avi_file_create "$outname"]
# Args: hdr codec width height fps keyframeinterval quality bitrate
set a_str [avi_video_stream_create $a_hdr $codec $seq_w $seq_h 30 30 75 50000]
#----------------------------------------------------------------
# now really start decoding the video frames. in each loop we
# make sure there are at least pic_size bytes available in the
# bitstream buffer.
#----------------------------------------------------------------
set len [mpeg_pic_hdr_find $bp]
set counter 0
while {($len != -1)&&($counter < 300)} {
check_bitstream_underflow $bs $bp $file $pic_size
mpeg_pic_hdr_parse $bp $fh
set play_order [mpeg_pic_hdr_get_temporal_ref $fh]
set type [mpeg_pic_hdr_get_type $fh]
if {$type == "i"} {
swap_buffer futurey prevy
swap_buffer futureu prevu
swap_buffer futurev prevv
mpeg_pic_i_parse $bp $hdr $fh $scy $scu $scv
sc_i_to_byte $scy $y
sc_i_to_byte $scu $u
sc_i_to_byte $scv $v
yuv_to_rgb_420 $y $u $v $r $g $b
swap_buffer y futurey
swap_buffer u futureu
swap_buffer v futurev
} elseif { $type == "p"} {
swap_buffer futurey prevy
swap_buffer futureu prevu
swap_buffer futurev prevv
mpeg_pic_p_parse $bp $hdr $fh $scy $scu $scv $fwdmv
sc_p_to_y $scy $fwdmv $prevy $y
sc_p_to_uv $scu $fwdmv $prevu $u
sc_p_to_uv $scv $fwdmv $prevv $v
yuv_to_rgb_420 $y $u $v $r $g $b
swap_buffer y futurey
swap_buffer u futureu
swap_buffer v futurev
} else {
mpeg_pic_b_parse $bp $hdr $fh $scy $scu $scv $fwdmv $bwdmv
sc_b_to_y $scy $fwdmv $bwdmv $prevy $futurey $y
sc_b_to_uv $scu $fwdmv $bwdmv $prevu $futureu $u
sc_b_to_uv $scv $fwdmv $bwdmv $prevv $futurev $v
yuv_to_rgb_420 $y $u $v $r $g $b
}
# Display order conversion code
if {$play_order == $togo} {
avi_video_frame_write $a_str $r $g $b
#puts -nonewline stderr "$play_order "
incr togo
if {$bnum == $togo} {
avi_video_frame_write $a_str $b_r $b_g $b_b
#puts -nonewline stderr "$bnum "
set bnum 0
incr togo
}
} elseif {$play_order > $togo} {
swap_buffer r b_r
swap_buffer g b_g
swap_buffer b b_b
set bnum $play_order
} else {
set togo 0
if {$play_order == $togo} {
avi_video_frame_write $a_str $r $g $b
#puts -nonewline stderr "$play_order "
incr togo
} else {
swap_buffer r b_r
swap_buffer g b_g
swap_buffer b b_b
set bnum $play_order
}
}
mpeg_pic_hdr_find $bp
set currCode [mpeg_get_curr_start_code $bp]
if {$currCode == "seq-end-code"} {
break
}
incr counter
puts -nonewline "$counter "
flush stdout
}
mpeg_pic_hdr_free $fh
avi_stream_close $a_str
avi_file_close $a_hdr
byte_free $y
byte_free $prevy
byte_free $futurey
byte_free $r
byte_free $g
byte_free $b
byte_free $b_r
byte_free $b_g
byte_free $b_b
byte_free $u
byte_free $prevu
byte_free $futureu
byte_free $v
byte_free $prevv
byte_free $futurev
vector_free $fwdmv
vector_free $bwdmv
sc_free $scy
sc_free $scu
sc_free $scv