[bb] Save AVI with Sound by jfk EO-11110 [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:42

Previous topic - Next topic

BlitzBot

Title : Save AVI with Sound
Author : jfk EO-11110
Posted : 1+ years ago

Description : Like the framegrabber this one is using AviFil32.dll and some of the codecs listed in the codec selection box won't work. I also guess MP3 won't work for the sound, better use a standard WAV file.
Some things may be not implemented 100% correctly, although it worked on my machine. Thanks to the PB Community where I have found most of the infos needed for this.

Have fun, Mr. Next-Industrial-Light-and-Magic. ;)


Code :
Code (blitzbasic) Select
; Save Avi with sound. Provided by Dieter Marfurt aka jfk 11110

; userlibs:

; avifil32.decls:

;.lib "avifil32.dll"
;AVIFileInit%()
;AVIFileOpen%(ppfile*,szFile$,uMode%,lpHandler%)
;AVIFileRelease%(pfile%)
;AVIFileInfo%(pfile%,pfi*,lSize%)
;AVIFileGetStream%(pfile%,ppavi*,fccType%,lParam%)
;AVIFileExit%()
;AVIFileCreateStream%(pfile%, ppavi*, psi*)
;AVIStreamInfo%(pStream%,pfi*,lSize%)
;AVIStreamStart%(pavi%)
;AVIStreamRelease%(pavi%)
;AVIStreamLength%(pavi%)
;AVIStreamGetFrameOpen%(pavi%,lpbiWanted%)
;AVIStreamGetFrame%(pFrame%,index%)
;AVIStreamGetFrameClose%(pFrame%)
;AVIStreamReadFormat%(pavi%, lPos%, lpFormat%, lpcbFormat*)
;AVIStreamSetFormat%(pavi%, lPos%, lpFormat*, cbFormat%)
;AVIStreamRead%(pavi%, lStart%, lSamples%, lpBuffer%, cbBuffer%, plBytes*, plSamples*)
;AVIStreamWrite%(pavi%, lStart%, lSamples%, lpBuffer*, cbBuffer%, dwflags%, plSampWritten%, plBytesWritten%)
;AVISaveOptions%(hwnd%, uiFlags%, nStreams%, ppavi*, plpOptions*)
;AVISaveOptionsFree%(nstreams%, plpOptions*)
;AVIMakeCompressedStream%(ppsCompressed*, psSource%, lpOptions%, pclsidHandler%)

; kernel32.decls:

;.lib "kernel32.dll"
;RtlMoveMemory2%(Destination*,Source,Length) : "RtlMoveMemory"



Graphics 800,600,32,2
SetBuffer BackBuffer()

Const streamtypeAUDIO = 1935963489
Const streamtypeVIDEO = 1935960438
Const ICMF_CHOOSE_KEYFRAME=1
Const ICMF_CHOOSE_DATARATE=2
Const ICMF_CHOOSE_ALLCOMPRESSORS=8

Const AVIERR_OK=0
Const AVIERR_NOCOMPRESSOR=$80000000 Or $40000 Or ($4000 + 113)
Const AVIERR_MEMORY=      $80000000 Or $40000 Or ($4000 + 103)
Const AVIERR_UNSUPPORTED= $80000000 Or $40000 Or ($4000 + 101)

Const AVIIF_KEYFRAME=$10
Const Lib = 0

Const OF_READ=$0
Const OF_WRITE=$1
Const OF_SHARE_DENY_WRITE=$20
Const OF_CREATE=$1000

Global hwnd=SystemProperty$("AppHWND")



; demo:

file$="test_save.avi"
target_width=640
target_height=480
target_rate=25




AVIFileInit(); this needs to be called before any other Avifil32.dll function call

; open avi for writing...
my_handle=OpenWriteAvi(file$,target_width,target_height,target_rate,"test.wav") ; optional last parameter is wav file to be added  (will be muxed in the CloseWriteAvi function)
If my_handle=0: CloseWriteAvi(my_handle): AVIFileExit(): End: EndIf


 ; write 11 frames to the avi
 For i=0 To 10
  Cls
  Color 0,255,0
  Rect 0,0,640,480,0
  For r=0 To 100
   Color Rand(255),Rand(255),Rand(255)
   Text (Rand(target_width) And $FFF0),Rand(target_height),i+"*+*"
  Next
  Flip
  AddFrameWriteAvi(my_handle, i); third (optional) parameter may be an image handle (make sure it's big enought), if omitted, the frame content will be grabbed from backbuffer
 Next

CloseWriteAvi(my_handle)
AVIFileExit()

Print "ok!"
WaitKey()
End








Function OpenWriteAvi(file$,target_width,target_height,target_rate,soundfile$="")
 Local i ; probalby you better make all variables explicit locals (?), I'm too lazy for now

 AviControlBank=CreateBank(1024+128) ; this bank will be used to store several bank handles, variables etc. that are required in other functions, especially for closing/releasing things and writing frames to.
 PokeInt AviControlBank,36,target_width
 PokeInt AviControlBank,40,target_height
 PokeInt AviControlBank,44,target_rate

 PokeInt AviControlBank,128,Len(soundfile$)
 For i=1 To Len(soundfile$)
  PokeByte AviControlBank,132+i-1,Asc(Mid$(soundfile$,i,1))
 Next

 ret=CreateBank($128) ; will hold the avifileopen handle
 PokeInt AviControlBank, 8,ret
 If AVIFileOpen(ret,file$,OF_WRITE Or OF_CREATE,0)=0
  avi_handle=PeekInt(ret,0)
  PokeInt AviControlBank, 0,avi_handle

  AVISTREAMINFO_bank=CreateBank($ffff)
  PokeInt AviControlBank,12,AVISTREAMINFO_bank

  ; possible flags:
  avisf_disabled=$1
  avisf_video_palchanges=$10000
  avisf_knownflags=$10001
  loc_fccType=streamtypeVIDEO
  loc_fccHandler=(Asc("c") Shl 24)Or(Asc("v") Shl 16)Or(Asc("s") Shl 8)Or(Asc("m"))

  For i=0 To 511 Step 4
   PokeInt AVISTREAMINFO_bank,i,0
  Next

  PokeInt AVISTREAMINFO_bank, 0,loc_fccType
  PokeInt AVISTREAMINFO_bank, 4,loc_fccHandler
  PokeInt AVISTREAMINFO_bank,20,1000/target_rate; eg 1000/25= 40...
  PokeInt AVISTREAMINFO_bank,24,1000;loc_dwRate ;    1000/40= 25 fps    (typical M$ :) )
  PokeInt AVISTREAMINFO_bank,40,40+(target_width*target_height*3) ;loc_dwSuggestedBufferSize ; 0

  If AVIFileCreateStream(avi_handle,ret,AVISTREAMINFO_bank)=0
   stream_Handle=PeekInt(ret,0)
   PokeInt AviControlBank, 4,stream_handle

   ; required by AviStreamSetFormat (seems to be the same as the header of DIBHEADER)
   BITMAPINFOHEADER=CreateBank(40)
   PokeInt AviControlBank,16,BITMAPINFOHEADER
   PokeInt BITMAPINFOHEADER, 0,40 ; size of header
   PokeInt BITMAPINFOHEADER, 4,target_width ; width
   PokeInt BITMAPINFOHEADER, 8,target_height ; height
   PokeShort BITMAPINFOHEADER,12,1 ; planes
   PokeShort BITMAPINFOHEADER,14,24  ; bitCount
   PokeInt BITMAPINFOHEADER,16,0 ; compression (0=no compression in DIB)
   PokeInt BITMAPINFOHEADER,20,(target_width*target_height*3);0 ; size in bytes (may be 0 for non compressed)
   PokeInt BITMAPINFOHEADER,24,3600 ; horiz pixel per meter
   PokeInt BITMAPINFOHEADER,28,3600 ; vert pixel per meter
   PokeInt BITMAPINFOHEADER,32,0;$FFFFFF ; Colors used (for palette)
   PokeInt BITMAPINFOHEADER,36,0;$FFFFFF ; number of important colors (lol)

   ; this bank will be used to write our frame data to, so avifil32 can grab it from there
   DIBHEADER=CreateBank(40+(target_width*target_height*3)+1000)
   PokeInt AviControlBank,20,DIBHEADER
   For i=0 To 36 ; we simply reuse BITMAPINFOHEADER
    PokeInt DIBHEADER,i,PeekInt(BITMAPINFOHEADER,i)
   Next

   ; A pointer to space for AVICOMPRESSOPTIONS: first Int of bank points to itself (offset 8)
   plpo=8
   plpOptionsList=CreateBank(8000) ; req. size guessed, but hey...
   PokeInt AviControlBank,24,plpOptionsList
   compression_handle=CreateBank(8000)
   PokeInt AviControlBank,28,compression_handle
   dumm_bank=CreateBank(8)
   PokeInt AviControlBank,32,dumm_bank
   RtlMoveMemory2(dumm_bank,plpOptionsList+4,4)
   PokeInt plpOptionsList,0,PeekInt(dumm_bank,0)+plpo
   PokeInt plpOptionsList,4,PeekInt(dumm_bank,0)+plpo+4000

   If AVISaveOptions(hwnd, 3, 1, ret, plpOptionsList)=1 ;=0
    res=AVIMakeCompressedStream(compression_handle, stream_handle, PeekInt(plpOptionsList,0), 0)
    If res=0
     ;If res= AVIERR_OK Then Print "ok!!!"
     ;If res= AVIERR_NOCOMPRESSOR Then Print "nocompressor"
     ;If res= AVIERR_MEMORY       Then Print "memory"
     ;If res= AVIERR_UNSUPPORTED  Then Print "unsupported"
     If AVIStreamSetFormat(PeekInt(compression_handle,0), 0, BITMAPINFOHEADER,40)=0
      Return AviControlBank ; ok ready to rumble
     EndIf
    EndIf
   EndIf
  EndIf
 EndIf
 Return 0 ; bah, failure
End Function



Function CloseWriteAvi(AviControlBank)
 Local i
 If AviControlBank<>0
   avi_handle=PeekInt(AviControlBank, 0)
   stream_handle=PeekInt(AviControlBank, 4)
   ret=PeekInt(AviControlBank, 8)
   AVISTREAMINFO_bank=PeekInt(AviControlBank,12)
   BITMAPINFOHEADER=PeekInt(AviControlBank,16)
   DIBHEADER=PeekInt(AviControlBank,20)
   plpOptionsList=PeekInt(AviControlBank,24)
   compression_handle=PeekInt(AviControlBank,28)
   dumm_bank=PeekInt(AviControlBank,32)
   len_wav=PeekInt(AviControlBank,128)
   wav_to_add$=""
   For i=1 To len_wav
    a=PeekByte(AviControlBank,132+i-1)
    If a=0 Then Exit
    wav_to_add$=wav_to_add$+Chr$(a)
   Next
   ; close all
   If compression_handle<>0
    AVIStreamRelease(PeekInt(compression_handle,0))
   EndIf
   If plpOptionsList<>0
    AVISaveOptionsFree(1,plpOptionsList)
   EndIf
   If stream_Handle<>0
    AVIStreamRelease(stream_Handle)
   EndIf
   ; multiplex Audio stream, if any
   If (wav_to_add$<>"") And (FileType(wav_to_add$)=1)
    AddWavWriteAvi(wav_to_add$,avi_handle)
   EndIf
   If avi_handle<>0
    AVIFileRelease(avi_handle)
   EndIf
   ;free banks...
   If ret<>0 Then FreeBank ret
   If AVISTREAMINFO_bank<>0 Then FreeBank AVISTREAMINFO_bank
   If nullBank<>0 Then FreeBank nullBank
   If BITMAPINFOHEADER<>0 Then FreeBank BITMAPINFOHEADER
   If DIBHEADER<>0 Then FreeBank DIBHEADER
   If plpOptionsList<>0 Then FreeBank plpOptionsList
   If compression_handle<>0 Then FreeBank compression_handle
   If dumm_bank<>0 Then FreeBank dumm_bank
   FreeBank AviControlBank
   ; i really hope I released everything, otherwise there would be a memoryleak
 EndIf
End Function


Function AddFrameWriteAvi(AviControlBank, index, buffer=0)
 If AviControlBank<>0
  width=PeekInt(AviControlBank,36)
  height=PeekInt(AviControlBank,40)
  If buffer=0 Then buffer = BackBuffer()
  DIBHEADER=PeekInt(AviControlBank,20)
  If DIBHEADER<>0
   compression_handle=PeekInt(AviControlBank,28)
   If compression_handle<>0
    Buffer2Dib(width,height,DIBHEADER,buffer)
    AVIStreamWrite(PeekInt(compression_handle,0), index, 1,  DIBHEADER ,40+(width * height *3) , AVIIF_KEYFRAME, 0,0)
   EndIf
  EndIf
 EndIf
End Function




Function Buffer2Dib(w,h,bank,buffer=0)
 Local x,y,count,rgb
 If buffer=0 Then buffer=BackBuffer()
 count=0
 SetBuffer Buffer
 LockBuffer Buffer
 For y=0 To h-1
  For x=0 To w-1
   rgb=ReadPixelFast(x,(h-1)-y) And $FFFFFF
   PokeByte bank,count+2,(rgb Shr 16) And $FF
   PokeByte bank,count+1,(rgb Shr 8) And $FF
   PokeByte bank,count+0, rgb And $FF
   count=count+3
  Next
 Next
 UnlockBuffer Buffer
End Function





Function AddWavWriteAvi(file$,avi_handle)
 Local b=0
 wav_handle=CreateBank(8)
 If AVIFileOpen(wav_handle,file$, OF_READ, 0)=0
;  Print "opened wav"
  wavstream_handle=CreateBank(8)
  If AVIFileGetStream(PeekInt(wav_handle,0), wavstream_handle, streamtypeAUDIO, 0) =0
;   Print "openened wav read stream"
   avi_info_bank=CreateBank(256);140
   If AVIStreamInfo(PeekInt(wavstream_handle,0), avi_info_bank,140) =0;140
;    Print "retrievend wav read stream info"
    lfmtSize=CreateBank(2560)
    lpFormat=CreateBank(256)
    If AVIStreamReadFormat(PeekInt(wavstream_handle,0), 0, 0, lfmtSize) =0
;     Print "used to read format of read stream"
     If PeekInt(lfmtSize,0)>0
      fmtWav=CreateBank(256)
      dummy=CreateBank(8)
      RtlMoveMemory2(dummy,fmtWav+4,4)
      If AVIStreamReadFormat(PeekInt(wavstream_handle,0), 0, PeekInt(dummy,0), lfmtSize)=0
;       Print "read format 2"
       lStreamLength = AVIStreamLength(PeekInt(wavstream_handle,0))
;       Print "got wav lenght"
       If lStreamLength <>0
        nullBank=CreateBank(lStreamLength)
        lpbData = CreateBank(lStreamLength+100000)
        RtlMoveMemory2(dummy,lpbData+4,4)
        If AVIStreamRead(PeekInt(wavstream_handle,0), 0, lStreamLength, PeekInt(dummy,0), lStreamLength, nullbank, nullbank)=0
;         Print "avistreamread wav data"
         psAvi=CreateBank(256)
         siWav=CreateBank(256)
         If AVIFileCreateStream(avi_handle, psAvi, avi_info_bank)=0
;          Print "created avi streat for writing audio"
          If AVIStreamSetFormat(PeekInt(psAvi,0), 0, fmtWav, PeekInt(lfmtSize,0))=0
;           Print "used to set format for audio write stream"
           If AVIStreamWrite (PeekInt(psAvi,0), 0, lStreamLength, lpbData, lStreamLength, AVIIF_KEYFRAME, 0, 0)=0
;            Print "AviWriteStream: written wav data to stream"
            b=1
           EndIf
          EndIf
          AVIStreamRelease(PeekInt(psAvi,0))
         EndIf
        EndIf
        FreeBank lpbData
        lpbData=0
       EndIf
      EndIf
     EndIf  
    EndIf
   EndIf
   AVIStreamRelease(PeekInt(wavstream_handle,0))
  EndIf
  AVIFileRelease(PeekInt(wav_handle,0))
 EndIf
 If wav_handle <>0 Then FreeBank wav_handle
 If wavstream_handle <>0 Then FreeBank wavstream_handle
 If avi_info_bank <>0 Then FreeBank avi_info_bank
 If lfmtSize <>0 Then FreeBank lfmtSize
 If lpFormat <>0 Then FreeBank lpFormat
 If fmtWav <>0 Then FreeBank fmtWav
 If dummy <>0 Then FreeBank dummy
 If lpbData <>0 Then FreeBank lpbData
 If psAvi <>0 Then FreeBank psAvi
 If siWav <>0 Then FreeBank siWav
 If nullbank <>0 Then FreeBank nullbank
 Return b
End Function


Comments :


John Blackledge(Posted 1+ years ago)

 Memoey Access Violation on RtlMoveMemory2


Hambone(Posted 1+ years ago)

 MAke sure you read the comments and put the decls in the right place:avifil32.decls and kernel32.decls are in the comments.Allan


John Blackledge(Posted 1+ years ago)

 Yes, I did.Then had to comment out RtlMoveMemory2 because it was already in another lib <sigh>,So does that mean that the other RtlMoveMemory2 definition is different from yours?


CS_TBL(Posted 1+ years ago)

 also important: can we expect a bmax port? :P


RGR(Posted 1+ years ago)

 @ JohnRTLMoveMemory is indeed a critical functionThere are 3 of them "on the market" (_from _to _ex)They differ only in the way the parameters are provided (Pointers and/or Variables). I just looked it up a minute ago because I had a MAV using the FreeImage.dll that was mentioned here 1 month ago ;-)


jfk EO-11110(Posted 1+ years ago)

 the only diffrence is: if you use a bank pointer (a*) or a plain int (a). So blitz will decide how it's handled. In kernel32.dll it's the same function.If this RtlMoveMemory2 isn't compatible with the one that is already in use by one of your userlibs, then off course you only have to rename it in the source and the kernel32.decls userlib mentioned in the code. Or you may use one of your existing declarations. Only make sure the pointers are used correctly:RtlMoveMemoryWhatever%( bank* ,source_adr,len) : "RtlMoveMemory"The star (*) is important.<div class="quote"> also important: can we expect a bmax port? :P </div>Maybe you should do that, all your contributions to the community are welcome. However, they sell those tongue cleaners ;).


Matty(Posted 1+ years ago)

 I realise this is an old code archive entry, but I am seeking some assistance in how to get it to work.I've put the decls in place, yet when I try to run the program (the small included demo in the listing above), I get an MAV (no extra info supplied even in Debug mode) on the following line:(line 184)
If AVISaveOptions(hwnd, 3, 1, ret, plpOptionsList)=1 ;=0




Matty(Posted 1+ years ago)

 anyone?


Warner(Posted 1+ years ago)

 Here the code runs fine. Are you on Vista? I believe that line executes a dialog in which the user can select what compressor should be used.Maybe you can omit this problem by changing these lines to:;;;If AVISaveOptions(hwnd, 3, 1, ret, plpOptionsList)=1 ;=0
res=AVIMakeCompressedStream(compression_handle, stream_handle, 0, 0)



Matty(Posted 1+ years ago)

 Thanks Warner I'll give it a try, I'm on XP though.


Matty(Posted 1+ years ago)

 Sorry, still didn't work, no debugging errors either.  I'll give up on this one I think...


Gladclef - Ben B(Posted 1+ years ago)

 Well, I don't know if anyone's paying attention to this anymore, but I'm having difficulties with this line:If AVISaveOptions(hwnd, 3, 1, ret, plpOptionsList)=1


jfk EO-11110(Posted 1+ years ago)

 I just see this, sorry ppl. Tho I don't know what your problem is with the code, there yould be several reasons:-Microsofts Implementation of AviFile Api lacks of compatibility (rather unlikely)-Security Management prevent it from working (eg. DEP)-Incompatible with other DECLS files (likely). To test this, save all decls in a backup folder, then delete everything but the ones used by this code.Also: there is the bank plpOptionsList, try to make it bigger for debugging purpose:   plpOptionsList=CreateBank(80000) ; req. size guessed, but hey...Seems like I didn't know exactly what I'm doing there :)Finally, try not to compare it against 1, but agains <>0 instead:If AVISaveOptions(hwnd, 3, 1, ret, plpOptionsList)<>0 [/i]