http://dis.eafit.edu.co/areas/telematica/proyectos/jmf/src/jmf211a_scsl_src/src/share/com/sun/media/parser/video/AviParser.java
http://themonospot.googlecode.com/svn-history/r77/trunk/themonospot-base/src/components/clsParserAVI.cs
*********************************************************************************
using System;
using System.IO;
using System.Text;
using System.Threading;
using Utility;
using System.Collections;
using System.Collections.Generic;
using themonospot_Base_Main;
namespace themonospot_Base_Main
{
/// <summary>
/// AVI Parser for Themonospot
/// </summary>
public class clsParserAVI : IParser
{
#region CONSTANTS
public const int DWORDSIZE = 4;
public const int TWODWORDSSIZE = 8;
public static readonly string RIFF4CC = "RIFF";
public static readonly string RIFX4CC = "RIFX";
public static readonly string LIST4CC = "LIST";
// Known file types
public static readonly int ckidAVI = Utility.clsEncoding.ToFourCC("AVI ");
public static readonly int ckidWAV = Utility.clsEncoding.ToFourCC("WAVE");
public static readonly int ckidRMID = Utility.clsEncoding.ToFourCC("RMID");
#endregion
#region GENERIC OBJECTS
public List<clsInfoItem> VideoItems
{
get {return _VideoItems; }
}
public List<clsInfoItem> AudioItems
{
get {return _AudioItems; }
}
private List<clsInfoItem> _VideoItems;
private List<clsInfoItem> _AudioItems;
private AVIMAINHEADER myAviHeader = new AVIMAINHEADER();
private AVISTREAMHEADER[] myAviStreamHeader = new AVISTREAMHEADER[0];
private BITMAPINFOHEADER[] myAviBitMap = new BITMAPINFOHEADER[0];
private WAVEFORMATEX[] myAviWave = new WAVEFORMATEX[0];
private string[] myUData = new string[0];
private string[] myMUData = new string[0];
private string _m_filename;
private string _m_shortname;
private string _udToChange = "";
private long _m_fourCC_AviStreamHeader;
private long _m_fourCC_AviVideoHeader;
private long _m_filesize;
private long _m_posStream;
private long _m_MoviSize=0;
private long _m_MoviSizeNew=0;
private long _m_MoviStart=0;
private long _m_IdxSize=0;
private long _m_IdxStart=0;
private double sizOfAudio=0;
private FileStream aviStreamReader = null;
private FileStream outFile = null;
private bool pbDetect = false;
private bool _rec_ix_presence = false;
private int stdBufferSize = 4096;
private int newTotalBytes = 0;
long[] framesOffset = new long[0];
long[] framesSize = new long[0];
#endregion GENERIC OBJECTS
#region PROPERTIES
public string udToChange
{
get { return _udToChange;}
set { _udToChange = value ;}
}
public bool rec_ix_presence
{ get { return _rec_ix_presence;} }
public long fourCC_AVISTREAMHEADER_offset
{ get { return _m_fourCC_AviStreamHeader;} }
public long fourCC_AVIVIDEOHEADER_offset
{ get { return _m_fourCC_AviVideoHeader;} }
public AVIMAINHEADER headerFile
{ get { return myAviHeader;} }
public BITMAPINFOHEADER[] videoStreams
{ get { return myAviBitMap;} }
public WAVEFORMATEX[] audioStreams
{ get { return myAviWave;} }
public AVISTREAMHEADER[] headerStreams
{ get { return myAviStreamHeader;} }
public string[] userData
{ get { return myUData;} }
public string[] MOVIuserData
{ get { return myMUData;} }
public string m_filename
{ get { return _m_filename; } }
public string m_shortname
{ get { return _m_shortname; } }
public long m_filesize
{ get { return _m_filesize; } }
public long m_MoviSize
{
get { return _m_MoviSize; }
set { _m_MoviSize = value; }
}
public long m_MoviStart
{
get { return _m_MoviStart; }
set { _m_MoviStart = value; }
}
public long m_IdxSize
{
get { return _m_IdxSize; }
set { _m_IdxSize = value; }
}
public long m_IdxStart
{
get { return _m_IdxStart; }
set { _m_IdxStart = value; }
}
#endregion PROPERTIES
#region METHODS TO ADD ITEMS AT STRUCTURES
private void addNew_AVISTREAMHEADER()
{
int j;
if (myAviStreamHeader == null)
{
// There isn't any elements
myAviStreamHeader = new AVISTREAMHEADER[1];
}
else
{
// There is another element
AVISTREAMHEADER[] tmpASR;
tmpASR = new AVISTREAMHEADER[myAviStreamHeader.Length + 1];
for (j=0; j<myAviStreamHeader.Length; j++)
{ tmpASR[j] = myAviStreamHeader[j]; }
myAviStreamHeader = tmpASR;
}
return;
}
private void addNew_BITMAPINFOHEADER()
{
int j;
if (myAviBitMap == null)
{
// There isn't any elements
myAviBitMap = new BITMAPINFOHEADER[1];
}
else
{
// There is another element
BITMAPINFOHEADER[] tmpASR;
tmpASR = new BITMAPINFOHEADER[myAviBitMap.Length + 1];
for (j=0; j<myAviBitMap.Length; j++)
{ tmpASR[j] = myAviBitMap[j]; }
myAviBitMap = tmpASR;
}
return;
}
private void addNew_WAVEFORMATEX()
{
int j;
if (myAviWave == null)
{
// There isn't any elements
myAviWave = new WAVEFORMATEX[1];
}
else
{
// There is another element
WAVEFORMATEX[] tmpASR;
tmpASR = new WAVEFORMATEX[myAviWave.Length + 1];
for (j=0; j<myAviWave.Length; j++)
{ tmpASR[j] = myAviWave[j]; }
myAviWave = tmpASR;
}
return;
}
private void addNew_STRING()
{
int j;
if (myUData == null)
{
// There isn't any elements
myUData = new string[1];
}
else
{
// There is another element
string[] tmpASR;
tmpASR = new string[myUData.Length + 1];
for (j=0; j<myUData.Length; j++)
{ tmpASR[j] = myUData[j]; }
myUData = tmpASR;
}
return;
}
private void addNew_moviSTRING()
{
int j;
if (myMUData == null)
{
// There isn't any elements
myMUData = new string[1];
}
else
{
// There is another element
string[] tmpASR;
tmpASR = new string[myMUData.Length + 1];
for (j=0; j<myMUData.Length; j++)
{ tmpASR[j] = myMUData[j]; }
myMUData = tmpASR;
}
return;
}
#endregion METHODS TO ADD ITEMS AT STRUCTURES
public clsParserAVI()
{
}
/// <summary>
/// Parse the selected file
/// </summary>
public void OpenFile(string FileName)
{
// File Not Found...
if (File.Exists(FileName) != true)
throw new ParserException("File (" + FileName + ") Not Found...");
_VideoItems = new List<clsInfoItem>();
_AudioItems = new List<clsInfoItem>();
// Read File Infos
FileInfo fi = new FileInfo(FileName);
_m_filename = fi.FullName;
_m_shortname = fi.Name;
_m_filesize = fi.Length;
// DEBUG
Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("");
Console.WriteLine("_m_filename = " + _m_filename);
Console.WriteLine("_m_shortname = " + _m_shortname);
Console.WriteLine("_m_filesize = " + _m_filesize.ToString("#,###.##"));
// Open the streamer
aviStreamReader = new FileStream(_m_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
_m_posStream = 0;
#region Verify Integrity
// Detect File Format
int FourCC = readDWord();
_m_posStream +=4;
string sFourCC;
string hexFourCC;
int dataSize = readDWord();
int fileType = readDWord();
_m_posStream +=8;
// Check FourCC Header
sFourCC = Utility.clsEncoding.FromFourCC(FourCC);
Console.WriteLine("FourCC ".PadRight(20,(char)46) + sFourCC + " (" + dataSize.ToString("#,###.##") + ")");
if (sFourCC != RIFF4CC &&
sFourCC != RIFX4CC)
{
// No Riff File
aviStreamReader.Close(); aviStreamReader = null;
throw new ParserException("Error. Not a valid RIFF file");
}
// Check File Size
if (_m_filesize < (dataSize + 8) )
{
// No Riff File
aviStreamReader.Close(); aviStreamReader = null;
throw new ParserException("Error. Truncated file");
}
#endregion
byte[] tmpByteArray;
int byteToRead;
int readBytes;
long byteOfList;
int byteOfINFO=0;
int byteOfINFOReaded=0;
string strType = "";
_m_fourCC_AviVideoHeader = 0;
_m_fourCC_AviStreamHeader = 0;
// Loop until EOF
while (_m_posStream < _m_filesize )
{
FourCC = readDWord();
sFourCC = Utility.clsEncoding.FromFourCC(FourCC);
hexFourCC = FourCC.ToString("X8");
byteToRead = readDWord();
// Adjust bytes to read (no odd)
// TO VERIFY ***********************************
if (byteToRead % 2 != 0)
byteToRead++;
_m_posStream +=8;
Console.WriteLine("FourCC ".PadRight(20,(char)46) + sFourCC + " (" + byteToRead.ToString("#,###.##") + ") [" + hexFourCC + "]" + " filepos=" + _m_posStream.ToString("#,###.00") );
// Check REC? or IX?? FourCC presence
if (sFourCC.ToLower().Substring(3) == "rec" ||
sFourCC.ToLower().Substring(2) == "ix" )
{
_rec_ix_presence = true;
}
// Check memory
if (_m_posStream + byteToRead > _m_filesize)
{
aviStreamReader.Close();
aviStreamReader = null;
return;
}
// Parse FourCC
if (sFourCC == LIST4CC)
{
// LIST section
FourCC = readDWord();
_m_posStream += 4;
sFourCC = Utility.clsEncoding.FromFourCC(FourCC);
hexFourCC = FourCC.ToString("X8");
byteOfList = byteToRead;
Console.WriteLine("LIST type ".PadRight(20,(char)46) + sFourCC + "[" + hexFourCC + "]"+ " filepos=" + _m_posStream.ToString("#,###.00") );
// Verify FourCC
if (FourCC == AviRiffData.ckidAVIMovieData)
{
// skip "movi" section 6D 6F 76 69
_m_MoviStart = aviStreamReader.Position;
_m_posStream += byteOfList-4;
// aviStreamReader.Seek(byteOfList-4, SeekOrigin.Current);
parseMoviChunk(byteOfList-4);
_m_MoviSize = byteOfList-4;
}
else if (FourCC == AviRiffData.ckidINFOList )
{
// INFO section
byteOfINFOReaded=0;
byteOfINFO=(int)(byteOfList-4);
}
}
else
{
// Other TAGS
if(FourCC == AviRiffData.ckidMainAVIHeader)
{
// "avih"
tmpByteArray = new byte[56];
readBytes = aviStreamReader.Read(tmpByteArray,0,56);
_m_posStream +=readBytes;
myAviHeader.loadDataStructure(tmpByteArray);
}
else if(FourCC == AviRiffData.ckidAVIStreamHeader)
{
// "strh"
tmpByteArray = new byte[byteToRead];
readBytes = aviStreamReader.Read(tmpByteArray,0,byteToRead);
_m_posStream +=byteToRead;
// Update Array of Stream Headers
addNew_AVISTREAMHEADER();
AVISTREAMHEADER tmpSH = new AVISTREAMHEADER();
tmpSH.loadDataStructure(tmpByteArray);
myAviStreamHeader[ myAviStreamHeader.Length-1 ] = tmpSH;
strType = clsEncoding.FromFourCC(tmpSH.fccType);
Console.WriteLine("STREAM TYPE ".PadRight(20,(char)46) + clsEncoding.FromFourCC(tmpSH.fccType) );
Console.WriteLine("STREAM HEARER ".PadRight(20,(char)46) + clsEncoding.FromFourCC(tmpSH.fccHandler) );
// fourCC_AviStreamHeader position
if (_m_fourCC_AviVideoHeader == 0)
_m_fourCC_AviStreamHeader = _m_posStream - (byteToRead - 4);
}
else if(FourCC == AviRiffData.ckidStreamFormat)
{
// "strf"
if (strType == "vids")
{
tmpByteArray = new byte[byteToRead];
readBytes = aviStreamReader.Read(tmpByteArray,0,byteToRead);
_m_posStream +=readBytes;
// fourCC_AviVideoHeader position
_m_fourCC_AviVideoHeader = _m_posStream - 24;
// Update Array of Stream Format Video
addNew_BITMAPINFOHEADER();
BITMAPINFOHEADER tmpBMP = new BITMAPINFOHEADER();
tmpBMP.loadDataStructure(tmpByteArray);
myAviBitMap[ myAviBitMap.Length-1 ] = tmpBMP;
}
else if (strType == "auds")
{
tmpByteArray = new byte[byteToRead];
readBytes = aviStreamReader.Read(tmpByteArray,0,byteToRead);
_m_posStream +=readBytes;
// Update Array of Stream Format Video
addNew_WAVEFORMATEX();
WAVEFORMATEX tmpWFR = new WAVEFORMATEX();
tmpWFR.loadDataStructure(tmpByteArray);
myAviWave[ myAviWave.Length-1 ] = tmpWFR;
string strAudioType = tmpWFR.wFormatTag.ToString("X4");
}
else
{
// other FourCC in a stream list
aviStreamReader.Seek(byteToRead, SeekOrigin.Current);
_m_posStream +=byteToRead;
}
}
else if(FourCC == AviRiffData.ckidAVIOldIndex)
{
// "idx1"
// parseIdxChunk(byteToRead);
_m_IdxStart = aviStreamReader.Position;
_m_IdxSize = byteToRead;
aviStreamReader.Seek(byteToRead, SeekOrigin.Current);
_m_posStream +=byteToRead;
}
else if(FourCC == AviRiffData.ckidJUNKTag)
{
// "JUNK"
// Skip
tmpByteArray = new byte[byteToRead];
readBytes = aviStreamReader.Read(tmpByteArray,0,byteToRead);
_m_posStream +=readBytes;
clsEncoding myEnc = new clsEncoding();
string theStrData = myEnc.getAsciiFromArray(tmpByteArray);
if (theStrData.Trim() != "")
{
addNew_STRING();
myUData[myUData.Length-1] = theStrData;
Console.WriteLine("JUNKDATA ".PadRight(20,(char)46) + myEnc.getAsciiFromArray(tmpByteArray));
}
// aviStreamReader.Seek(byteToRead, SeekOrigin.Current);
}
else if(FourCC == AviRiffData.ckidAVIISFT)
{
// "ISFT"
tmpByteArray = new byte[byteToRead];
readBytes = aviStreamReader.Read(tmpByteArray,0,byteToRead);
_m_posStream +=readBytes;
byteOfINFOReaded += byteToRead+8;
addNew_STRING();
clsEncoding myEnc = new clsEncoding();
myUData[myUData.Length-1] = myEnc.getAsciiFromArray(tmpByteArray);
Console.WriteLine("ISFTDATA ".PadRight(20,(char)46) + myEnc.getAsciiFromArray(tmpByteArray));
// Check remaining byte
if ((byteOfINFO - byteOfINFOReaded) < 8 )
aviStreamReader.Seek((byteOfINFO - byteOfINFOReaded), SeekOrigin.Current);
_m_posStream +=(byteOfINFO - byteOfINFOReaded);
}
else
{
// other FourCC
aviStreamReader.Seek(byteToRead, SeekOrigin.Current);
_m_posStream +=byteToRead;
}
}
} // end while (_m_posStream < dataSize)
// Close the streamer
aviStreamReader.Close(); aviStreamReader = null;
GetAudioInformations();
GetVideoInformations();
return;
}
// Parse MOVI Chunk to extract xxdc or xxdb subarea
private void parseMoviChunk(long MoviChunkSize)
{
long tmpMoviPointer = 0;
int FourCC = 0;
int byteOfRead = 0;
string sFourCC = "";
int frameCount = 0;
pbDetect=false;
while (tmpMoviPointer < MoviChunkSize)
{
FourCC = readDWord();
sFourCC = Utility.clsEncoding.FromFourCC(FourCC);
byteOfRead = readDWord();
// Adjust bytes to read (no odd)
if ((byteOfRead % 2) != 0 )
byteOfRead++;
tmpMoviPointer += 8;
// Console.WriteLine(sFourCC + " Founded, size = " + byteOfRead.ToString());
if (sFourCC.Substring(2,2) == "dc" || sFourCC.Substring(2,2) == "db")
{
frameCount ++;
if (frameCount == 1)
parseDCuserdata(byteOfRead);
else
parseDCvopdata(byteOfRead);
tmpMoviPointer += byteOfRead;
// scan only first 100 xxdc or xxdb frames in MOVI chunk
if (frameCount >= 200)
{
aviStreamReader.Seek(MoviChunkSize - tmpMoviPointer, SeekOrigin.Current);
tmpMoviPointer += (MoviChunkSize - tmpMoviPointer);
}
}
else
{
//Console.WriteLine(sFourCC + " Founded, size = " + byteOfRead.ToString());
aviStreamReader.Seek(byteOfRead, SeekOrigin.Current);
tmpMoviPointer += byteOfRead;
}
}
}
// Extract UserData info from DC subarea
private void parseDCuserdata(long DCsubareaSize)
{
//aviStreamReader.Seek(DCsubareaSize, SeekOrigin.Current);
clsEncoding myEnc = new clsEncoding();
string outValue="";
int sPoint = 0;
int ePoint = 0;
byte[] dcBuffer = new byte[(int)DCsubareaSize];
aviStreamReader.Read(dcBuffer,0,(int)DCsubareaSize);
// Find UserData ... START
sPoint = myEnc.compareBytesArray(dcBuffer, AviRiffData.UserDataBytes, 0);
while (sPoint < DCsubareaSize && sPoint >= 0)
{
ePoint = myEnc.compareBytesArray(dcBuffer, AviRiffData.UserDataBytes, sPoint + 3);
if (ePoint < 0)
ePoint = myEnc.compareBytesArray(dcBuffer, AviRiffData.VOLStartBytes , sPoint + 3);
if (ePoint < 0)
ePoint = myEnc.compareBytesArray(dcBuffer, AviRiffData.VOPStartBytes , sPoint + 3);
if (ePoint < 0)
{
// from sPoint to end of Byte Array
outValue = myEnc.getHexFromBytes(dcBuffer,sPoint+4, ((int)DCsubareaSize - (sPoint+3)));
addNew_moviSTRING();
myMUData[myMUData.Length-1] = myEnc.getAsciiFromHex(outValue);
Console.WriteLine("UD founded".PadRight(20,(char)46) + myEnc.getAsciiFromHex(outValue));
break;
}
else
{
// from sPoint to ePoint
outValue = myEnc.getHexFromBytes(dcBuffer,sPoint+4, (ePoint - (sPoint+4)));
addNew_moviSTRING();
myMUData[myMUData.Length-1] = myEnc.getAsciiFromHex(outValue);
Console.WriteLine("UD founded".PadRight(20,(char)46) + myEnc.getAsciiFromHex(outValue));
sPoint = myEnc.compareBytesArray(dcBuffer, AviRiffData.UserDataBytes, ePoint);
}
}
// Find UserData ... END
dcBuffer = null;
}
// Extract VOP count in to DC subarea (Packet Bitstream Detect)
private void parseDCvopdata(long DCsubareaSize)
{
//aviStreamReader.Seek(DCsubareaSize, SeekOrigin.Current);
clsEncoding myEnc = new clsEncoding();
//string outValue="";
int sPoint = 0;
//int ePoint = 0;
int vopCount = 0;
byte[] dcBuffer = new byte[(int)DCsubareaSize];
aviStreamReader.Read(dcBuffer,0,(int)DCsubareaSize);
// Find Packed Bitstream ... START
sPoint = myEnc.compareBytesArray(dcBuffer, AviRiffData.VOPStartBytes, 0);
while (sPoint < (DCsubareaSize-2) && sPoint >= 0)
{
vopCount++;
sPoint = myEnc.compareBytesArray(dcBuffer, AviRiffData.VOPStartBytes, sPoint+3);
}
// Find Packed Bitstream ... END
// Debug Packet Bitstream Detect
// Console.WriteLine("VOP detect".PadRight(20,(char)46) + vopCount.ToString());
if (vopCount > 1)
pbDetect = true;
dcBuffer = null;
}
// Parse IDX_ Chunk to extract index
private void parseIdxChunk(long IdxChunkSize)
{
long tmpIdxPointer = 0;
int FourCC = 0;
int byteOfRead = 0;
string sFourCC = "";
int frameCount = 0;
while (tmpIdxPointer < IdxChunkSize)
{
FourCC = readDWord();
sFourCC = Utility.clsEncoding.FromFourCC(FourCC);
// Adjust bytes to read (no odd)
if ((byteOfRead % 2) != 0 )
byteOfRead++;
tmpIdxPointer += 4;
frameCount ++;
// Console.WriteLine(sFourCC + " Founded, size = " + (byteOfRead).ToString());
aviStreamReader.Seek(12, SeekOrigin.Current);
tmpIdxPointer += 12;
}
Console.WriteLine("Total Frame Counted: " + frameCount.ToString() );
}
/// <summary>
/// Detect audio codec
/// </summary>
/// <param name="audioVal">codec id</param>
/// <returns>codec string name</returns>
public string parseAudioType(string audioVal)
{
// return name of codec audio
if (audioVal == "0055") return "0x" + audioVal + " (MP3)";
else if (audioVal == "0001") return "0x" + audioVal + " (PCM)";
else if (audioVal == "2001") return "0x" + audioVal + " (DTS)";
else if (audioVal == "000A") return "0x" + audioVal + " (WMA9)";
else if (audioVal == "0030") return "0x" + audioVal + " (Dolby AC2)";
else if (audioVal == "0050") return "0x" + audioVal + " (MPEG)";
else if (audioVal == "2000") return "0x" + audioVal + " (AC3)";
else
return "0x" + audioVal + " (?)";
}
public void saveNewAvi(string newFileName,
ref bool _redrawInfo,
ref string _saveError,
ref double _saveStatus,
ref bool _saveFlag,
ref double _totProgressItems,
ref string _saveInfo )
{
// Begin Write new avi file
aviStreamReader = new FileStream(_m_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
outFile = new FileStream(newFileName, FileMode.Create,FileAccess.Write, FileShare.None);
FileInfo fi = new FileInfo(_m_filename);
long filmsize = fi.Length;
long filmoffset = 0;
long diffBytes = 0;
byte[] tmpBytes;
int totMoviItems = Convert.ToInt32(_m_IdxSize / 16) + 3000;
framesOffset = new long[ totMoviItems ];
framesSize = new long[ totMoviItems ];
int bufferSize = stdBufferSize;
// Write data before MOVI chunk
_totProgressItems = (double)_m_MoviStart;
Console.WriteLine("Write Header START");
_saveInfo = clsLanguages.EXPMESSAGE1;
_redrawInfo=true;
Console.WriteLine("filmoffset = " + filmoffset.ToString() + " - _m_MoviStart = " + _m_MoviStart.ToString() );
while (filmoffset < _m_MoviStart)
{
if ((filmoffset + bufferSize) > _m_MoviStart)
bufferSize = (int)(_m_MoviStart - filmoffset);
tmpBytes = new byte[bufferSize];
aviStreamReader.Read(tmpBytes, 0, bufferSize);
outFile.Write(tmpBytes, 0, bufferSize);
_saveStatus = (double)filmoffset;
// myPBar.Text = Convert.ToInt32(myPBar.Fraction * 100).ToString("D3") + "%";
if (_saveFlag == false)
{
outFile.Close();
outFile.Dispose();
outFile = null;
aviStreamReader.Close();
aviStreamReader.Dispose();
aviStreamReader = null;
return;
}
filmoffset += bufferSize;
}
Console.WriteLine("Write Header END");
// Write new MOVI chunk from old
if (writeMoviChunk(ref _redrawInfo, ref _saveError, ref _saveStatus, ref _saveFlag, ref _totProgressItems, ref _saveInfo) != 0)
{
outFile.Close();
outFile.Dispose();
outFile = null;
aviStreamReader.Close();
aviStreamReader.Dispose();
aviStreamReader = null;
return;
}
// Write data before IDX1 chunk
diffBytes = (_m_IdxStart - aviStreamReader.Position);
bufferSize = stdBufferSize;
_saveStatus = 0;
_totProgressItems = (double)diffBytes;
filmoffset = 0;
Console.WriteLine("Write CONTENT_1 START");
_saveInfo = clsLanguages.EXPMESSAGE2;
_redrawInfo = true;
while (filmoffset < diffBytes)
{
if ((filmoffset + bufferSize) > diffBytes)
bufferSize = (int)(diffBytes - filmoffset);
tmpBytes = new byte[bufferSize];
aviStreamReader.Read(tmpBytes, 0, bufferSize);
outFile.Write(tmpBytes, 0, bufferSize);
_saveStatus = (double)filmoffset;
// myPBar.Text = Convert.ToInt32(myPBar.Fraction * 100).ToString("D3") + "%";
if (_saveFlag == false)
{
outFile.Close();
outFile.Dispose();
outFile = null;
aviStreamReader.Close();
aviStreamReader.Dispose();
aviStreamReader = null;
return;
}
filmoffset += bufferSize;
}
Console.WriteLine("Write CONTENT 1 END");
// Write new IDX1 chunk from new MOVI created
if (writeIdx1Chunk(ref _redrawInfo, ref _saveError, ref _saveStatus, ref _saveFlag, ref _totProgressItems, ref _saveInfo) != 0)
{
outFile.Close();
outFile.Dispose();
outFile = null;
aviStreamReader.Close();
aviStreamReader.Dispose();
aviStreamReader = null;
return;
}
// Write data after IDX1 chunk
diffBytes = (filmsize - aviStreamReader.Position);
bufferSize = stdBufferSize;
_saveStatus = 0;
_totProgressItems = (double)diffBytes;
filmoffset = 0;
Console.WriteLine("Write CONTENT 2 START");
_saveInfo = clsLanguages.EXPMESSAGE2;
_redrawInfo = true;
while (filmoffset < diffBytes)
{
if ((filmoffset + bufferSize) > diffBytes)
bufferSize = (int)(diffBytes - filmoffset);
tmpBytes = new byte[bufferSize];
aviStreamReader.Read(tmpBytes, 0, bufferSize);
outFile.Write(tmpBytes, 0, bufferSize);
_saveStatus = (double)filmoffset;
// myPBar.Text = Convert.ToInt32(myPBar.Fraction * 100).ToString("D3") + "%";
if (_saveFlag == false)
{
outFile.Close();
outFile.Dispose();
outFile = null;
aviStreamReader.Close();
aviStreamReader.Dispose();
aviStreamReader = null;
return;
}
filmoffset += bufferSize;
}
Console.WriteLine("Write CONTENT 2 END");
// Close output
outFile.Close();
outFile.Dispose();
outFile = null;
// Read File Infos
fi = new FileInfo(newFileName);
newTotalBytes = (int)(fi.Length - 8);
fi = null;
// Update MOVI Size and File Size
outFile = new FileStream(newFileName, FileMode.Open,FileAccess.Write, FileShare.None);
outFile.Seek(4,SeekOrigin.Begin);
outFile.Write(intToByteArray(newTotalBytes),0,4);
outFile.Seek(_m_MoviStart - 8,SeekOrigin.Begin);
outFile.Write(intToByteArray((int)(_m_MoviSizeNew + 4)),0,4);
// Close all streams and return to Main Window
outFile.Close();
outFile.Dispose();
outFile = null;
aviStreamReader.Close();
aviStreamReader.Dispose();
aviStreamReader = null;
_saveFlag=false;
}
// write MOVI Chunk frames
private int writeMoviChunk(ref bool _redrawInfo,
ref string _saveError,
ref double _saveStatus,
ref bool _saveFlag,
ref double _totProgressItems,
ref string _saveInfo )
{
long tmpMoviPointer = 0;
int FourCC = 0;
int byteOfRead = 0;
int sizeOfFrame = 0;
int newByteOfRead = 0;
string sFourCC = "";
int frameCount = 0;
int lenOfFrame = 0;
string hexFourCC = "";
byte[] tmpByteArray = new byte[0];
int stepGuiUpdate = 1024;
int stepFrame = 0;
// Write data before MOVI chunk
_saveStatus = 0;
_totProgressItems = (double)_m_MoviSize;
_saveInfo = clsLanguages.EXPMESSAGE3;
_redrawInfo = true;
Console.WriteLine("Write MOVI START (" + _m_MoviStart.ToString() + " SIZE " + _m_MoviSize.ToString() + ")");
_m_MoviSizeNew = 0;
while (tmpMoviPointer < _m_MoviSize)
{
// Exit if Cancel button was pressed
if (_saveFlag == false)
return 1;
FourCC = readDWord();
hexFourCC = FourCC.ToString("X8");
sFourCC = Utility.clsEncoding.FromFourCC(FourCC);
byteOfRead = readDWord();
tmpMoviPointer += 8;
_m_MoviSizeNew += 8;
// Adjust bytes to read (no odd)
sizeOfFrame = byteOfRead;
if ((byteOfRead % 2) != 0 )
byteOfRead++;
tmpByteArray = new byte[byteOfRead];
aviStreamReader.Read(tmpByteArray, 0, byteOfRead);
stepFrame ++;
// Verify frame type
if (sFourCC.Substring(2,2) == "dc" || sFourCC.Substring(2,2) == "db")
{
//
tmpByteArray = processFrame(tmpByteArray, ref sizeOfFrame);
newByteOfRead = tmpByteArray.Length;
framesOffset[frameCount] = outFile.Position;
framesSize[frameCount] = (long)newByteOfRead;
outFile.Write(intToByteArray(FourCC),0, 4);
outFile.Write(intToByteArray(sizeOfFrame),0, 4);
outFile.Write(tmpByteArray, 0, newByteOfRead);
tmpMoviPointer += byteOfRead;
_m_MoviSizeNew += newByteOfRead;
}
else
{
framesOffset[frameCount] = outFile.Position;
framesSize[frameCount] = (long)byteOfRead;
outFile.Write(intToByteArray(FourCC),0, 4);
outFile.Write(intToByteArray(sizeOfFrame),0, 4);
outFile.Write(tmpByteArray, 0, byteOfRead);
tmpMoviPointer += byteOfRead;
_m_MoviSizeNew += byteOfRead;
}
frameCount ++;
if (stepFrame >= stepGuiUpdate)
{
// Update progressbar
_saveStatus = (double)tmpMoviPointer;
// myPBar.Text = Convert.ToInt32(myPBar.Fraction * 100).ToString("D3") + "%";
stepFrame = 0;
}
}
Console.WriteLine("Tot Frames: " + frameCount.ToString());
Console.WriteLine("Write MOVI END");
return 0;
}
// Extract UserData info from DC subarea
private byte[] processFrame(byte[] inByteArray, ref int frameLength)
{
clsEncoding myEnc = new clsEncoding();
byte[] tmpByteArray = null;
byte[] userdataOld = null;
byte[] outByteArray = null;
ASCIIEncoding TextEncoding = new ASCIIEncoding();
tmpByteArray = inByteArray;
userdataOld = TextEncoding.GetBytes(_udToChange);
int startPos = myEnc.compareBytesArray(tmpByteArray, userdataOld,0);
// int totalFrameBytes = 0;
int totalFrameBytes, newFrameLength;
if (startPos >= 0)
{
// totalFrameBytes = tmpByteArray.Length - _udToChange.Length + 12;
newFrameLength = (frameLength - _udToChange.Length) + 12;
// Padded to an even number of bytes but make sure the padding isn't included
// in the size written to the chunk header or index
totalFrameBytes = newFrameLength;
if ((totalFrameBytes % 2) != 0)
totalFrameBytes ++;
// frameLength = totalFrameBytes;
// frameLength = newFrameLength;
outByteArray = new byte[totalFrameBytes];
Array.Copy(tmpByteArray, outByteArray, startPos);
Array.Copy(TextEncoding.GetBytes("DivX999b000p"), 0, outByteArray, startPos, 12);
Array.Copy(tmpByteArray,
startPos + _udToChange.Length ,
outByteArray,
startPos + 12,
frameLength - (_udToChange.Length + startPos));
frameLength = newFrameLength;
}
else
outByteArray = tmpByteArray;
return outByteArray;
}
// Write new Idx1 Chunk
private int writeIdx1Chunk(ref bool _redrawInfo,
ref string _saveError,
ref double _saveStatus,
ref bool _saveFlag,
ref double _totProgressItems,
ref string _saveInfo )
{
long tmpIdxPointer = 0;
int FourCC = 0;
int byteOfRead = 0;
string sFourCC = "";
int frameCount = 0;
string hexFourCC = "";
byte[] tmpByteArray = new byte[16];
byte[] tmpDWordArray = new byte[4];
int stepGuiUpdate = 256;
int stepFrame = 0;
// Write data before MOVI chunk
_saveStatus = 0;
_totProgressItems = (double)_m_IdxSize;
_saveInfo = clsLanguages.EXPMESSAGE4;
_redrawInfo=true;
Console.WriteLine("Write IDX START (" + _m_IdxStart.ToString() + " SIZE " + _m_IdxSize.ToString() + ")");
while (tmpIdxPointer < _m_IdxSize)
{
// Exit if Cancel button was pressed
if (_saveFlag == false)
return 1;
aviStreamReader.Read(tmpByteArray,0,16);
// Offsets are relative to the start of the 'movi' list type
tmpDWordArray = intToByteArray((int)(framesOffset[frameCount] - (_m_MoviStart - 4)));
for (int j=0; j<4; j++)
tmpByteArray[8+j]=tmpDWordArray[j];
tmpDWordArray = intToByteArray((int)framesSize[frameCount]);
for (int j=0; j<4; j++)
tmpByteArray[12+j]=tmpDWordArray[j];
outFile.Write(tmpByteArray,0,16);
tmpIdxPointer += 16;
frameCount ++;
stepFrame++;
if (stepFrame >= stepGuiUpdate)
{
// Update progressbar
_saveStatus = (double)tmpIdxPointer;
// myPBar.Text = Convert.ToInt32(myPBar.Fraction * 100).ToString("D3") + "%";
stepFrame = 0;
}
}
Console.WriteLine("Tot Frames: " + frameCount.ToString());
Console.WriteLine("Write IDX END");
return 0;
}
/// <summary>
/// Read 2 bytes and return the value
/// </summary>
/// <returns></returns>
private int readWord()
{
int retValue;
int readBytes;
byte[] tmpBuffer = new byte[2];
readBytes = aviStreamReader.Read(tmpBuffer,0,2);
retValue = (tmpBuffer[0]) + (tmpBuffer[1]<<8);
return retValue;
}
// returns a byte array of length 4
private byte[] intToByteArray(int i)
{
byte[] dword = new byte[4];
dword[0] = (byte) (i & 0x00FF);
dword[1] = (byte) ((i >> 8) & 0x000000FF);
dword[2] = (byte) ((i >> 16) & 0x000000FF);
dword[3] = (byte) ((i >> 24) & 0x000000FF);
return dword;
}
/// <summary>
/// Read 4 bytes and return the value
/// </summary>
/// <returns></returns>
private int readDWord()
{
int retValue;
int readBytes;
byte[] tmpBuffer = new byte[4];
readBytes = aviStreamReader.Read(tmpBuffer,0,4);
retValue = tmpBuffer[0]+(tmpBuffer[1]<<8)+(tmpBuffer[2]<<16)+(tmpBuffer[3]<<24);
return retValue;
}
public void change4CC(string ASHval, string VSHval, long ASHpos, long VSHpos)
{
byte[] tmpASHarray = null;
byte[] tmpVSHarray = null;
FileStream updaterSW = new FileStream(_m_filename , FileMode.Open ,FileAccess.ReadWrite);
updaterSW.Seek(ASHpos, SeekOrigin.Current);
tmpASHarray = Utility.clsEncoding.ToFourCCByte(ASHval);
Console.WriteLine(tmpASHarray[0].ToString("X2") +
tmpASHarray[1].ToString("X2") +
tmpASHarray[2].ToString("X2") +
tmpASHarray[3].ToString("X2"));
updaterSW.Write(tmpASHarray, 0, 4);
updaterSW.Seek(VSHpos, SeekOrigin.Begin);
tmpVSHarray = Utility.clsEncoding.ToFourCCByte(VSHval);
Console.WriteLine(tmpVSHarray[0].ToString("X2") +
tmpVSHarray[1].ToString("X2") +
tmpVSHarray[2].ToString("X2") +
tmpVSHarray[3].ToString("X2"));
updaterSW.Write(tmpVSHarray, 0, 4);
updaterSW.Close();
updaterSW.Dispose();
updaterSW = null;
return;
}
private void GetVideoInformations () {
int k, l;
int iV=0;
int iA=0;
//double sizOfAudio=0;
double sizOfVideo=0;
double sizOfHeader=0;
double videoQuality=0;
double WdH=0;
int framePerSec=1;
int AverageVideoBitRate = 0;
string fccDesc = "";
string Frame_Size = "";
string Total_Time = "";
string Frame_Rate = "";
string Total_Frames = "";
string Video_Data_Rate = "";
string Packet_Bitstream = "Off";
Console.WriteLine(myAviStreamHeader.Length);
for (k=0; k < headerStreams.Length; k++ ) {
Console.WriteLine(k);
if(clsEncoding.FromFourCC(headerStreams[k].fccType)
!= "vids" )
continue;
long totalTime = 0;
fccDesc = clsEncoding.FromFourCC(headerStreams[k].fccHandler);
if (headerFile.dwMicroSecPerFrame > 0)
totalTime =(long)((long)headerFile.dwTotalFrames *
(long) headerFile.dwMicroSecPerFrame);
totalTime = (long)(totalTime / 1000000.0);
int hours = (int)(totalTime / 3600);
totalTime -= (long)(hours * 3600);
int mins = (int)(totalTime / 60);
totalTime -= (long)(mins * 60);
framePerSec = headerStreams[k].dwRate / headerStreams[k].dwScale;
WdH = videoStreams[0].biWidth;
WdH /= videoStreams[0].biHeight;
Frame_Size =
videoStreams[0].biWidth.ToString()
+ " x " +
videoStreams[0].biHeight.ToString();
Total_Time = String.Format("{0:00}:{1:00}:{2:00.00#} seconds", hours, mins, totalTime);
Frame_Rate = String.Format("{0:N2} Frames/Sec", (1000000.0 / headerFile.dwMicroSecPerFrame));
Total_Frames = String.Format("{0:G}", headerFile.dwTotalFrames );
Video_Data_Rate = String.Format("{0:N2} frames/Sec", framePerSec );
iV++;
}
sizOfHeader = headerFile.dwTotalFrames * 8 * (iA+1);
sizOfVideo = m_MoviSize - sizOfHeader - sizOfAudio;
Console.WriteLine("m_MoviSize = " + m_MoviSize.ToString());
Console.WriteLine("sizOfHeader = " + sizOfHeader.ToString());
Console.WriteLine("sizOfAudio = " + sizOfAudio.ToString());
Console.WriteLine("sizOfVideo = " + sizOfVideo.ToString());
Console.WriteLine("dwTotalFrames = " + headerFile.dwTotalFrames.ToString());
AverageVideoBitRate = (int)((sizOfVideo * framePerSec * 8) / (headerFile.dwTotalFrames * 1000));
videoQuality = (0.75 * WdH) * (AverageVideoBitRate / framePerSec);
if (pbDetect == true)
Packet_Bitstream = "On";
_VideoItems.Add(new clsInfoItem("Video codec:", clsEncoding.FromFourCC(videoStreams[0].biCompression)) );
_VideoItems.Add(new clsInfoItem("Codec descr:", fccDesc));
_VideoItems.Add(new clsInfoItem("Frame Size:", Frame_Size) );
_VideoItems.Add(new clsInfoItem("Average Video Bitrate:", AverageVideoBitRate.ToString() + " Kb/Sec"));
_VideoItems.Add(new clsInfoItem("Avi file size:", ((m_filesize / 1024).ToString("#,### KB"))));
_VideoItems.Add(new clsInfoItem("Total Time:", Total_Time ));
_VideoItems.Add(new clsInfoItem("Frame Rate:", Frame_Rate)) ;
_VideoItems.Add(new clsInfoItem("Total Frames:", Total_Frames));
_VideoItems.Add(new clsInfoItem("Video Data Rate:", Video_Data_Rate ));
_VideoItems.Add(new clsInfoItem("Video Quality:", videoQuality.ToString("#,###.##") ));
_VideoItems.Add(new clsInfoItem("Packet Bitstream:", Packet_Bitstream ));
if (userData.Length >0 )
for (l=0; l<userData.Length;l++)
_VideoItems.Add(new clsInfoItem("Info Data[" + l + "]:", userData[l].ToString()));
if (MOVIuserData.Length >0 )
{
for (l=0; l<MOVIuserData.Length;l++)
{
_VideoItems.Add(new clsInfoItem("User Data[" + l + "]:", MOVIuserData[l].ToString()));
if (MOVIuserData[l].IndexOf("DivX") == 0 && MOVIuserData[l] != "DivX999b000p")
this._udToChange = MOVIuserData[l];
}
}
return;
}
private void GetAudioInformations () {
int k;
int iA=0;
int blockPerSec=0;
sizOfAudio=0;
for (k=0; k < headerStreams.Length; k++ ) {
if(clsEncoding.FromFourCC(headerStreams[k].fccType)
== "vids" )
continue;
string aFormat = audioStreams[iA].wFormatTag.ToString("X4");
string CVBR = "";
double audioRate = (8.0 * audioStreams[iA].nAvgBytesPerSec) ;
//if (headerStreams[k].dwSampleSize > 0 )
// audioRate /= (double) headerStreams[k].dwSampleSize;
if(aFormat == "0055") {
CVBR = "";
// MP3 CODEC
_AudioItems.Add(new clsInfoItem("Audio " +
(iA+1).ToString() +
":",
parseAudioType(aFormat)
+ " " + CVBR + " " +
String.Format("{0:N2} Kb/Sec",
audioRate / 1000.0) + " " +
"- " + audioStreams[iA].nSamplesPerSec + " Hz (" +
audioStreams[iA].nChannels.ToString() + " Channels)"));
} else {
// Other codec
_AudioItems.Add(new clsInfoItem("Audio " + (iA+1).ToString() + ":",
parseAudioType(aFormat) + " " +
String.Format("{0:N2} Kb/Sec", audioRate / 1000.0) + " " +
"- " + audioStreams[iA].nSamplesPerSec + " Hz (" +
audioStreams[iA].nChannels.ToString() + " Channels)"));
}
// Calc Data for AVBitrate
blockPerSec = headerStreams[k].dwRate / headerStreams[k].dwScale;
double tmpAudio = headerStreams[k].dwLength;
tmpAudio *= audioStreams[iA].nAvgBytesPerSec;
tmpAudio /= blockPerSec;
sizOfAudio += tmpAudio;
// increment total audio streams
iA++;
}
return;
}
}
}
***************************************************************************************
/*
* @(#)AviParser.java 1.39 01/03/16
*
* Copyright (c) 1996-2001 Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
package com.sun.media.parser.video;
import java.io.IOException;
import javax.media.Track;
import javax.media.Time;
import javax.media.Duration;
import javax.media.IncompatibleSourceException;
import javax.media.BadHeaderException;
import javax.media.TrackListener;
import javax.media.Buffer;
import javax.media.protocol.DataSource;
import javax.media.protocol.SourceStream;
import javax.media.protocol.PullSourceStream;
import javax.media.protocol.Seekable;
import javax.media.Format;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.CachedStream;
import javax.media.format.AudioFormat;
import com.sun.media.format.WavAudioFormat;
import javax.media.format.VideoFormat;
import com.sun.media.parser.BasicPullParser;
import com.sun.media.vfw.BitMapInfo;
public class AviParser extends BasicPullParser {
private static ContentDescriptor[] supportedFormat =
new ContentDescriptor[] {new ContentDescriptor("video.x_msvideo")};
private PullSourceStream stream = null;
private CachedStream cacheStream;
private Track[] tracks;
private Seekable seekableStream;
private int numSupportedTracks = 0;
private int length;
private int audioTrack = -1;
private int videoTrack = -1;
private int keyFrameTrack = -1;
private final static int SIZE_OF_AVI_INDEX = 16;
private final static int AVIH_HEADER_LENGTH = 56;
private final static int STRH_HEADER_LENGTH = 56;
private final static int STRF_VIDEO_HEADER_LENGTH = 40;
private final static int STRF_AUDIO_HEADER_LENGTH = 16;
final static int AVIF_HASINDEX = 0x00000010;
final static int AVIF_MUSTUSEINDEX = 0x00000020;
final static int AVIF_ISINTERLEAVED = 0x00000100;
final static int AVIF_WASCAPTUREFILE = 0x00010000;
final static int AVIF_COPYRIGHTED = 0x00020000;
final static int AVIF_KEYFRAME = 0x00000010;
final static String AUDIO = "auds";
final static String VIDEO = "vids";
final static String LISTRECORDCHUNK = "rec ";
final static String VIDEO_MAGIC = "dc"; // Video
final static String VIDEO_MAGIC_JPEG = "db"; // Video
final static String VIDEO_MAGIC_IV32a = "iv"; // Indeo 3.2
final static String VIDEO_MAGIC_IV32b = "32"; // Indeo 3.2
final static String VIDEO_MAGIC_IV31 = "31"; // Indeo 3.1
final static String VIDEO_MAGIC_CVID = "id"; // Cinepak
final static String AUDIO_MAGIC = "wb"; // Audio
private int usecPerFrame = 0;
private long nanoSecPerFrame = 0;
private int maxBytesPerSecond;
private int paddingGranularity;
private int flags;
private int totalFrames = 0;
private int initialFrames;
private int numTracks = 0;
private int suggestedBufferSize;
private int width;
private int height;
private TrakList[] trakList;
private int idx1MinimumChunkOffset;
private int moviOffset = 0;
private Time duration = Duration.DURATION_UNKNOWN;
private boolean moviChunkSeen = false;
private boolean idx1ChunkSeen = false;
private int maxAudioChunkIndex = 0;
private int maxVideoChunkIndex = 0;
private int extraHeaderLength = 0;
private byte[] codecSpecificHeader = null;
// Used to make the seek and the subsequent readBytes call atomic
// operations, so that the video and audio track
// threads don't trample each other.
private Object seekSync = new Object();
/**
* Avi format requires that the stream be seekable and
* random accessible.
*/
protected boolean supports(SourceStream[] streams) {
return seekable;
}
public void setSource(DataSource source)
throws IOException, IncompatibleSourceException {
super.setSource(source);
stream = (PullSourceStream) streams[0];
seekableStream = (Seekable) streams[0];
}
public ContentDescriptor [] getSupportedInputContentDescriptors() {
return supportedFormat;
}
public Track[] getTracks() throws IOException, BadHeaderException {
if (tracks != null)
return tracks;
if (seekableStream == null) {
return new Track[0];
}
readHeader();
if (!moviChunkSeen) {
throw new BadHeaderException("No movi chunk");
}
if (!idx1ChunkSeen) {
throw new BadHeaderException("Currently files with no idx1 chunk are not supported");
}
if (numTracks <= 0) {
throw new BadHeaderException("Error parsing header");
}
tracks = new Track[numTracks];
for (int i = 0; i < tracks.length; i++) {
TrakList trakInfo = trakList[i];
if (trakInfo.trackType.equals(AUDIO)) {
tracks[i] = new AudioTrack(trakInfo);
} else if (trakInfo.trackType.equals(VIDEO)) {
tracks[i] = new VideoTrack(trakInfo);
}
}
return tracks;
}
private void readHeader()
throws IOException, BadHeaderException {
String magicRIFF = readString(stream);
if (!(magicRIFF.equals("RIFF"))) {
throw new BadHeaderException("AVI Parser: expected string RIFF, got "
+ magicRIFF);
}
length = readInt(stream, /* bigEndian = */ false);
length += 8; // For RIFF and AVI
String magicAVI = readString(stream);
if (!(magicAVI.equals("AVI "))) {
throw new BadHeaderException("AVI Parser: expected string AVI, got "
+ magicAVI);
}
int currentTrack = 0;
while (getLocation(stream) <= (length-12)) {
String next = readString(stream);
int subchunkLength = readInt(stream, /* bigEndian = */ false);
if (next.equals("LIST")) {
String subchunk = readString(stream);
if (subchunk.equals("hdrl")) {
parseHDRL();
} else if (subchunk.equals("strl")) {
parseSTRL(subchunkLength, currentTrack);
currentTrack++;
} else if (subchunk.equals("movi"))
parseMOVI(subchunkLength - 4);
else {
// System.err.println("Unsupported subchunk " + subchunk +
// " in LIST");
skip(stream, subchunkLength-4);
}
} else if (next.equals("idx1")) {
parseIDX1(subchunkLength);
} else {
skip(stream, subchunkLength);
if ( (subchunkLength & 1) > 0)
skip(stream, 1);
}
}
if ( (totalFrames != 0) && (usecPerFrame != 0) ) {
duration = new Time((long) usecPerFrame * totalFrames * 1000);
}
}
// The inner class can use this method as they cannot use
// the getLocation(stream) method.
private long getLocation() {
return getLocation(stream);
}
private void parseHDRL() throws BadHeaderException {
try {
String next = readString(stream);
if (!next.equals("avih")) {
throw new BadHeaderException("AVI Parser: expected string AVIH, got "
+ next);
}
int headerLength = readInt(stream, /* bigEndian = */ false);
parseAVIH(headerLength);
trakList = new TrakList[numTracks];
} catch (IOException e) {
throw new BadHeaderException("IOException when parsing hdrl");
}
}
private void parseSTRL(int length, int currentTrack) throws BadHeaderException {
try {
if (currentTrack >= trakList.length ) {
throw new BadHeaderException("inconsistent number of strl atoms");
}
length -= 12; // for "LIST <length> strl"
while (length >= 12) { // TODO: check
String subchunkid = readString(stream);
int subchunkLength = readInt(stream, /* bigEndian = */ false);
if (subchunkid.equals("strh")) {
parseSTRH(subchunkLength, currentTrack);
} else if (subchunkid.equals("strf")) {
if (trakList[currentTrack] == null) {
throw new BadHeaderException("strf doesn't have a strh atom preceding it");
}
parseSTRF(subchunkLength, currentTrack);
} else {
// System.err.println("Unsupported subchunk " + subchunkid +
// " in strl. length " + subchunkLength);
if ( (subchunkLength & 1) > 0) {
// Some avi files like billy.avi are don't have strn
// chunks with incorrect odd number for the length.
// The actual offset is 1 more. If this correction
// is not made all the remaining chunks will be read
// incorrectly.
subchunkLength++;
}
skip(stream, subchunkLength);
}
length -= (subchunkLength + 4); // 4 is for subchunkid
}
} catch (IOException e) {
throw new BadHeaderException("IOException when parsing hdrl");
}
}
private void parseSTRH(int length, int currentTrack) throws BadHeaderException {
try {
if (length < STRH_HEADER_LENGTH) {
throw new BadHeaderException("strh: header length should be atleast " +
STRH_HEADER_LENGTH + " but is " +
length);
}
trakList[currentTrack] = new TrakList();
trakList[currentTrack].trackType = readString(stream);
trakList[currentTrack].streamHandler = readString(stream);
trakList[currentTrack].flags = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].priority = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].initialFrames = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].scale = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].rate = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].start = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].length = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].suggestedBufferSize = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].quality = readInt(stream, /* bigEndian = */ false);
trakList[currentTrack].sampleSize = readInt(stream, /* bigEndian = */ false);
skip(stream, 8); // Padding
if ( (length - STRH_HEADER_LENGTH) > 0)
skip(stream, length - STRH_HEADER_LENGTH);
} catch (IOException e) {
throw new BadHeaderException("IOException when parsing hdrl");
}
}
private void parseSTRF(int length, int currentTrack) throws BadHeaderException {
try {
String trackType = trakList[currentTrack].trackType;
if (trackType.equals(VIDEO)) {
Video video = new Video();
video.size = readInt(stream, /* bigEndian = */ false);
video.width = readInt(stream, /* bigEndian = */ false);
video.height = readInt(stream, /* bigEndian = */ false);
video.planes = readShort(stream, /* bigEndian = */ false);
video.depth = readShort(stream, /* bigEndian = */ false);
// Instead of readString, read the four bytes to see
// if its a raw format.
byte [] intArray = new byte[4];
readBytes(stream, intArray, 4);
if (intArray[0] > 32) {
video.compressor = new String(intArray);
} else {
switch (intArray[0]) {
case 0:
video.compressor = VideoFormat.RGB;
break;
case 1:
video.compressor = "rle8";
break;
case 2:
video.compressor = "rle4";
break;
case 3:
video.compressor = VideoFormat.RGB;
break;
}
}
// Get the BITMAPINFO data needed by the decompressor
BitMapInfo bmi = new BitMapInfo();
bmi.biWidth = video.width;
bmi.biHeight = video.height;
bmi.biPlanes = video.planes;
bmi.biBitCount = video.depth;
bmi.fourcc = new String(video.compressor);
video.bitMapInfo = bmi;
bmi.biSizeImage = readInt(stream, false);
bmi.biXPelsPerMeter = readInt(stream, false);
bmi.biYPelsPerMeter = readInt(stream, false);
bmi.biClrUsed = readInt(stream, false);
bmi.biClrImportant = readInt(stream, false);
if ( (length - STRF_VIDEO_HEADER_LENGTH) > 0) {
bmi.extraSize = (length - STRF_VIDEO_HEADER_LENGTH);
bmi.extraBytes = new byte[bmi.extraSize];
readBytes(stream, bmi.extraBytes, bmi.extraSize);
}
trakList[currentTrack].media = video;
trakList[currentTrack].media.maxSampleSize =
trakList[currentTrack].suggestedBufferSize;
videoTrack = currentTrack;
} else if (trackType.equals(AUDIO)) {
Audio audio = new Audio();
audio.formatTag = readShort(stream, /* bigEndian = */ false);
audio.channels = readShort(stream, /* bigEndian = */ false);
audio.sampleRate = readInt(stream, /* bigEndian = */ false);
audio.avgBytesPerSec = readInt(stream, /* bigEndian = */ false);
audio.blockAlign = readShort(stream, /* bigEndian = */ false);
audio.bitsPerSample = readShort(stream, /* bigEndian = */ false);
int remainingFormatSize = length - STRF_AUDIO_HEADER_LENGTH;
codecSpecificHeader = null;
int extraFieldsSize = 0;
if (remainingFormatSize >= 2) {
extraFieldsSize = readShort(stream, /* bigEndian = */ false);
remainingFormatSize -= 2;
if (extraFieldsSize > 0) {
codecSpecificHeader = new byte[extraFieldsSize];
readBytes(stream, codecSpecificHeader, codecSpecificHeader.length);
remainingFormatSize -= extraFieldsSize;
}
// TODO: do other encodings provide samplesPerBlock?
// Note that this info is there in codecSpecificHeader
if ( (audio.formatTag == WavAudioFormat.WAVE_FORMAT_ADPCM) ||
(audio.formatTag == WavAudioFormat.WAVE_FORMAT_DVI_ADPCM) ||
(audio.formatTag == WavAudioFormat.WAVE_FORMAT_GSM610) ) {
if (extraFieldsSize < 2) {
throw new
BadHeaderException("samplesPerBlock field not available for encoding" + audio.formatTag);
}
audio.samplesPerBlock = BasicPullParser.parseShortFromArray(codecSpecificHeader,
/* bigEndian = */ false);
}
}
if (remainingFormatSize < 0) {
throw new BadHeaderException("Avi Parser: incorrect headersize in the STRF");
}
if ( remainingFormatSize > 0)
skip(stream, length - STRF_AUDIO_HEADER_LENGTH);
trakList[currentTrack].media = audio;
audioTrack = currentTrack;
} else {
throw new BadHeaderException("strf: unsupported stream type " + trackType);
}
} catch (IOException e) {
throw new BadHeaderException("IOException when parsing hdrl");
}
}
private void parseAVIH(int length) throws BadHeaderException {
try {
if (length < AVIH_HEADER_LENGTH) {
throw new BadHeaderException("avih: header size is not 56");
}
usecPerFrame = readInt(stream, /* bigEndian = */ false);
nanoSecPerFrame = usecPerFrame * 1000;
maxBytesPerSecond = readInt(stream, /* bigEndian = */ false);
paddingGranularity = readInt(stream, /* bigEndian = */ false);
flags = readInt(stream, /* bigEndian = */ false);
totalFrames = readInt(stream, /* bigEndian = */ false);
initialFrames = readInt(stream, /* bigEndian = */ false);
numTracks = readInt(stream, /* bigEndian = */ false);
suggestedBufferSize = readInt(stream, /* bigEndian = */ false);
width = readInt(stream, /* bigEndian = */ false);
height = readInt(stream, /* bigEndian = */ false);
skip(stream, 4*4); // int reserved[4]
if ( (length - AVIH_HEADER_LENGTH) > 0)
skip(stream, length - AVIH_HEADER_LENGTH);
} catch (IOException e) {
throw new BadHeaderException("IOException when parsing hdrl");
}
}
private void parseIDX1(int length) throws BadHeaderException {
try {
if (!moviChunkSeen) {
throw new BadHeaderException("idx1 chunk appears before movi chunk");
}
// TODO: check for valid length value
int numIndices = (length / SIZE_OF_AVI_INDEX);
String id;
int flag;
int chunkOffset;
int chunkLength;
for (int i = 0; i < numTracks; i++) {
if (trakList[i] == null) {
throw new BadHeaderException("Bad file format");
}
trakList[i].chunkInfo = new AVIIndexEntry[numIndices];
if (trakList[i].trackType.equals(VIDEO)) {
trakList[i].keyFrames = new int[numIndices];
}
}
idx1MinimumChunkOffset = Integer.MAX_VALUE;
for (int i = 0; i < numIndices; i++) {
id = readString(stream);
if (id.equals(LISTRECORDCHUNK)) {
// $$$ DISCARD for now
readInt(stream, /* bigEndian = */ false);
readInt(stream, /* bigEndian = */ false);
readInt(stream, /* bigEndian = */ false);
continue;
}
int streamNumber;
try {
streamNumber = Integer.parseInt(id.substring(0,2));
} catch (NumberFormatException e) {
// DISCARD chunk at it doesn't represent a stream
readInt(stream, /* bigEndian = */ false);
readInt(stream, /* bigEndian = */ false);
readInt(stream, /* bigEndian = */ false);
continue;
}
if ( (streamNumber < 0) || (streamNumber >= numTracks) ) {
throw new BadHeaderException("index chunk has illegal stream # " +
streamNumber);
}
flag = readInt(stream, /* bigEndian = */ false);
chunkOffset = readInt(stream, /* bigEndian = */ false);
chunkLength = readInt(stream, /* bigEndian = */ false);
AVIIndexEntry[] chunkInfo = trakList[streamNumber].chunkInfo;
int index = trakList[streamNumber].maxChunkIndex;
chunkInfo[index] = new AVIIndexEntry();
chunkInfo[index].id = id;
chunkInfo[index].flag = flag;
chunkInfo[index].chunkOffset = chunkOffset;
chunkInfo[index].chunkLength = chunkLength;
if (trakList[streamNumber].trackType.equals(AUDIO)) {
int c = trakList[streamNumber].tmpCumulativeChunkLength += chunkLength;
chunkInfo[index].cumulativeChunkLength = c;
}
if (trakList[streamNumber].trackType.equals(VIDEO)) {
if ( (flag & AVIF_KEYFRAME) > 0 ) {
int keyFrameIndex = trakList[streamNumber].numKeyFrames;
trakList[streamNumber].keyFrames[keyFrameIndex] = index;
trakList[streamNumber].numKeyFrames++;
}
}
trakList[streamNumber].maxChunkIndex++;
if (chunkOffset < idx1MinimumChunkOffset) {
idx1MinimumChunkOffset = chunkOffset;
}
}
// For video tracks, if all the frames are not key frames,
// build the indexToKeyframeIndex table
// which maps a video frame to a key frame.
for (int i = 0; i < numTracks; i++) {
if (trakList[i].trackType.equals(VIDEO)) {
int numKeyFrames = trakList[i].numKeyFrames;
if (numKeyFrames > 0)
keyFrameTrack = i;
int maxChunkIndex = trakList[i].maxChunkIndex;
if ( (numKeyFrames > 0) && (numKeyFrames < maxChunkIndex) ) {
trakList[i].indexToKeyframeIndex =
buildIndexToKeyFrameIndexTable(trakList[i].keyFrames,
numKeyFrames,
maxChunkIndex);
}
trakList[i].keyFrames = null;
}
}
if (idx1MinimumChunkOffset >= moviOffset) {
// idx1 chunk offsets refer to start of the file.
moviOffset = 0;
}
moviOffset += 8; // for chunk id and size
} catch (IOException e) {
throw new BadHeaderException("IOException when parsing IDX1");
}
idx1ChunkSeen = true;
}
private void parseMOVI(int length) throws BadHeaderException {
try {
moviChunkSeen = true;
if ( (flags & AVIF_HASINDEX) > 0) {
// Subtract 4 to include MOVI string
moviOffset = (int) getLocation(stream) - 4;
skip(stream, length);
} else {
// System.out.println("parseMOVI: NO AVIF_HASINDEX"); // REMOVE
}
} catch (IOException e) {
throw new BadHeaderException("IOException when parsing movi");
}
}
public Time setPosition(Time where, int rounding) {
int keyframeNum = -1;
if ( (keyFrameTrack != -1) && (tracks[keyFrameTrack].isEnabled()) ) {
// keyframe track present and is enabled
TrakList trakInfo = trakList[keyFrameTrack];
Track track = tracks[keyFrameTrack];
int frameNum = track.mapTimeToFrame(where);
keyframeNum = frameNum;
// TODO: handle FRAME_UNKNOWN
if (trakInfo.indexToKeyframeIndex.length > frameNum) {
keyframeNum = trakInfo.indexToKeyframeIndex[frameNum];
}
if (keyframeNum != frameNum) {
where = track.mapFrameToTime(keyframeNum);
}
}
for (int i = 0; i < numTracks; i++) {
if (!tracks[i].isEnabled())
continue;
int chunkNumber =0;
int offsetWithinChunk = 0;
try {
if (i == keyFrameTrack) {
chunkNumber = keyframeNum;
continue;
}
TrakList trakInfo = trakList[i];
if (trakInfo.trackType.equals("vids")) {
if (usecPerFrame != 0) {
chunkNumber = (int) (where.getNanoseconds() / nanoSecPerFrame);
if (chunkNumber < 0)
chunkNumber = 0;
else if (chunkNumber >= trakInfo.maxChunkIndex) {
continue; // EOM
}
}
} else if (trakInfo.trackType.equals("auds")) {
int bytePos = (int) ( where.getSeconds() *
((Audio) trakInfo.media).avgBytesPerSec);
if (bytePos < 0)
bytePos = 0;
// Note: the else statement can also handle the if
// case, ie maxChunkIndex == 1, but is separated here
// for clarity and a slight efficiency.
if (trakInfo.maxChunkIndex == 1) {
if (bytePos >= trakInfo.chunkInfo[0].chunkLength) {
chunkNumber = trakInfo.maxChunkIndex; // EOM
continue; // EOM
}
chunkNumber = 0;
offsetWithinChunk = bytePos;
} else {
int approx;
chunkNumber = trakInfo.getChunkNumber(bytePos);
if (chunkNumber >= trakInfo.maxChunkIndex)
continue; // EOM
approx = trakInfo.chunkInfo[chunkNumber].cumulativeChunkLength -
trakInfo.chunkInfo[chunkNumber].chunkLength;
offsetWithinChunk = bytePos - approx;
}
if ( (offsetWithinChunk & 1) > 0)
offsetWithinChunk--;
int blockAlign = ((Audio) trakInfo.media).blockAlign;
if (blockAlign != 0) {
offsetWithinChunk -= (offsetWithinChunk % blockAlign);
}
}
} finally {
((MediaTrack)tracks[i]).setChunkNumberAndOffset(chunkNumber,
offsetWithinChunk);
}
}
return where;
}
public Time getMediaTime() {
return null; // TODO
}
public Time getDuration() {
return duration;
}
/**
* Returns a descriptive name for the plug-in.
* This is a user readable string.
*/
public String getName() {
return "Parser for avi file format";
}
private boolean isSupported(String trackType) {
return ( trackType.equals(VIDEO) || trackType.equals(AUDIO) );
}
private int[] buildIndexToKeyFrameIndexTable(int[] syncSamples,
int numKeyFrames,
int numberOfSamples) {
int[] syncSampleMapping = new int[numberOfSamples];
int index = 0;
int previous;
if (syncSamples[0] != 0) {
// Bug in the sync table of the avi file
// The first sample should always be a key frame
previous = syncSampleMapping[0] = 0;
} else {
previous = syncSampleMapping[0] = 0;
index++;
}
for (; index < numKeyFrames; index++) {
int next = syncSamples[index];
for (int j = previous+1; j < next; j++) {
syncSampleMapping[j] = previous;
}
syncSampleMapping[next] = next;
previous = next;
}
int lastSyncFrame = syncSamples[numKeyFrames - 1];
for (index = lastSyncFrame+1; index < numberOfSamples; index++) {
syncSampleMapping[index] = lastSyncFrame;
}
return syncSampleMapping;
}
private abstract class Media {
int maxSampleSize;
abstract Format createFormat();
}
private class Audio extends Media {
int formatTag;
int channels;
int sampleRate;
int avgBytesPerSec;
int blockAlign;
int bitsPerSample;
int samplesPerBlock;
AudioFormat format = null;
Format createFormat() {
if (format != null)
return format;
String encodingString = (String)
WavAudioFormat.formatMapper.get(new Integer(formatTag));
if (encodingString == null) {
encodingString = "unknown";
}
boolean signed;
if (bitsPerSample > 8)
signed = true;
else
signed = false;
// TODO: If possible create WavAudioFormat only when necessary otherwise
// create AudioFormat
format = new WavAudioFormat(encodingString,
sampleRate,
bitsPerSample,
channels,
/*frameSizeInBits=*/blockAlign * 8,
avgBytesPerSec,
AudioFormat.LITTLE_ENDIAN,
signed ? AudioFormat.SIGNED : AudioFormat.UNSIGNED,
Format.NOT_SPECIFIED, // No FRAME_RATE specified
Format.byteArray,
codecSpecificHeader);
return format;
}
public String toString() {
System.out.println("Audio Media: " + format);
System.out.println("Number of channels " + channels);
System.out.println("average bytes per second " + avgBytesPerSec);
System.out.println("sampleRate " + sampleRate);
System.out.println("blockAlign " + blockAlign);
System.out.println("bitsPerSample " + bitsPerSample);
System.out.println("formatTag " + formatTag);
return super.toString();
}
}
private class Video extends Media {
int size;
int width;
int height;
int planes;
int depth;
String compressor;
VideoFormat format = null;
BitMapInfo bitMapInfo = null;
Format createFormat() {
if (format != null)
return format;
if (usecPerFrame != 0) {
format = bitMapInfo.createVideoFormat(Format.byteArray,
(float) ((1.0/usecPerFrame)*1000000));
} else {
format = bitMapInfo.createVideoFormat(Format.byteArray);
}
return format;
}
public String toString() {
System.out.println("size is " + size);
System.out.println("width is " + width);
System.out.println("height is " + height);
System.out.println("planes is " + planes);
System.out.println("depth is " + depth);
System.out.println("compressor is " + compressor);
return super.toString();
}
}
private class TrakList {
Time duration = Duration.DURATION_UNKNOWN;
String trackType; // Chunk identifier
String streamHandler; // Device handler identifier
int flags; // Data Parameters
int priority; // Set to 0
int initialFrames; // Number of initial audio frames
int scale; // Unit used to measure time
int rate; // Data rate of playback
int start; // Starting time of AVI data
int length; // Size of AVI data chunk
int suggestedBufferSize; // Minimum playback buffer size
int quality; // Sample quality factor
int sampleSize; // Size of the sample in bytes
Media media; // Info specific to each track type
// From the implementation
// Can be used as a debugging aid to disable a track
boolean supported = true; // Is this track type supported
AVIIndexEntry[] chunkInfo = new AVIIndexEntry[0];
int maxChunkIndex = 0;
int[] indexToKeyframeIndex = new int[0];
int[] keyFrames = new int[0];
// boolean allKeyFrames = false;
int numKeyFrames = 0;
int tmpCumulativeChunkLength = 0;
// TODO: speedup: use binary search
int getChunkNumber(int offset) {
for (int i = 0; i < maxChunkIndex; i++) {
if (offset < chunkInfo[i].cumulativeChunkLength) {
return i;
}
}
return maxChunkIndex; // EOM
}
}
// TODO extend BasicTrack if possible
private abstract class MediaTrack implements Track {
protected TrakList trakInfo;
private boolean enabled = true;
private int numBuffers = 4; // TODO: check
private Format format;
private long sequenceNumber = 0;
private int chunkNumber = 0;
protected int useChunkNumber = 0;
protected int offsetWithinChunk = -1;
protected int useOffsetWithinChunk = 0;
private AviParser parser = AviParser.this;
private AVIIndexEntry indexEntry;
private Object header = null;
private TrackListener listener;
MediaTrack(TrakList trakInfo) {
this.trakInfo = trakInfo;
format = trakInfo.media.createFormat();
}
public void setTrackListener(TrackListener l) {
listener = l;
}
public Format getFormat() {
return format;
}
public void setEnabled(boolean t) {
enabled = t;
}
public boolean isEnabled() {
return enabled;
}
public Time getDuration() {
return trakInfo.duration;
}
public Time getStartTime() {
return new Time(0); // TODO
}
synchronized void setChunkNumberAndOffset(int number, int offset) {
chunkNumber = number;
offsetWithinChunk = offset;
}
public void readFrame(Buffer buffer) {
if (buffer == null)
return;
if (!enabled) {
buffer.setDiscard(true);
return;
}
synchronized (this) {
if (offsetWithinChunk == -1) {
useOffsetWithinChunk = 0;
} else {
useOffsetWithinChunk = offsetWithinChunk;
offsetWithinChunk = -1; // Reset offsetWithinChunk
}
useChunkNumber = chunkNumber;
}
// TODO: handle chunkNumber < 0 case differently
if ( (useChunkNumber >= trakInfo.maxChunkIndex) ||
(useChunkNumber < 0 ) ) {
buffer.setLength(0);
buffer.setEOM(true);
return;
}
buffer.setFormat(format);
indexEntry = trakInfo.chunkInfo[useChunkNumber];
int chunkLength = indexEntry.chunkLength;
Object obj = buffer.getData();
byte[] data;
buffer.setHeader(new Integer(indexEntry.flag));
if ( (obj == null) ||
(! (obj instanceof byte[]) ) ||
( ((byte[])obj).length < chunkLength) ) {
data = new byte[chunkLength];
buffer.setData(data);
} else {
data = (byte[]) obj;
}
try {
int actualBytesRead;
synchronized (seekSync) {
seekableStream.seek(indexEntry.chunkOffset
+ moviOffset +
useOffsetWithinChunk);
actualBytesRead = parser.readBytes(stream, data,
chunkLength - useOffsetWithinChunk);
offsetWithinChunk = 0;
buffer.setTimeStamp(getTimeStamp());
}
buffer.setLength(actualBytesRead);
long frameDuration = Buffer.TIME_UNKNOWN;
if (trakInfo.trackType.equals(VIDEO)) {
if (nanoSecPerFrame > 0)
frameDuration = nanoSecPerFrame;
if (
// All Frames are key frames
(trakInfo.indexToKeyframeIndex.length == 0) ||
// or current frame is a key frame
(useChunkNumber == trakInfo.indexToKeyframeIndex[useChunkNumber])
) {
buffer.setFlags(buffer.getFlags() | Buffer.FLAG_KEY_FRAME);
}
}
buffer.setDuration(frameDuration);
buffer.setSequenceNumber(++sequenceNumber);
} catch (IOException e) {
buffer.setLength(0);
buffer.setEOM(true);
}
synchronized(this) {
if (chunkNumber == useChunkNumber) // Not changed by setPosition()
chunkNumber++;
}
}
abstract void doReadFrame(Buffer buffer);
public int mapTimeToFrame(Time t) {
return FRAME_UNKNOWN;
}
public Time mapFrameToTime(int frameNumber) {
return TIME_UNKNOWN;
}
abstract long getTimeStamp();
}
private class AudioTrack extends MediaTrack {
int channels;
int avgBytesPerSec;
AVIIndexEntry[] chunkInfo;
AudioTrack(TrakList trakInfo) {
super(trakInfo);
channels = ((Audio)trakInfo.media).channels;
avgBytesPerSec = ((Audio) trakInfo.media).avgBytesPerSec;
chunkInfo = trakInfo.chunkInfo;
}
void doReadFrame(Buffer buffer) {
}
long getTimeStamp() {
if (avgBytesPerSec > 0) {
long bytes = useOffsetWithinChunk;
if (useChunkNumber > 0) {
bytes += chunkInfo[useChunkNumber - 1].cumulativeChunkLength;
}
return (long) (((float) bytes / avgBytesPerSec) * 1E9);
} else {
return 0;
}
}
}
private class VideoTrack extends MediaTrack {
int needBufferSize;
boolean variableSampleSize = true;
VideoTrack(TrakList trakInfo) {
super(trakInfo);
}
void doReadFrame(Buffer buffer) {
}
long getTimeStamp() {
return (useChunkNumber * usecPerFrame * 1000L);
}
public int mapTimeToFrame(Time t) {
if (nanoSecPerFrame <= 0)
return FRAME_UNKNOWN;
if (t.getNanoseconds() < 0)
return FRAME_UNKNOWN;
int chunkNumber;
chunkNumber = (int) (t.getNanoseconds() / nanoSecPerFrame);
if (chunkNumber >= trakInfo.maxChunkIndex)
return trakInfo.maxChunkIndex - 1;
return chunkNumber;
}
public Time mapFrameToTime(int frameNumber) {
if ( (frameNumber < 0) || (frameNumber >= trakInfo.maxChunkIndex) )
return TIME_UNKNOWN;
long time = frameNumber * nanoSecPerFrame;
return new Time(time);
}
}
// An Index Chunk has the identifier idx1 and must appear after hdrl and
// movi chunks. This chunk contains a list of all chunks within the AVI
// chunk, along with their locations, and is used for random access of audio
// and video data.
//
private class AVIIndexEntry {
public String id; // Chunk identifier reference
public int flag; // Type of Chunk referenced
public int chunkOffset; // Position of Chunk in file
public int chunkLength; // Length of chunk in bytes
// Currently only audio track uses cumulativeChunkLength
public int cumulativeChunkLength = 0; // Derived data
}
}
#include <fstream.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
/*RIFF ('AVI '
LIST ('hdrl'
'avih'(<Main AVI Header>)
LIST ('strl'
'strh'(<Stream header>)
'strf'(<Stream format>)
'strd'(additional header data)
.
.
.
)
.
.
.
)
LIST ('movi'
{SubChunk | LIST('rec '
SubChunk1
SubChunk2
.
.
.
)
.
.
.
}
.
.
.
)
['idx1'<AVIIndex>]
)*/
typedef struct { //The Main AVI Header
DWORD dwMicroSecPerFrame;
DWORD dwMaxBytesPerSec;
DWORD dwReserved1;
DWORD dwFlags;
DWORD dwTotalFrames;
DWORD dwInitialFrames;
DWORD dwStreams;
DWORD dwSuggestedBufferSize;
DWORD dwWidth;
DWORD dwHeight;
DWORD dwScale;
DWORD dwRate;
DWORD dwStart;
DWORD dwLength;
} MainAVIHeader;
typedef struct { //AVI Stream Header
char fccType[4];
char fccHandler[4];
DWORD dwFlags;
DWORD dwReserved1;
DWORD dwInitialFrames;
DWORD dwScale;
DWORD dwRate;
DWORD dwStart;
DWORD dwLength;
DWORD dwSuggestedBufferSize;
DWORD dwQuality;
DWORD dwSampleSize;
} AVIStreamHeader;
/*WAVE Bytes '##wb'
BYTE abBytes[];*/
/*DIB Bits '##db'
BYTE abBits[];*/
typedef struct {
BYTE bFirstEntry;
BYTE bNumEntries;
WORD wFlags;
BYTE *peNew;
} AVIPALCHANGE;
typedef struct {
DWORD ckid;
DWORD dwFlags;
DWORD dwChunkOffset;
DWORD dwChunkLength;
} AVIINDEXENTRY;
typedef struct BMPFILEHEADER {
char type[2];
DWORD size;
DWORD reserved;
DWORD offset;
}FileHeader;
typedef struct BMPINFOHEADER {
DWORD headersize;
DWORD width;
DWORD height;
WORD planes;
WORD bitsperpixel;
DWORD compression;
DWORD sizeimage;
DWORD xpixelspermeter;
DWORD ypixelspermeter;
DWORD colorsused;
DWORD colorsimportant;
} BmpHeader;
typedef struct tagBITMAP {
DWORD width;
DWORD height;
BYTE *data;
}BITMAP;
int main(void)
{
ifstream infile;
ofstream outfile;
infile.open("filedisk.avi"
outfile.open("testnew.bmp"
if (!infile)
cout << "Error opening file";
// DWORD x;
FileHeader bmpf;
bmpf.type[0] = 'B';
bmpf.type[1] = 'M';
bmpf.reserved = 0;
// DWORD size;
// DWORD offset;
BmpHeader bmph;
BITMAP bitmap;
char palette[768];
char ch,che[4];
int i=0,count;
int imageno = 1;
int skip = 0; //This is no. of fields delimited by 'db' and/or 'dc' to skip
int x=0,y=261;
MainAVIHeader avih;
while (!infile.eof())
{
infile >> ch;
if (ch == 'a')
{
infile >> che[0];
infile >> che[1];
infile >> che[2];
che[3] = '\0';
if (!strcmp(che,"vih"))
{
infile.ignore(4);
infile.read((char *)&avih,14*4);
break;
}
}
}
while (!infile.eof())
{
infile >> ch;
if (ch == 'v')
{
infile >> che[0];
infile >> che[1];
infile >> che[2];
che[3] = '\0';
if (!strcmp(che,"ids"))
{
while(1)
{
infile >> ch;
if (ch == 's')
{
infile >> che[0];
infile >> che[1];
infile >> che[2];
che[3] = '\0';
if (!strcmp(che,"trf"))
{
infile.ignore(4);
infile.read((char *)&bmph,40);
// cout << bmph.bitsperpixel;
if (bmph.bitsperpixel != 24)
{
/***read palette information***/
for(i=0;i<256;i++)
{
palette[i*3+2]=infile.get(
palette[i*3+1]=infile.get(
palette[i*3+0]=infile.get(
infile.get();
}
}
bitmap.width=bmph.width;
bitmap.height=bmph.height;
bitmap.data=new BYTE[bitmap.width*bitmap.h
count = bitmap.width*bitmap.height
while(1)
{
infile >> ch;
if (ch == '0')
{
infile >> che[0];
infile >> che[1];
infile >> che[2];
che[3] = '\0';
if (!strcmp(che,"0db") || !strcmp(che,"0dc"))
{
imageno--;
if (imageno == 0)
{
infile >> bitmap.data[i++];
while (1)
{
infile >> ch;
if (ch == '0')
{
infile >> che[0];
infile >> che[1];
infile >> che[2];
che[3] = '\0';
if (!strcmp(che,"0db") || !strcmp(che,"0dc"))
{
skip--;
if (skip == -1)
{
count = i;
bmpf.size = i;
break;
}
}
else
{
bitmap.data[i++] = ch;
bitmap.data[i++] = che[0];
bitmap.data[i++] = che[1];
bitmap.data[i++] = che[2];
if (i > bmph.sizeimage)
break;
}
}
else
{
bitmap.data[i++] = ch;
if (i > bmph.sizeimage)
break;
}
}
break;
}
}
}
}
break;
}
}
}
}
}
// infile.read((char *)&x,4);
// outfile.write((char *)&x,4);
}
bmpf.offset = 54;
bmpf.size += 54;
if (bmph.bitsperpixel != 24)
{
bmpf.offset += 1024;
bmpf.size += 1024;
}
outfile.write((char *)&bmpf,14);
outfile.write((char *)&bmph,40);
for(i=0;i<256;i++)
{
outfile.put(palette[i*3+2]
outfile.put(palette[i*3+1]
outfile.put(palette[i*3+0]
outfile.put(0);
}
outfile.write(bitmap.data,
delete bitmap.data;
outfile.close();
infile.close();
getch();
closegraph();
return 0;
}