ECE291 Computer Engineering II Lockwood, Spring 1999

MULTI-AVI-SYNCHRONIZATION

Team Members:
 
Name
Razvan Mathias Project Design, Documentation maintenance, Video Decoding
Peter Prokopios Video Display
Sidney Thong Network, AVI Sound
Ari Silverman MAVIS Interface


Table of Contents

Introduction
Problem Description
Implementation
    Message Subsystem
    Message Packet Types
    Memory Allocation
Coding Specifics
    Main
    Major Variables Used
    External Routines


Introduction

   Mavis is an application which allows playing an AVI file across more than one screen to allow for distant viewing of digital movie files.  To achieve such a task, one must own multiple computers that are networked together, each with its own monitor.  An arbitrary computer will be designated the "host" which will play sound and take input from the user, controlling the other screens for synchronization and file seeking resolution.
 
 
Keyboard Shortcuts
Numbers 1-9 (only on  host) set that number as the length of a side in terms monitors, ie 1=> 1 screen, 2=> 4 screens, 3=> 9 screens...
Tab changes to the next 
P (only on host) will pause when playing, or play when paused
X (only on host) Stop all 
R (only on host) Rewinds a few frames
F (only on host) Fast Forwards a few frames
Up or Down (only on host) changes the volume of the sound
Escape On Host: stops video and closes all clients
On Clients: closes that individual client

 
Mouse Commands (only in host)
Click Play Plays the video
Click Pause Pauses the video
Click Exit Exits video
Click Stop Stops the video and exits.
Click on progress bar Takes you ahead or behind in the video

 


Problem Description


    There are many technical challenges that have to be overcome before this project takes off.  The following is a list of the major tasks and their descriptions:
 



 

Implementation

    MAVIS will be built around a client/server model.  The host and client are integrated in one common application.  Host mode is an option triggered by a command-line switch.  There can only be one host existing on a network running the MAVIS application.  Each of the individual computers will store a local copy of the avi file to be displayed.  Each will stream and decompress the file from disk.  The host application will also run as a client.  This way, the client code will be localized, and reused throughout the system.  Signals are sent by a subsystem on the host computer.  These signals will be accepted by the host and the clients, which will respond by playing the proper video sequence.
 

Message Subsystem:

    A messaging system will be used to do all of the synchronization in the project.  There are two basic sources for the synchronization messages.  The first is the sound playing system, which will send a timing message out every time a buffer has finished emptying.  The MainLoop in the application will play the video at it's own pace (generally a bit faster than necessary); this loop will respond to the synchronization messages, which will slow the video down slightly in order to keep in step with the sound.  Keeping the sound buffer small enough will ensure that synchronization will occur frequently, making it unnoticable by the human eye.

    Actually, two sound buffers will be used.  While one is playing, the other will be refilling in the MainLoop.  An interrupt will occur when the first buffer has completed playing, triggering a sychronization message (PLAY or IDLE packets) to be propagated to the client subsystems.  On the clients, RecievePackets will take the messages and update the local machine's variables to reflect the arrival of the synchronization message.  The MainLoop will then respond to these changes as shown:

    In addition to the synchronization messages will be seek messages (or JUMP packets) which will cause the MainLoop to seek a for a certain location within a file and then enter and idle (paused) state, waiting for a play message to be created.  These messages will originate in the MouseHandler and KbdHandler functions.
    The message structure is made simple so that a common method can be used to create all outgoing packets from the host application.  The messages contain a type (PLAY, IDLE, JUMP, and EXIT), and an identifier indicating the frame number.  If play is encountered by the main application, the packet is used for synchronization purposes.  If IDLE is found, the client does nothing (as in the paused state).  If JUMP is found, the client seeks the frame number in the file, and then enters an IDLE state waiting for a play to be sent by the host (this is done to ensure synchronization among the different screens).  If EXIT is found, the client application simply quits.
 

Message Packet Types:
 
Packet Name Additional Information Description
IDLE GlobalFrameNum tells clients to enter IDLE state
PLAY GlobalFrameNum tells clients to enter PLAY state, and also synchronizes them by having them wait if they are ahead
STOP none tells all clients to enter EXIT state
GLOBALNUMSCREENS Number of Screens Updates GlobalNumScreens on all client machines

 

Memory allocation requirements:

    The allocation of memory will be important in solving the real-mode programming problem.  The following is a breakdown of the AVI file as it moves from the file, through the application's memory, and to the respective output devices.

    The basic flow of the data is fairly simple.  First the file is read from disk.  It is then written to two buffers, either video or audio; these buffers simply contain the chunks that were read from the hard disk.  The video chunks are decompressed in a buffer in another segment.  After this buffer, the correct portion of the image is StretchBlt'ed to the video memory.  An uninterrupted sound stream is created and sent to fill one of two buffers; while one buffer fills with data, the other plays on the sound card.


Coding Specifics
;==============================================================
;CalcIndexX
;Input:  DI is the current column on screen to calculate
;Output:  DL is the byte to display to the screen
;     ;DS:[SI+BX] points to BMP segment
;Purpose: calculate the correct index horizontally using formula
;     DX=Row#*VIDwidth/SCRwidth+OffSetX
;Coded By Peter Prokopios

;==============================================================
;CalcIndexY
;Input:  BP is the row to calculate(display) on the screen
;Output:  SI will be the calculated bmp row to be used
;Purpose: uses the following equation to calculate bmp row to display
;     SI=(OffSetY-Row#*VIDHight/SCRhight)*TVIDwidth
;Coded By Peter Prokopios

;==============================================================
 InitInterface PROC NEAR
; Purpose:  Display the introductions screen and allow the user to set some
;              initial variables such as the file name and the host status
; Input: none
; Output:   Initializes variables that need to be set by the user
; Coded by : Ari Silverman

;==============================================================
MakeHost PROC NEAR
; Purpose:  fill in the appropriate box on the introduction screen to indicate
;           a host computer
; Input: none
; Output:   Draws a box to the screen
; Coded by : Ari Silverman

MakeClient PROC NEAR
; Purpose:  fill in the appropriate box on the introduction screen to indicate
;           a host computer
; Input: none
; Output:   Draws a box to the screen
; Coded by : Ari Silverman

;==============================================================
SetPallette PROC NEAR
; Purpose:  Sets the pallette for the 256 color registers on the video card
; Input: ds:[si] contains location of pallette in memory
; Output:   Updates pallette register
; Coded by : Ari Silverman

;==============================================================
EnterAVIFile PROC NEAR
; Purpose:  Allows the user to enter which avi file they want to play.
;           Does not let the user enter more than 8.3 characters (DOS filenames)
;           and allows the user to backspace, but not past the first character
; Input: None
; Output:   Changes the file name, and updates the screen
; Coded by : Ari Silverman

;==============================================================
DisplayFilename PROC NEAR
; Purpose:  Displays the avi filename to the screen.
; Input: None
; Output:   Updates the screen
; Coded by : Ari Silverman

;==============================================================
DrawSolidBox PROC NEAR
; Purpose:  Draws a solid box with variable dimensions, color, and placement
; Input: di = location on screen
;        bx = height of box
;        cx = length of box
;        al = color of box (corresponding to pallette entry 0....255)
; Output:   Updates the screen
; Coded by : Ari Silverman

;==============================================================
OpenFile PROC NEAR
; Purpose:  Opens a file
; Input: dx = offset of name of file
; Output:   ax = file handle
;           carry is set if unsuccessful
; Coded by : Ari Silverman

;==============================================================
CloseFile PROC NEAR
; Purpose:  Closes a file
; Input: bx = file handle
; Output:   none
; Coded by : Ari Silverman

;==============================================================
LoadFile PROC NEAR
; Purpose:  Loads data from a file into memory
; Input: bx = file handle
;        ds:[dx] = begining location in memory place where file is to be written
;        cx = number of bytes to be read from file
; Output:   carry is set if unsuccessful
; Coded by : Ari Silverman

;==============================================================
clrScreen PROC NEAR
; Purpose:  Clears the screen to pallette entry zero
; Input: none
; Output:   Updates the screen
; Coded by : Ari Silverman

;==============================================================
DrawBMP PROC NEAR
; Purpose:  Streams a bitmap from a file onto the screen
;           Assuming a 320X200 image is to be read (64000 bytes)
; Input: bx = file handler
;        file pointer is set to begining of data to be read
; Output:   Updates the screen
; Coded by : Ari Silverman

;==============================================================
KbdHandler PROC NEAR
; Purpose:  Process Keyboard inputs
;           Accessed using a doscall
; Input:    none
; Output:   Updates the appropriate variables and calls appropriate procedures
; Coded by : Ari Silverman

;==============================================================
MouseHandler PROC NEAR
; Purpose:  Process Mouse inputs
;           Accessed by reading status of mouse
; Input:    none
; Output:   Updates the appropriate variables and calls appropriate procedures
; Coded by : Ari Silverman

;==============================================================
CalcNewFrame PROC NEAR
; Purpose:  Calculate what frame number file should be on based on jump
; Input:    al = percentage of the way through the file
; Output:   variables updated
; Coded by : Ari Silverman

;==============================================================
MAVISSendPacket PROC NEAR
; Purpose: Filling RxBuffer with the packet host play status and send out the packet
; Input:   variable Status
; Output:  TXBuffer filled with a packet of format PacketMsg (1 Byte long)
; Description: This function makes a library funtion call: SendPacket
; Coded by :   Sidney Thong
netpost PROC NEAR
; Purpose: Callback function for incoming network messages from host
;   Update variables
; Input: BX=Receive Buffer
;   AX=Message Length
; Output: Variables Status updated
; Description: This is a Datagram callback routine
; Coded by :   Sidney Thong

;====== SoundCallBack ====================================================
; Purpose: SoundCallBack is called whenever the DSP generates an interrupt
;   at the end of playing each half buffer.
;   SoundCallBack is used to call send packet containing host
;   play status so that the clients are synchronized
; Input: none
; Output: none
;
; Description: This is an ISR Callback function. It switches to Single cycle mode
;      when there are no more raw sound data to be played.
;      Sound Playing is then stop
; Coded by :   Sidney Thong

;==============================================================
; SoundCallBackWOSend
; Purpose: SoundCallBackWOSend is called whenever the DSP generates an interrupt
;   at the end of playing each half buffer.
;   This is specifically used to play the intro sound
; Input: none
; Output: none
;
; Description: This is an ISR Callback function. It keeps looping the IntroSound
;      Sound Playing is then stop, and blaster resources are freed
; Coded by :   Sidney Thong

;====== OpenRawSound  ====================================================
; Purpose: OpenRawSound opens a wav file and advances to the beginning of data
; Input: None.
; Output: wavremaining = bytes of Sound Data.
;    SoundSample contains the wav file header information
; Coded by : Sidney Thong

;====== LoadSoundBuffer ======================================
; Purpose: This function load half of the SoundBuf with data
;     or with zero from the wav file.
; Input:   WhichHalfBuf  ; WriteFirstHalf == 1st half,
;         WriteSecondHalf== 2nd half
;
; Output: The following variables are updated
;    LastSoundBuf  = 1 if just loaded last quarter buffer
;    LastSoundBufLength = the last number bytes loaded into quarter buffer
; Description: This procedure is called from the call back function
;      to load the next quarter sound buffer
; Coded by : Sidney Thong

; ===== InstallSound =========================================
InstallSound proc near
; Purpose: Install DMA to play sound  and install correct sound call back functions
;   depending whether we are playing avi file or the intro sound
; Input: array SoundBuf,SoundBufLenght, WhichSound, WhichHalf
; Output: SoundBufPage, SoundBufOffset, SoundCallBack or SoundCallBackWOSend
;   is set
; Description: This function first compute the linear address of SoundBuf
;      and make sure it does not cross the page boundary.
;      If the page boundary is crossed, then the buffer is shifted
;      by SoundBufLength.
; Coded by : Sidney Thong

;====== PlaySound  ============================================
; Purpose: Open wav file and start playing sound
; Input: SoundBufOffset, SoundBufPage, SoundBufLength,
; Output: wavremaining updated
; Description: This function first check if Single cycle mode of auto-initialize
;      mode is to be used. If sound data is so small that it would only
;      fill up the first two buffer, then single cycle mode is used.
;      In this case, sound playing is then stop, and blaster resources are freed
; Coded by : Sidney Thong

;==============================================================
SoundCloseFile PROC NEAR
; Purpose: Close the Intro sound file
; Input: SoundFileHandle
; Output: none
; Coded by : Sidney Thong

;==============================================================
DoIntroduction PROC NEAR
; Purpose: Call the respective functions to Play the intro sound and run through
;   interface screens
; Input: none
; Output: none
; Coded by : Sidney Thong

;====== EndSound  ============================================
EndSound PROC NEAR
; Purpose:  This procedure changes the dsp playback mode to single-cycle mode
;           The sound is stopped, Blaster resources are freed.
; Input:    none
; Output:   LastSoundBuf reset to 0
; Coded by : Sidney Thong

;====== EndSound  ============================================
ClearSoundBuffer PROC NEAR
; Purpose:  This function fills the entire SoundBuf with zero's.
;           This creates sillence at the sound blaster output.
; Input:    SoundBufPage, SoundBufOffset, SoundBufLength
; Output:   none
; Coded by : Sidney Thong

;--------------------------------------------------------------------------
; AVIOpenFile
; Input:  AviName contains the name of the file to open
; Output: AVIFileHandle contains a DOS file handle to the open file
; Modifies: AX, DX
; Purpose: Opens an AVI file and returns a DOS handle to it
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; AVICloseFile
; Input:  AviName contains the name of the file to open
; Output: AVIFileHandle contains a DOS file handle to the open file
; Modifies: AX, BX
; Purpose: Closes an AVI file
; Coded By: Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; AVICalcJump
; Input:
;
;     EAX = location to jump to, scaled to 100%
;     AVIFileHandle = handle of open
; Output: FileHandle is set to point to correct place in file
; Modifies: EAX, EBX, EDX, DI
; Purpose: Jumps to a percent location within a file
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; CheckJump
; Input:
;     JumpLocation is the place to jump to
;     AVIFileHandle = handle of open
; Output: FileHandle is set to point to correct place in file
; Modifies: EAX, EBX, EDX, DI
; Purpose: Jumps to a percent location within a file
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; LoadAVIHeader
; Input:
;     AVIFileHandle = handle of open
; Output: FileHandle is set to point to correct place in file
; Modifies: EAX, EBX, EDX, DI
; Purpose: Loads the info from the AVI header into the local variables
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; LoadIndex
; Input:
;     JumpLocation is the place to jump to
;     AVIFileHandle = handle of open
; Output: FileHandle is set to point to correct place in file
; Modifies: EAX, EBX, EDX, DI
; Purpose: Loads the Index into the Index Chunk
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; LoadNextSoundChunk
; Input: None
; Output: SoundBuf is loaded with sound
; Modifies: AX, BX, CX, DX
; Purpose: Loads the next sound chunk, but doesn't move ahead too far in
;           loading to prevent skipping and maintain synchronization
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; LoadNextChunk
; Input: None
; Output:
;   ChunkSize, ChunkType
;   SoundBuf or VideoChunkBuffer contains the buffer info
; Modifies: None
; Purpose: Loads the next chunk from the AVI file.  If it's a video chunk
;           it goes into the VideoChunkBuffer.  Otherwise it goes into
;           the SoundBuf
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; InstallTimer
; Input: None
; Output: MilliSecCount counts the number of loops within a millisecond
; Modifies: None
; Purpose: Sets the timer variable for video synchronization to the correct
;           value.
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; AVIDelay
; Input: MicroSecPerFrame from the AVI Header, MillSecCount
; Output: none
; Modifies: none
; Purpose: Delays the correct amount of time per frame.  Uses base 2
;           arithmetic where allowable, to result in quick execution
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; AVIDrawFrame
; Input: VideChunkBuffer
; Output: none
; Modifies: AX, CX, DI
; Purpose: Draws a frame onto the screen without stretching, used for
;           extending chunk types in the future and debugging
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; SetVideoPalette
; Input: VideoChunkBuffer must contain the header info
; Output: values to the screen palette
; Modifies: None
; Purpose: Sets the current palette to the screen's palette
; Coded By Razvan Mathias
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;CalcOSetX
;Input:         MonitX = X postition of which grid of picture frame to display
;                               VIDwidth = width of video portion that will be displayed
;Output:                OffSetX = offset to correctly display image
;Purpose:       using following formula to correctly display image
;                                       OffsetX = MonitX*VIDwidth
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;CalcOSetY
;Input:         TVIDhight-1 = last row of bmp segment
;                               MonitY = Y postition of which grid of picture frame to display
;                               VIDhight        = hight of video portion that will be displayed
;Output:                OffSetY = offset to correctly display image
;Purpose:       use the following formula to correctly calculate offset
;                                       OffSetY=(TVIDhight-1)-MonitY*VIDhight
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;StrethBlt
;Input:         VideoChunkBuffer = bmp segment used to display to screen
;                               VidGrSEG = segment used to display bmp
;Output:                Stretched portion of bmp
;Purpose:       Stretches a portion of a video onto the appropriate screen
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;NumOfScrns
;Inputs:                calls functions CalcWidth and Height
;Outputs:       initializes VIDwidth    and VIDhight
;Purpose:       sets up the grid size for correct display
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;CalcWidth
;Inputs:                VIDNumScrn = sqrt of the num of grids to display to
;                               TVIDwidth  = original video width
;Outputs:       VIDwidth          = calculated video width grid
;Purpose:       calculate video width using the following formula:
;                                       VIDwidth = TVIDwidth/VIDNumScrn
;                       reset OffSetX and MonitX to defaults each time chng of VIDNumScrn
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;CalcHeight
;Inputs:                VIDNumScrn = sqrt of the num of grids to display to
;                               TVIDhight  = original video height
;Outputs:       VIDhight          = calculated video height grid
;Purpose:       calculate video height using the following formula:
;                                       VIDhight = TVIDhight/VIDNumScrn
;                       reset OffSetY and MonitY to defaults each time chng of VIDNumScrn
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;ChangeScrn
;Input:         None, uses variables VIDNumScrn, MonitX, MonitY,
;                                               OffSetX, OffSetY
;Output:                MonitX, MonitY, OffSetX, OffSetY
;Purpose:       Change the current BMP grid that should be shown on the screen
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;ChgYCutOff
;Input: NONE
;Output:        YCutOff is either set to 200 for full screen or 170 for menu
;Purpose: toggles between full and menu screen view used in kbdhandler
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;DspMenu
;Inputs:        NONE
;Outputs: Display to the screen
;Purpose:       Prints out the menu bar to the sreen, reinitializes mouse
;Coded By Peter Prokopios
;--------------------------------------------------------------------------

;--------------------------------------------------------------------------
;CalcScrnNum
;Inputs:        MonitY, MonitX, VIDNumScrn
;Outputs:       Display to screen the current screen number
;Purpose:       Calculate the current screen number using
;                                       Scrn# = MonitY*VIDNumScrn+(MonitX+1)
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;CalcResNum
;Inputs:        AX = Resolution Number to print to screen
;               DL = Screen Column to display to
;Outputs:       Display to screen the current video resolution
;Coded By Peter Prokopios
;Purpose:       Display the current video resolution
;--------------------------------------------------------------------------
;-------------------------------------------------------------------------
;DspResNum
;Input:   TVIDhight, TVIDwidth
;Output:  Display to screen resolution of current video
;Purpose: Display resolution TVIDwidthxTVIDhight
;Coded By Peter Prokopios
;-------------------------------------------------------------------------
;--------------------------------------------------------------------------
;NumberScrn
;Input: AX = new number of screens
;Output:        YCutOff is either set to 200 for full screen or 170 for menu
;Purpose: toggles between full and menu screen view used in kbdhandler
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;DspNumMenu
;Inputs:        NONE
;Outputs: Display to the screen
;Purpose:       Prints out the menu bar to the sreen, reinitializes mouse
;Coded By Peter Prokopios
;--------------------------------------------------------------------------
; Purpose: Change the frequency to fit playback of 11.025khz and 22.05khz AVI file
; Input: The following values are used for frequency and the correspoinding Time Constant
;        Frequency    : AL=0 for 11.025kHz, AL=1 for 22.05kHz
;        TimeConstant : A5 for 11.025kHz, D2 for 22.05kHz
;        SB_BaseAddr, DSP_WRITE_PORT, DSP_TIME_CONSTANT
; Output: none
; Description: The frequency calculation is done using the following formula
;              TIME_CONSTANT = 256 - 1000000 / frequency  (result is rounded off)
; Coded by: Sidney Thong
 
 

External Routines

ECE291 Sound Library Functions for sound playback
ECE291 Network Library for Netbios

Main Procedure

; ====== MAIN Routine ====================================================

main proc far
  mov ax, cseg   ; Initialize DS register
  mov ds, ax
 

      ; Begin assuming that we can play the first sound
  MOV Status, LOADNEXTSOUND

      ; Initialize network for the entire message subsystem
  Call NetInit

StartOver:
      ; Display the introduction screen
      Call DoIntroduction

      ; We can only exit the entire application from this screen
    cmp  Status, Exit
  je    ExitNow

     ; Actual video Playback starts here.
  MOV WhichSound, AVISound
  CALL AVIOpenFile

      ; Load the index of the file for ff/rew purposes
  CALL LoadIndex

      ; Load the file's information into the local variables
  CALL LoadAVIHeader
  CALL SetVideoPalette

      ; Install the frame timer
  CALL InstallTimer

  ; We assume the  first chunk is a sound chunk.  We it to calculate
      ; our sound buffer sizes.
  mov  WhichSound, AVISound
  CALL LoadNextChunk

  ; Initializes sound and begins playing the buffers, only for the host
  CMP HostClientStatus, Client
  JE MainLoop
  Call  InstallSound
  Call   PlaySound
 

MAINLOOP:
      ; check for input and exit video playback if necessary
      call MouseHandler
  call KbdHandler
  cmp Status, Exit
  je EndMainLoop
 

      ; If there is a jump message, deal with it and go to IDLE (Pause mode)
  Call CheckJump

      ; If in IDLE mode, we sit and do nothing
  cmp cs:Status, IDLE
  jne NotIdle

  jmp MAINLOOP

NotIdle:

     ; grab the next chunk from the file for processing
     CALL LoadNextChunk

     ; if the next chunk is the last one, exit.
     cmp cs:Status, Exit
   je EndMainLoop

   cmp ChunkType, 'B'
   jne DonePlayingChunk

     ; if this is a bitmap chunk, show it and increase the frame number
   call StretchBlt
     inc NFrames
   mov ax, NFrames
    cmp ax, NumFrames
     je EndMainLoop

DonePlayingChunk:
   JMP MAINLOOP

EndMainLoop:

     ; We must delay for all incoming network packets to arrive before
     ; we exit
     delay 200

     ; Clear out and clean Sound buffer
     call  ClearSoundBuffer
   call  EndSound
NoUninstallSound:

     ; close the file, clear the screen and prepare for intro screen and
     ; next avi load
     Call AVICloseFile
     Call clrScreen
   MOV Status, LOADNEXTSOUND

   mov NFrames, 0
   mov YCutOff, 200
   mov WhichSound, IntroSound
 

   JMP StartOver

   ; Jumps here when we're done with the introscreen, and we've exited the app.
   ExitNow:
     ; Clean the sound buffer, close the file, change to text mode, and exit
     Call   EndSound
   Call   SoundCloseFile
   Call  AVICloseFile

   TMODE

   Call NetRelease

   Call DOSXIT

main endp

Major Variables Used:

; ---- Play Status Information ---
IDLE         EQU 0
PLAY         EQU 1
PAUSE         EQU 2
STOP         EQU 3
EXIT         EQU 4
JUMP         EQU 5
LOADNEXTSOUND  EQU 6
 
; ---- Network Message Types & Formats ---
 
PacketMsg STRUC   ; Packet Message Structure
          ; Used for synchoronizing clients with host activity.
  HostStatus   db 0 ; Status of host
  JumpTo    dd 0
PacketMsg ENDS
 

ScreenMsg STRUC   ; Screen Message Structure
   HostVIDWidth  dw 320
   HostVIDHight  dw 200
   HostVIDNumScrn dw 1

ScreenMsg ENDS

;====== Sound Info =========================================================
; ---- Wave Header Struct----------
WaveHdr STRUC
  format db  'RIFF'
  filelen dd  ?
  wavefmt db  'WAVEfmt_'
  fmt_len dd  ?
  fmt_tag dw  ?
  channel dw  ?
  samples dd  ?
  bytesps dd  ?
  bkalign dw  ?
  bitsps dw  ?
  data db  'data'
  datalen dd  ?
WaveHdr Ends

;====== Stack Segment =====================================================
stkseg segment stack     ; *** STACK SEGMENT ***
  db  64 dup ('STACK   ')
stkseg ends

; --- Define sound buffer segment---
SNDSEG SEGMENT
   SoundBuf  db   8191 dup (00,00,00,00,00,00,00,00) ; (64KB - 1B) of empty sound
SNDSEG ENDS          ; Note: 64kB -1B requires only
              ; requires a word for addressing.
 
 

;====== Define code segment ===============================================

cseg segment public 'CODE'   ; *** CODE SEGMENT ***
  assume cs:cseg, ds:cseg, ss:stkseg, es:nothing

;====== External procedures ===============================================

; -- Lib291 Function Calls made to the library used --
extrn kbdin:near, binasc:near, dspmsg:near, dspout:near, dosxit:near
extrn kbdine:near, rsave:near, rrest:near

; -- LibNet (Free) --
extrn NetInit:near, SendPacket:near, NetRelease:near, NetTest:Near
extrn TXBuffer:byte, RXBuffer:byte

; -- Sblib291 (Free) --
extrn SB_Init:near, SB_Clean:near, SB_Play:near, SB_Stop:near
extrn SB_SetCallback:near, SB_SingleCycle:near

; The following two procedures are the functions extended in the SB291lib.asm
; coded specifically for MAVIS.
extrn SB_ChangeVolume:near
extrn SB_ChangeFrequency:near

;====== Variables ========================================================

pbuf     db 7 dup(?)
crlf     db CR,LF,'$'

Status db 1  ; Play Status: 0=Idle, 1=Play, 2=Pause, 3=Stop, 4=Exit
PUBLIC Status

FrameNumber dw 0
PUBLIC FrameNumber

grp_name db    'WCE291MAVIS$$$$$'
my_name db   'WCE291Client00$$' ; The '00' will be the number assigned
                     ; dynamically.

 
PUBLIC grp_name, my_name

; Variables for Sound procedures
WriteFirstHalf   EQU    0
WriteSecondHalf   EQU    1

IntroSound    EQU    0
AVISound    EQU    1
 
SoundBufPage db  ?
SoundBufOffset dw  ?
SoundBufLength dw  Sizeof SoundBuf

LastSoundBufLength  dw  ?
LastSoundBuf db  0  ; 1 == the last sound buffer is just read
SoundSample    WaveHdr < >
WhichHalfBuf db  0  ; WriteFirstHalf  == first half of SoundBuf
        ; WriteSecondHalf == second half of SoundBuf
WhichSound  dw  0
Volume   db  07Fh
SoundChunkSize dw  0

; Variables for wav file
Introwavfile db  'Omavis2.wav',0 ; null terminate file names
SoundFileHandle dw  ?
wavremaining dd  0
 

; Variables for interface

InterfaceBMP db 10678 dup(0)
NumMenuBMP db 10678 dup(0)
NumStorage db 7 dup('$')
Pallette db 1024 dup(0)       ; holds the pallette for the startup screens

; Names of the files we will be using
ImageName db 'IntImage.bmp',0
NBarName db 'NumBar.bmp',0
Screen1 db 'Screen1.bmp',0
Screen2 db 'Screen2.bmp',0
Screen3 db 'Screen3.bmp',0
Screen4 db 'Screen4.bmp',0
Aviname db 'test4.avi   ', 0          ; holds the name of the avi file

NumberOfScreens  db   1
ScreenNumber     db   0
MouseStatus        db   0 ; 0 - mouse button is up, 1 - mouse
                              ; button is down
FrameJump          db       0 ; Holds where to jump in the avi file
HostClientStatus   db HOST    ; Holds whether a host or client is displaying the video
FirstIntro         db 0

; Constants telling whether we are running host or client mode.
HOST  EQU  1
CLIENT EQU 0

; Variables for video playback
AVIFileHandle dw ?

; Local variables used to tell the size and type of the last loaded chunk.
ChunkSize dw ?
ChunkType db ?

SOUND EQU 1
VIDEO EQU 2

; This buffer is used to load the headers of each individual chunk
HeaderBuffer   db 30 dup(' ')

; This structure will contain the only information we need from the actual header
; chunk in the AVI
PaletteOffset   EQU  0C8h

;================ VARIABLES USED BY STRETCHBLT FUNCTION ===================

;screen width and height, in video mode 320x200
SCRwidth DW 320
SCRhight DW 200
;video portion that will be displayed on monitor
VIDwidth DW ?
VIDhight DW ?
;Sqrt of the Num of Grids the BMP is split into, default is 1
VIDNumScrn DW 1
;total video width and height
TVIDwidth DW ?
TVIDhight DW ?
;which monitor is being used to display bmp, DEFAULT MonitX & Y = 0
MonitX DW 0
MonitY DW 0
;offset to correctly display image, OffSetX=0, OffSetY=(TVIDhight-1)
OffSetX DW 0
OffSetY DW ?
;used by host computer on whether to print Control panel or not
;initialy this is equal to SCRhight=200, Host YCutOff=SCRhight-30
YCutOff DW  200
;used to correctly display the number menu; DEFAULT = 0
YNumCutOff DW 0
;==========================================================================

;Variables grabbed from avi file
MicroSecPerFrame DD 0
IndexBufferSize DD 0    ; size of the index buffer
NumFrames DW 0          ; Total number of frames

JumpLocation DD 0       ; Used to determine the local jump location when
                        ; Status==JUMP

; frame number we're on.
NFrames DW 0

; number of loops in Delay function for one millisecond
MilliSecCount DD 0

; These are all standard for most AVI files.
;   The files ***MUST*** be decompressed RGB 666 format with
;   11 KHz sound.  The test files were created using MainActor
;   a video creation utility.
; First chunk in AVI RIFF file is the AVI Main Header Chunk
AVIHeaderOffset         EQU   0Ch

MainAVIHeaderType STRUC
   dwMicroSecPerFrame      DD    0
   dwMaxBytesPerSec        DD    0
   dwReserved1             DD    0
   dwFlags                 DD    0
   dwTotalFrames           DD    0
   dwInitialFrames         DD    0
   dwStreams               DD    0
   dwSuggestedBufferSize   DD    0
   dwWidth                 DD    0
   dwHeight                DD    0
   dwScale                 DD    0
   dwRate                  DD    0
   dwStart                 DD    0
   dwLength                DD    0
MainAVIHeaderType ENDS

   strhOffset              EQU   64

AVIStreamHeader STRUC
   fccType                 DB    'vids'
   fccHandler              DB    'dvsd'
   dwFlags                 DD    0
   wPriority               DW    0
   wLanguage               DW    0
   dwInitialFrames         DD    0
   dwScale                 DD    100
   dwRate                  DD    2997
   dwStart                 DD    0
   dwLength                DD    2192
   dwSuggestedBufferSize   DD    120000
   dwQuality               DD    0
   dwSampleSize            DD    0
   rcFrame                 DD    0,0,720,480
AVIStreamHeader ENDS

   strfOffset              EQU   0A0h

; This is actually a header at the beginning of the file telling
; us about the dimensions and colors of the file
BMPFormat STRUC
; --- BitMapInfoHeader --- ; 40 bytes
  BISize            DD   ?  ; Size of BitMapInfoHeader (28h = 40d)
  BIWidth           DD   ?  ; # Pixel Rows
  BIHeight          DD   ?  ; # Pixel Columns
  BIPlanes          DW   1  ;
  BIBitCount        DW   ?  ; Log2(palette size) = 4 for 16-color image
  BICompression     DD   0  ; RGB = 0 = Uncompressed
  BISizeImage       DD   ?  ; Size of Image (Bytes)
  BIXPelsPerMeter   DD   ?  ;
  BIYPelsPerMeter   DD   ?  ;
  BIColorsUsed      DD   0  ; 0=All
  BIColorsImportant DD   0  ;
  RGBQuad           DB 16 dup ( 4 dup(?) ) ; Blue, Green, Red, Unused
BMPFormat ENDS

   moviChunkOffset         EQU   0D4h