#------------------------------------------------------------------------
#
# 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