Theora playback project

BlitzMax Forums/BlitzMax Programming/Theora playback project

PantsOn(Posted 2007) [#1]
Hi

Had a look at the theora file format and have started to write a loader for blitz.
At the mo... I've nearly completed the header loaders. Still a bit of work to do.

As Theora is open source... heres my code for blitz (its very messy at the mo). If any one wants to add to it.

All docs can be found at www.theora.org
temp3.ogg was created using the ffmpeg2theora util found on the above site from a standard AVI.
SuperStrict

Type theora_typ
	Field bank:TBank
	Field stream:TStream
	Field pos:Int
	
	Field VMAJ:Int
	Field VMIN:Int
	Field VREV:Int
	
	Field FMBW:Int,FMBH:Int
	
	Field NSBS:Int,NBS:Int,NBMS:Int,NMBS:Int
	
	Field PICW:Int,PICH:Int
	Field PICX:Int,PICY:Int
	
	Field FRN:Int,FRD:Int
	
	Field PARN:Int,PARD:Int
	
	Field CS:Int
	
	Field PF:Int
	
	Field NOMBR:Int
	
	Field QUAL:Int
	
	Field KFGSHIFT:Int
	
	Field vendor:String
	Field comments:String[]
	
	Field LFLIMS:Int[64]
	Field ACSCALE:Int[64]
	Field DCSCALE:Int[64]
	
	Field BMS:Int[385,64]
	Field NQRS:Int[2,3]
	Field QRSIZES:Int[2,3,64]
	Field QRBMIS:Int[2,3,64]
End Type

Global t:theora_typ
Global bits_val:Int
Global bits_b:Int

LoadTheora("temp3.ogg")

Function LoadTheora:Int(file:String)
	t = New theora_typ
	T.stream = OpenStream(file)
	
	SeekStream(t.stream,28)
	If Not Header_ID() Then End

	SeekStream(t.stream,109)
	If Not Header_Comment() Then End
	
	If Not Header_Setup() Then End
	
	CloseStream t.stream
End Function

Function Header_Setup:Int()
	Local nbits:Int
	Local i:Int
	Local j:Int
	Local NEWQR:Int
	Local qti:Int, qtj:Int
	Local pli:Int, plj:Int
	Local RPQR:Int
	
	If Header_Common("82") = False Return False
	Print "header setup found"
	
	nbits = readbits(3,True)
	Print nbits	
	i = 0
	While i < 64
		t.LFLIMS[i] = readbits(nbits) 
		i:+1
	Wend

	nbits = readbits(4) + 1
	i = 0
	While i < 64
		t.ACSCALE[i] = readbits(nbits) 
'		Print i + " " + t.ACSCALE[i]
		i:+1
	Wend

	nbits = readbits(4) + 1
	i = 0
	While i < 64
		t.DCSCALE[i] = readbits(nbits) 
'		Print i + " " + t.DCSCALE[i]
		i:+1
	Wend

	t.NBMS = readbits(9) + 1
	Print t.NBMS
	
	i = 0
	While i < t.NBMS
		j = 0
		While j < 64
			t.BMS[i,j] = readbits(8)
			j:+1
		Wend
		i:+1
	Wend
	
	qti = 0
	While qti<2
		pli = 0
		While pli < 3
			If qti>0 Or pli>0 
				NEWQR = readbits(1)
			Else
				NEWQR = 1
			EndIf
			
			Select NEWQR
			Case 0
				' copy
				If qti > 0 
					RPQR = 1
				Else
					RPQR = 0
				EndIf
				
				If RPQR = 1
					qtj = qti - 1
					plj = pli
				Else
					qtj = (3 * qti + pli - 1)/3
					plj = (pli + 2) Mod 3
				EndIf
				
				t.NQRS[qti,pli] = t.NQRS[qtj,plj]
				
				i = 0
				While i<63
					t.QRSIZES[qti,pli,i] = t.QRSIZES[qtj,plj,i]
					t.QRBMIS[qti,pli,i] = t.QRBMIS[qtj,plj,i]
					i:+1
				Wend
			Case 1
				' new set
			EndSelect
			pli:+1
		Wend
		qti:+1
	Wend
	Print "header setup complete"
	Return True
End Function

Function ReadBits:Int(nbits:Int,reset:Int=False)
	Local tmp:Int
	Local val:Int
	
	' mask
	tmp = (Ceil(2^nbits)-1)
	
	' if reset
	If reset = True
		bits_b = 0
		bits_val = (ReadByte(t.stream) Shl 8) + ReadByte(t.stream)
	EndIf
	
'	Print Bin(bits_val)
'	Print Bin(tmp)
	val = (bits_val Shr (16-(nbits+bits_b))) & tmp
	
	bits_b:+nbits
	If bits_b > 7
		bits_b:-8
		bits_val = ((bits_val Shl 8) & %1111111100000000) + ReadByte(t.stream)
	EndIf
	
	Return val
End Function

Function Header_Comment:Int()
	Local ln:Int
	Local i:Int,j:Int
	Local ncomments:Int
		
	If Header_Common("81") = False Return False
	Print "header comment found"
	
	ln = ReadByte(t.stream)
	ln:+ ReadByte(t.stream) Shl 8
	ln:+ ReadByte(t.stream) Shl 16
	ln:+ ReadByte(t.stream) Shl 24
	
	' vendor
	t.vendor = ""
	i = 0
	While i < ln
		t.vendor:+Chr(ReadByte(t.stream))
		i:+1
	Wend
'	Print t.vendor
	
	' comments
	ln = ReadByte(t.stream)
	ln:+ ReadByte(t.stream) Shl 8
	ln:+ ReadByte(t.stream) Shl 16
	ln:+ ReadByte(t.stream) Shl 24

	' clear commnets list
	t.comments = Null
	GCCollect()

	ncomments = ln
	i = 0
	While i < ncomments
		ln = ReadByte(t.stream)
		ln:+ ReadByte(t.stream) Shl 8
		ln:+ ReadByte(t.stream) Shl 16
		ln:+ ReadByte(t.stream) Shl 24
			
		t.comments = t.comments:String[..i+1]
		t.comments[i] = ""
		j = 0
		While j < ln
			t.comments[i]:+Chr(ReadByte(t.stream))
			j:+1
		Wend
'		Print t.comments[i]
		i:+1
	Wend
		
	Print "header comment complete"
	Return True
End Function

Function Header_ID:Int()
	Local tmp:Int
	
	If Header_Common("80") = False Return False
	Print "header id found"
	
	t.VMAJ = ReadByte(t.stream)
	t.VMIN = ReadByte(t.stream)
	t.VREV = ReadByte(t.stream)
	
	t.FMBW = Read16(t.stream)
	t.FMBH = Read16(t.stream)
	
	t.PICW = Read24(t.stream) & %11111111111111111111
	t.PICH = Read24(t.stream) & %11111111111111111111
	
	t.PICX = ReadByte(t.stream)
	t.PICY = ReadByte(t.stream)
	
	t.FRN = Read32(t.stream)
	t.FRD = Read32(t.stream)
	
	t.PARN = Read24(t.stream)
	t.PARD = Read24(t.stream)
	
	t.CS = ReadByte(t.stream)
	
	t.NOMBR = Read24(t.stream)
	
	tmp = Read16(t.stream)
	t.QUAL = (tmp & %1111110000000000) Shr 10
	t.KFGSHIFT = (tmp & %0000001111100000) Shr 5
	t.PF = (tmp & %0000000000011000) Shr 3
		
	If t.PF = 1 Then Return False
	If (tmp & %111) <> 0 Then Return False
	
	Select t.PF
	Case 0
		t.NSBS = ((t.FMBW+1)/2)*((t.FMBH+1)/2)+2*((t.FMBW+3)/4)*((t.FMBH+3)/4)
		t.NBS = 6*t.FMBW*t.FMBH
	Case 2
		t.NSBS = ((t.FMBW+1)/2)*((t.FMBH+1)/2)+2*((t.FMBW+3)/4)*((t.FMBH+1)/2)
		t.NBS = 8*t.FMBW*t.FMBH
	Case 3
		t.NSBS = 3*((t.FMBW+1)/2)*((t.FMBH+1)/2)
		t.NBS = 12*t.FMBW*t.FMBH
	End Select 
	
	t.NMBS = t.FMBW*t.FMBH
	
	Print "header ID complete"
	Return True
End Function

Function Read32:Int(stream:TStream)
	Return (ReadByte(stream) Shl 24) + (ReadByte(stream) Shl 16) + (ReadByte(stream) Shl 8) + ReadByte(stream)
End Function

Function Read16:Int(stream:TStream)
	'Return (ReadByte(stream) Shl 8) + ReadByte(stream)

	Local val:Int
	
	val = ReadShort(stream)
	Return ((val Shr 8) & 255) + ((val & 255) Shl 8)	
End Function

Function Read24:Int(stream:TStream)
	Return (ReadByte(stream) Shl 16) + (ReadByte(stream) Shl 8) + ReadByte(stream)
End Function

Function Header_Common:Int(HX:String)	
	If Lower(Right(Hex( ReadByte(T.stream) ),2)) <> Lower(HX) Then Return False
	If peekstring(T.stream,6) <> "theora" Then Return False
	Return True
End Function

Function peekstring:String(stream:TStream,length:Int)
	Local i:Int
	Local s:String
	
	s = ""
	
	i = 0
	While i < length
		s:+Chr(ReadByte(stream))
		i:+1
	Wend
	
	Return s
End Function




PantsOn(Posted 2007) [#2]
I will try and finish the header loaders today sometime.


Brucey(Posted 2007) [#3]
Isn't there a "library" to do this already somewhere? (rather than writing a BlitzMax loader)


PantsOn(Posted 2007) [#4]
There are libs on theora.org but i don't know how to plug them into Blitz. Would the libs be cross-platform?


Brucey(Posted 2007) [#5]
Would the libs be cross-platform?

They should be. I imagine that all they do is decompress/decode the stream and give you a pointer to some RGB data (for a video frame) that you can then do with what you will.


PantsOn(Posted 2007) [#6]
i'll have a look at the C libs.
I can read C code no problem, just haven't written anything in C for over 10 years. (Just getting back into it for NDS programming)

If someone else knows what they are doing, they can get the libs and file spec from www.theora.org