Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
AVI file format
#16
@Steffan-68 Big Grin

I simply wrote another program to list the contents of the AVI headers. FFMpeg can warn me about chunk displacement, but that was not the case, so when testing in FFMPeg it passed correctly. I finally found the bug, but it took a lot of time. It is commented in the EndAvi SUB, I will leave the previous version above, so you can compare it yourself.
Through further testing I found the following: As already written, the limit of an AVI file IS 4 Gigabytes. So - to be more precise - it is and is not Smile For programs such as Windows Media Player and Microsoft's "Movies and Shows" program, there is that limit. VLC ignore it and MPC-HC ignore it either. This is because I defined the _Unsigned Long data type, so that when the limit of a 32-bit number _Unsigned Long is exhausted, it does not fall into a negative number like the Long data type, but smoothly overflows and starts from zero. This causes incorrect chunk positions in the file and also incorrect file size in the AVI header. Microsoft players strictly follow these records and therefore if you record a 5 gigabyte file, WMP and other Microsoft players treat it as a 1 gigabyte file and play the first gigabyte of the file.

But then someone in programming thought about more and wrote VLC and MPC-HC and the file is read differently there, so this players play the entire file just fine! Tested on a 7 gigabyte file. And now... i start developing some compression. Slowly!


For this program is needed MP3 file (is used as sound source). 

Repair 2, sound is now correct in WMP.
Code: (Select All)

Dim ms As _MEM
Dim snd As Long
snd = _SndOpen("slunce.mp3") ' MP3 file, from which we read raw samples

' -------------------------
' Structure Definitions
' -------------------------
Type AVIMainHeader
    microSecPerFrame As Long ' Microseconds per frame
    maxBytesPerSec As Long ' Max data rate (bytes per second)
    paddingGranularity As Long
    flags As Long ' e.g. AVIF_HASINDEX
    totalFrames As Long
    initialFrames As Long
    streams As Long ' number of streams, e.g. 2 (video+audio)
    suggestedBufferSize As Long
    width As Long
    height As Long
    reserved As String * 16
End Type

Type AVIStreamHeader
    fccType As String * 4 ' "vids" or "auds"
    fccHandler As String * 4
    flags As Long
    priority As Integer
    language As Integer
    initialFrames As Long
    scale As Long
    rate As Long
    start As Long
    length As Long
    suggestedBufferSize As Long
    quality As Long
    sampleSize As Long
    frameLeft As Long
    frameTop As Long
    frameRight As Long
    frameBottom As Long
End Type

Type BITMAPINFOHEADER
    size As Long
    width As Long
    height As Long
    planes As Integer
    bitCount As Integer
    compression As Long
    sizeImage As Long
    xPelsPerMeter As Long
    yPelsPerMeter As Long
    clrUsed As Long
    clrImportant As Long
End Type

Type WAVEFORMATEX
    wFormatTag As Integer
    nChannels As Integer
    nSamplesPerSec As Long
    nAvgBytesPerSec As Long
    nBlockAlign As Integer
    wBitsPerSample As Integer
    cbSize As Integer
End Type

Type ChunkIndex
    chunkID As String * 4
    flags As Long
    offset As _Unsigned Long ' offset from start of 'movi'
    size As Long
End Type

' -------------------------
' SHARED variables
' -------------------------
Dim Shared aviFileNum As Integer

Dim Shared riffSizePos As _Unsigned Long, hdrlSizePos As _Unsigned Long
Dim Shared moviSizePos As _Unsigned Long, moviDataStart As _Unsigned Long
Dim Shared mainHdrPos As _Unsigned Long
Dim Shared vidStrhPos As _Unsigned Long
Dim Shared audStrhPos As _Unsigned Long
Dim Shared audStrh As AVIStreamHeader

Dim Shared shMainHdr As AVIMainHeader
Dim Shared shVidStrh As AVIStreamHeader

Dim Shared totalFrames As Long, totalAudioSamples As Long
ReDim Shared idxArr(1 To 10000) As ChunkIndex
Dim Shared idxCount As Long

' -------------------------
' Parameters
' -------------------------
Const vidFPS = 25
Const bytesPerPixel = 4
Dim Shared frameWidth As Long: frameWidth = 320
Dim Shared frameHeight As Long: frameHeight = 240
Const sampleRate = 44100

' -------------------------
' Main program
' -------------------------
Cls
idxCount = 0
totalFrames = 0
totalAudioSamples = 0

' 1) Initialize AVI
StartAvi "VideoTest5.avi"

Screen _NewImage(frameWidth, frameHeight, 32)

' 2) Recording loop
Dim Shared As Long x, img
Dim Shared frameData As String
Dim Shared memData As _MEM
Dim Shared audioSamplesPerFrame As Long
audioSamplesPerFrame = sampleRate \ vidFPS
Dim Shared audioData As String
Dim Shared iSample As Long
Dim Shared As Integer sampleL, sampleR
Dim StartTimer As Single
StartTimer = Timer

Dim afs As _Unsigned Long

' Audio reading
ms = _MemSound(snd, 0)
Dim audL(frameWidth) As Single
Dim audR(frameWidth) As Single
Dim au As Long, xx As Long
Dim SampleL1 As Single, SampleR1 As Single
Dim preDest As Long
Dim uVal As Long
Dim jN As Single
jN = .011
img = _NewImage(frameWidth, frameHeight, 32)
preDest = _Dest
VisStep = _Ceil(audioSamplesPerFrame / frameWidth)

Do While InKey$ = "" Or au > ms.SIZE - 8
    totalFrames = totalFrames + 1

    ' --- Generate video frame ---
    _Dest img
    Cls
    deltaX = (frameWidth - xx) \ 2
    For x = 1 To xx
        ' Some sample visual effect
        Line (deltaX + x, frameHeight / 2 - 150 * audL(x - 1))-(deltaX + x + 1, frameHeight / 2 - 150 * audL(x)), _RGB32(Sin(j) * 255, Sin(j + .45) * 255, Sin(j + .9) * 255), BF
        j = j + jN
        Line (deltaX + x, frameHeight / 2 + 150 * audR(x - 1))-(deltaX + x + 1, frameHeight / 2 + 150 * audR(x)), _RGB32(Cos(j) * 255, Cos(j + .45) * 255, Cos(j + .9) * 255), BF
        j = j + jN
    Next x

    If Abs(j) > 6.28 Then jN = jN * -1
    frameData = Space$(frameWidth * frameHeight * bytesPerPixel)
    memData = _MemImage(img)
    _MemGet memData, memData.OFFSET, frameData
    _MemFree memData
    _Dest preDest

    ' --- Generate audio block ---
    audioData = ""
    xx = 0

    For iSample = 1 To audioSamplesPerFrame
        SampleL1 = _MemGet(ms, ms.OFFSET + au, Single)
        SampleR1 = _MemGet(ms, ms.OFFSET + au + 4, Single)
        _SndRaw SampleL1, SampleR1

        sampleL = SampleL1 * 32767
        sampleR = SampleR1 * 32767

        If iSample Mod VisStep = 0 Then
            audL(xx) = SampleL1
            audR(xx) = SampleR1
            xx = xx + 1
        End If

        au = au + 8
        If au > ms.SIZE - 8 Then Exit Do


        uVal = sampleL And &HFFFF
        audioData = audioData + Chr$(uVal And &HFF) + Chr$((uVal \ 256) And &HFF)
        uVal = sampleR And &HFFFF
        audioData = audioData + Chr$(uVal And &HFF) + Chr$((uVal \ 256) And &HFF)
    Next

    totalAudioSamples = totalAudioSamples + audioSamplesPerFrame * 2

    ' --- Write chunk (video+audio) ---
    CreateAviData frameData, audioData

    _PutImage , img, 0
    Locate 1
    afs = LOF(aviFileNum)
    Print "Press any key to stop generating AVI. File size: "; afs; " Duration: "; Int(Timer - StartTimer)

    _Display
    _Limit vidFPS + 1
    If _Exit Then Exit Do
    Do Until _SndRawLen < .1
    Loop
Loop

_Dest preDest
_AutoDisplay
Print "Closing AVI, please wait!"
_MemFree ms
_SndClose snd

' 3) Finalize AVI (update total frames etc.)
EndAvi

Print "Done: 'VideoTest5.avi'"

End

' ------------------------------------------------------
' Sub StartAvi – opens file, writes base headers, prepares LIST movi
' ------------------------------------------------------
Sub StartAvi (aviName As String)
    Dim dummyLong As Long: dummyLong = 0

    aviFileNum = FreeFile
    If _FileExists(aviName) Then Kill aviName
    Open aviName For Binary As #aviFileNum

    ' "RIFF"
    Dim strRIFF As String
    strRIFF = "RIFF"
    Put #aviFileNum, , strRIFF
    riffSizePos = LOF(aviFileNum) + 1
    Put #aviFileNum, , dummyLong

    Dim strAVI As String
    strAVI = "AVI "
    Put #aviFileNum, , strAVI

    ' "LIST" (hdrl)
    Dim strLIST As String
    strLIST = "LIST"
    Put #aviFileNum, , strLIST
    hdrlSizePos = LOF(aviFileNum) + 1
    Put #aviFileNum, , dummyLong

    Dim strHdrl As String
    strHdrl = "hdrl"
    Put #aviFileNum, , strHdrl

    ' "avih" chunk
    Dim strAvih As String
    strAvih = "avih"
    Put #aviFileNum, , strAvih

    Dim avihChunkSize As Long: avihChunkSize = 56
    Put #aviFileNum, , avihChunkSize

    ' Prepare main AVI header
    shMainHdr.microSecPerFrame = 1000000 \ vidFPS
    ' Critically important: set a realistic data rate:
    shMainHdr.maxBytesPerSec = frameWidth * frameHeight * bytesPerPixel * vidFPS
    shMainHdr.paddingGranularity = 0
    shMainHdr.flags = &H110 ' AVIF_HASINDEX + AVIF_ISINTERLEAVED
    shMainHdr.totalFrames = 0 ' will fill real value in EndAvi
    shMainHdr.initialFrames = 0
    shMainHdr.streams = 2 ' video+audio
    shMainHdr.suggestedBufferSize = frameWidth * frameHeight * bytesPerPixel
    shMainHdr.width = frameWidth
    shMainHdr.height = frameHeight
    shMainHdr.reserved = String$(16, Chr$(0))

    mainHdrPos = LOF(aviFileNum) + 1
    Put #aviFileNum, , shMainHdr

    ' LIST strl (video)
    strLIST = "LIST"
    Put #aviFileNum, , strLIST
    Dim listVidSizePos As _Unsigned Long
    listVidSizePos = LOF(aviFileNum) + 1
    Put #aviFileNum, , dummyLong

    Dim strlVid As String
    strlVid = "strl"
    Put #aviFileNum, , strlVid

    ' "strh" video
    Dim strhVid As String
    strhVid = "strh"
    Put #aviFileNum, , strhVid
    Dim strhVidSize As Long: strhVidSize = 64
    Put #aviFileNum, , strhVidSize

    ' Prepare video stream header
    shVidStrh.fccType = "vids"
    shVidStrh.fccHandler = "DIB "
    shVidStrh.flags = 0
    shVidStrh.priority = 0
    shVidStrh.language = 0
    shVidStrh.initialFrames = 0
    shVidStrh.scale = 1
    shVidStrh.rate = vidFPS
    shVidStrh.start = 0
    shVidStrh.length = 0 ' real value in EndAvi
    shVidStrh.suggestedBufferSize = frameWidth * frameHeight * bytesPerPixel
    shVidStrh.quality = -1
    shVidStrh.sampleSize = 0
    shVidStrh.frameLeft = 0
    shVidStrh.frameTop = 0
    shVidStrh.frameRight = frameWidth
    shVidStrh.frameBottom = frameHeight

    vidStrhPos = LOF(aviFileNum) + 1
    Put #aviFileNum, , shVidStrh

    ' "strf" video => BITMAPINFOHEADER
    Dim strfVid As String
    strfVid = "strf"
    Put #aviFileNum, , strfVid
    Dim strfVidSize As Long: strfVidSize = 40
    Put #aviFileNum, , strfVidSize

    Dim bmpInfo As BITMAPINFOHEADER
    bmpInfo.size = 40
    bmpInfo.width = frameWidth
    bmpInfo.height = -frameHeight ' top–down
    bmpInfo.planes = 1
    bmpInfo.bitCount = 32
    bmpInfo.compression = 0
    bmpInfo.sizeImage = frameWidth * frameHeight * bytesPerPixel
    bmpInfo.xPelsPerMeter = 0
    bmpInfo.yPelsPerMeter = 0
    bmpInfo.clrUsed = 0
    bmpInfo.clrImportant = 0
    Put #aviFileNum, , bmpInfo

    Dim currPos As _Unsigned Long
    currPos = LOF(aviFileNum) + 1
    Dim calcListVidSize As _Unsigned Long
    calcListVidSize = currPos - listVidSizePos - 4
    Seek #aviFileNum, listVidSizePos
    Put #aviFileNum, , calcListVidSize
    Seek #aviFileNum, currPos

    ' LIST strl (audio)
    Put #aviFileNum, , strLIST
    Dim listAudSizePos As _Unsigned Long
    listAudSizePos = LOF(aviFileNum) + 1
    Put #aviFileNum, , dummyLong

    Dim strlAud As String
    strlAud = "strl"
    Put #aviFileNum, , strlAud

    Dim strhAud As String
    strhAud = "strh"
    Put #aviFileNum, , strhAud
    Dim strhAudSize As Long: strhAudSize = 64
    Put #aviFileNum, , strhAudSize

    ' Prepare audio stream header
    audStrh.fccType = "auds"
    audStrh.fccHandler = String$(4, 0)
    audStrh.flags = 0
    audStrh.priority = 0
    audStrh.language = 0
    audStrh.initialFrames = 0
    audStrh.scale = 1
    audStrh.rate = sampleRate
    audStrh.start = 0
    audStrh.length = 0 ' set final in EndAvi
    audStrh.suggestedBufferSize = sampleRate * 4
    audStrh.quality = -1
    audStrh.sampleSize = 4 ' stereo 16-bit = 4 bytes/frame
    audStrh.frameLeft = 0
    audStrh.frameTop = 0
    audStrh.frameRight = 0
    audStrh.frameBottom = 0

    audStrhPos = LOF(aviFileNum) + 1
    Put #aviFileNum, , audStrh

    Dim strfAud As String
    strfAud = "strf"
    Put #aviFileNum, , strfAud

    ' For PCM => 16 bytes waveformat
    Dim wfSize As Long
    wfSize = 16
    Put #aviFileNum, , wfSize

    Dim wf As WAVEFORMATEX
    wf.wFormatTag = 1
    wf.nChannels = 2
    wf.nSamplesPerSec = sampleRate
    wf.nBlockAlign = wf.nChannels * (16 \ 8)
    wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign
    wf.wBitsPerSample = 16
    wf.cbSize = 0
    Put #aviFileNum, , wf

    currPos = LOF(aviFileNum) + 1
    Dim calcListAudSize As _Unsigned Long
    calcListAudSize = currPos - listAudSizePos - 4
    Seek #aviFileNum, listAudSizePos
    Put #aviFileNum, , calcListAudSize
    Seek #aviFileNum, currPos

    ' LIST movi
    Put #aviFileNum, , strLIST
    moviSizePos = LOF(aviFileNum) + 1
    Put #aviFileNum, , dummyLong
    Dim strMovi As String
    strMovi = "movi"
    Put #aviFileNum, , strMovi
    moviDataStart = LOF(aviFileNum) + 1
End Sub

' ------------------------------------------------------
' Sub CreateAviData – writes video chunk (00db) + audio chunk (01wb)
' ------------------------------------------------------
Sub CreateAviData (frameData As String, audioData As String)
    Dim chunkOffset As _Unsigned Long
    chunkOffset = LOF(aviFileNum) - moviDataStart + 1

    ' Video chunk
    Dim vidChunkID As String
    vidChunkID = "00db"
    Put #aviFileNum, , vidChunkID

    Dim frameLen As Long
    frameLen = Len(frameData)
    Put #aviFileNum, , frameLen
    Put #aviFileNum, , frameData
    If (frameLen Mod 2) <> 0 Then
        Dim padV As String
        padV = Chr$(0)
        Put #aviFileNum, , padV
    End If

    idxCount = idxCount + 1
    If UBound(idxArr) < idxCount Then ReDim _Preserve idxArr(1 To idxCount + 1000) As ChunkIndex
    idxArr(idxCount).chunkID = vidChunkID
    idxArr(idxCount).flags = &H10 ' keyframe
    idxArr(idxCount).offset = chunkOffset
    idxArr(idxCount).size = frameLen

    ' Audio chunk
    Dim chunkOffsetA As _Unsigned Long
    chunkOffsetA = LOF(aviFileNum) - moviDataStart + 1

    Dim audChunkID As String
    audChunkID = "01wb"
    Put #aviFileNum, , audChunkID

    Dim audioLen As Long
    audioLen = Len(audioData)
    Put #aviFileNum, , audioLen
    Put #aviFileNum, , audioData
    If (audioLen Mod 2) <> 0 Then
        Dim padA As String
        padA = Chr$(0)
        Put #aviFileNum, , padA
    End If

    idxCount = idxCount + 1
    If UBound(idxArr) < idxCount Then ReDim _Preserve idxArr(1 To idxCount + 1000) As ChunkIndex
    idxArr(idxCount).chunkID = audChunkID
    idxArr(idxCount).flags = 0
    idxArr(idxCount).offset = chunkOffsetA
    idxArr(idxCount).size = audioLen + 1
End Sub

' ------------------------------------------------------
' Sub EndAvi – finalize: update frame counts, sizes, index
' ------------------------------------------------------
Sub EndAvi
    ' Fill the real total frames into mainHdr + video strh
    shMainHdr.totalFrames = totalFrames
    shVidStrh.length = totalFrames

    ' For stereo: totalAudioSamples are L+R separately, so we do \2
    audStrh.length = totalAudioSamples \ 2

    ' Rewrite them at known positions
    Put #aviFileNum, mainHdrPos, shMainHdr
    Put #aviFileNum, vidStrhPos, shVidStrh
    Put #aviFileNum, audStrhPos, audStrh

    ' Now fix sizes of movi, riff, hdrl
    Dim currPos As _Unsigned Long
    currPos = LOF(aviFileNum) + 1

    ' movi size
    Dim moviSize As _Unsigned Long
    moviSize = (currPos - moviSizePos) - 4
    Put #aviFileNum, moviSizePos, moviSize
    Seek #aviFileNum, currPos

    ' RIFF size
    Dim riffSize As _Unsigned Long
    riffSize = currPos - 8
    Put #aviFileNum, riffSizePos, riffSize
    Seek #aviFileNum, currPos

    ' Write idx1
    Dim strIdx As String
    strIdx = "idx1"
    Put #aviFileNum, , strIdx
    Dim idxSize As Long
    idxSize = idxCount * 16
    Put #aviFileNum, , idxSize

    Dim Nsize As _Unsigned Long
    Nsize = LOF(aviFileNum) + 1

    ' hdrl size
    Dim hdrlSize As _Unsigned Long
    hdrlSize = 310 'valid just for this format (32bit rgba, 16bit PCM stereo)  THIS WAS THE WMP BUG!!!
    Put #aviFileNum, hdrlSizePos, hdrlSize
    Seek #aviFileNum, Nsize

    ' Write all index entries
    Dim i As Long
    For i = 1 To idxCount
        Put #aviFileNum, , idxArr(i).chunkID
        Put #aviFileNum, , idxArr(i).flags
        Put #aviFileNum, , idxArr(i).offset
        Put #aviFileNum, , idxArr(i).size
    Next i

    Close #aviFileNum
End Sub


Let the GigaBytes fly!

             Angel


Reply


Messages In This Thread
AVI file format - by Petr - 03-22-2025, 08:00 PM
RE: AVI file format - by madscijr - 03-22-2025, 11:01 PM
RE: AVI file format - by mrbcx - 03-23-2025, 01:12 AM
RE: AVI file format - by ahenry3068 - 03-23-2025, 01:24 AM
RE: AVI file format - by madscijr - 03-23-2025, 02:26 AM
RE: AVI file format - by Petr - 03-23-2025, 08:58 AM
RE: AVI file format - by madscijr - 03-23-2025, 03:09 PM
RE: AVI file format - by mdijkens - 03-24-2025, 04:49 PM
RE: AVI file format - by madscijr - 03-24-2025, 06:13 PM
RE: AVI file format - by madscijr - 03-24-2025, 07:59 PM
RE: AVI file format - by Petr - 03-24-2025, 09:35 PM
RE: AVI file format - by madscijr - 03-24-2025, 09:55 PM
RE: AVI file format - by mdijkens - 03-25-2025, 08:05 AM
RE: AVI file format - by Petr - 03-25-2025, 09:20 AM
RE: AVI file format - by Steffan-68 - 03-25-2025, 05:25 PM
RE: AVI file format - by Petr - 03-25-2025, 06:14 PM
RE: AVI file format - by madscijr - 05-28-2025, 10:50 PM
RE: AVI file format - by Petr - 08-02-2025, 04:30 PM
RE: AVI file format - by madscijr - 08-03-2025, 01:03 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
  APNG File Format Petr 5 567 11-20-2025, 02:32 PM
Last Post: ahenry3068
  GIF89a File Format Petr 6 1,370 03-04-2025, 01:20 AM
Last Post: a740g
  PCX file format Petr 13 3,451 03-01-2025, 10:52 PM
Last Post: Petr
  BMP File format Petr 8 1,786 02-23-2025, 07:54 PM
Last Post: Petr

Forum Jump:


Users browsing this thread: 1 Guest(s)