function fileinfo = aviinfo(filename,outputType)
%AVIINFO Information about AVI file.
%   AVIINFO will be removed in a future release. Use VIDEOREADER instead.
%
%   FILEINFO = AVIINFO(FILENAME) returns a structure whose fields contain
%   information about the AVI file. FILENAME is a character vector or string scalar that specifies the
%   name of the AVI file.  If FILENAME does not include an extension, then
%   '.avi' will be used.  The file must be in the current working directory
%   or in a directory on the MATLAB path. 
%
%   The set of fields for FILEINFO are:
%   
%   Filename           - A character vector or string scalar, containing the name of the file.
%   		      
%   FileSize           - An integer indicating the size of the file in bytes.
%   		      
%   FileModDate        - A character vector or string scalar, containing the modification date of the file.
%   		      
%   NumFrames          - An integer indicating the total number of frames in
%                     	 the movie.
%   		      
%   FramesPerSecond    - An integer indicating the desired frames per second
%                     	 during playback.
%   		      
%   Width              - An integer indicating the width of AVI movie in
%                        pixels.
%   		      
%   Height             - An integer indicating the height of AVI movie in
%                     	 pixels.
%   		      
%   ImageType          - A character vector or string scalar, indicating the type of image; either
%                     	 'truecolor' for a truecolor (RGB) image, or
%                     	 'indexed', for an indexed image.
%   		      
%   VideoCompression   - A character vector or string scalar, containing the compressor used to compress 
%                     	 the AVI file.   If the compressor is not Microsoft
%                     	 Video 1, Run-Length Encoding, Cinepak, or Intel
%                     	 Indeo, the four character code is returned.   
%		      
%   Quality            - A number between 0 and 100 indicating the video
%                     	 quality in the AVI file.  Higher quality numbers
%                     	 indicate higher video quality, where lower
%                     	 quality numbers indicate lower video quality.  This
%                     	 value is not always set in AVI files and therefore
%                     	 may be inaccurate.
%
%   NumColormapEntries - The number of colors in the colormap. For a
%                        truecolor image this value is zero.
%   
%   If the AVI file contains an audio stream, the following fields will be
%   set in FILEINFO:
%   
%   AudioFormat      - A character vector or string scalar, containing the name of the format used to
%                      store the audio data.
%   
%   AudioRate        - An integer indicating the sample rate in Hertz of
%                      the audio stream.
%   
%   NumAudioChannels - An integer indicating the number of audio channels in
%                      the audio stream.
%   
%   See also VIDEOWRITER, VIDEOREADER.

%   Copyright 1984-2020 The MathWorks, Inc.

if nargin > 0
    filename = convertStringsToChars(filename);
end

if nargin > 1
    outputType = convertStringsToChars(outputType);
end

warning(message('MATLAB:audiovideo:aviinfo:FunctionToBeRemoved')); 

msg = '';
msgID = '';
narginchk(1,2);

if (~ischar(filename))
  error(message('MATLAB:audiovideo:aviinfo:invalidInputType'));
end

[path,name,ext] = fileparts(filename);
if isempty(ext)
  filename = strcat(filename,'.avi');
end

if nargin == 1
  outputType = [];
end

fid = matlab.internal.fopen(filename,'r','l');
if fid == -1
  error(message('MATLAB:audiovideo:aviinfo:unableToOpenFile', filename));
else
  filename = matlab.internal.fopen(fid);
end

% Find RIFF chunk.
[chunk, msg, msgID] = findchunk(fid,'RIFF');
errorWithFileClose(msgID,msg,fid);

% Read AVI chunk.
[rifftype,msg,msgID] = readfourcc(fid);
errorWithFileClose(msgID,msg,fid);
if ( strcmpi(rifftype,'AVI ') == 0 )
  error(message('MATLAB:audiovideo:aviinfo:invalidAVIFile'));
end

% Find hdrl LIST chunk.
[hdrlsize, msg, msgID] =  findlist(fid,'hdrl');
errorWithFileClose(msgID,msg,fid);

% Find avih chunk.
[chunk,msg,msgID] = findchunk(fid,'avih');
errorWithFileClose(msgID,msg,fid);

fileinfo.Filename = filename;
d = dir(filename);
fileinfo.FileModDate = d.date;
fileinfo.FileSize = d.bytes;

% Read main AVI header.
fileinfo.MainHeader = readAVIHeader(fid);

% Find the video and audio streams.
found = 0; audiofound = 0;
for i = 1:fileinfo.MainHeader.NumStreams
  % Find strl LIST chunk.
  [strlsize,msg,msgID] = findlist(fid,'strl');
  errorWithFileClose(msgID,msg,fid);
  % Read strh chunk.
  [strhchunk,msg,msgID] = findchunk(fid,'strh');
  errorWithFileClose(msgID,msg,fid);
  % Determine stream type.
  streamtype = readfourcc(fid);
  % If it is a video or audio stream, read it.
  if(strcmpi(streamtype,'vids') && (found==0))
    found = 1;
    if ( strhchunk.cksize == 64 )
      strh = read64ByteHeader(fid);
      fileinfo.VideoStreamHeader = strh;
    elseif ( strhchunk.cksize == 56 )
       strh = read56ByteHeader(fid);
       fileinfo.VideoStreamHeader = strh;
    elseif ( strhchunk.cksize == 48 )
      strh = read48ByteHeader(fid);
      fileinfo.VideoStreamHeader = strh;
    else 
      error(message('MATLAB:audiovideo:aviinfo:unknownHeaderSize'));
    end
    
    % Some files have a mismatched number of frames in the MainHeader and
    % VideoStreamHeader.  We should always trust the
    % VideoStreamHeaders number of frames over the MainHeader.TotalFrmes.  
    % However, it is possible that there are files whose
    % MainHeader.TotalFrames is greater than VideoStreamHeader's frames. 
    % So, to support such AVI files, only update the main headers total
    % frames if it is less than the video stream headers frame count.
    if fileinfo.MainHeader.TotalFrames < fileinfo.VideoStreamHeader.Length
        fileinfo.MainHeader.TotalFrames = fileinfo.VideoStreamHeader.Length;
    end
    
    % Read strf chunk.
    [strfvchunk, msg, msgID] = findchunk(fid,'strf');
    errorWithFileClose(msgID,msg,fid);
    
    % Read the data header.
    strfv = readBitmapHeader(fid, strfvchunk.cksize);
    
    % AVIINFO returns info only about the first video stream because
    % VIDEOREADER will only read the first video stream.
  elseif ( strcmpi(streamtype,'auds') && (audiofound == 0))
    audiofound = 1;
    if ( strhchunk.cksize == 64 )
      strh = read64ByteHeader(fid);
    elseif ( strhchunk.cksize == 56 )
      strh = read56ByteHeader(fid);
    else 
      msgID = 'MATLAB:audiovideo:aviinfo:unknownAudioStreamHeader';
      msg = getString(message(msgID));
    end
        
    if isempty(msg)
      % Read strf chunk.
      [strfachunk, msg, msgID] = findchunk(fid,'strf');
      errorWithFileClose(msgID,msg,fid);
      
      % Read the data header.
      strfa = readAudioFormat(fid, strfachunk.cksize);
      fileinfo.AudioStreamHeader = strfa;
    else
      warning(msgID, msg)
      if fseek(fid,strhchunk.cksize-4,0) == -1
        errorWithFileClose('MATLAB:audiovideo:aviinfo:invalidFileChunkSize',getString(message('MATLAB:audiovideo:aviinfo:invalidFileChunkSize')),fid);
      end
    end
  else
    % Seek to end of strl list minus the amount we read.
    if ( fseek(fid,strlsize - 16,0) == -1 )  
        error(message('MATLAB:audiovideo:aviinfo:invalidFileChunkSize'));
    end
  end
end
  
if (found == 0)
  errorWithFileClose('MATLAB:audiovideo:aviinfo:noVideoStreamFound',getString(message('MATLAB:audiovideo:aviinfo:noVideoStreamFound')),fid);
end

strfv.NumColormapEntries = (strfvchunk.cksize - strfv.BitmapHeaderSize)/4;

% 8-bit grayscale
% 24-bit truecolor
% 8-bit indexed
if strfv.NumColorsUsed > 0
  strfv.ImageType = 'indexed';
else
  if strfv.BitDepth > 8
    strfv.ImageType = 'truecolor';
    strfv.ImageType = 'truecolor';
  else
    strfv.ImageType = 'grayscale';
  end
end

fileinfo.VideoFrameHeader = strfv;

fileinfo = formulateOutput(fileinfo,outputType);
fclose(fid);
return;

% ------------------------------------------------------------------------
function outinfo = formulateOutput(info,outputType)
if isempty(outputType)
  outputType = 'Normal';
end

if strcmpi(outputType,'Normal');
  outinfo.Filename = info.Filename;
  outinfo.FileSize = info.FileSize;
  outinfo.FileModDate = info.FileModDate;
  outinfo.NumFrames = info.MainHeader.TotalFrames;
  outinfo.FramesPerSecond = info.VideoStreamHeader.Rate/info.VideoStreamHeader.Scale;
  outinfo.Width = info.VideoFrameHeader.Width;
  outinfo.Height = abs(info.VideoFrameHeader.Height);
  outinfo.ImageType = info.VideoFrameHeader.ImageType;
  outinfo.VideoCompression = info.VideoFrameHeader.CompressionType;
  outinfo.Quality = info.VideoStreamHeader.Quality/100;
  outinfo.NumColormapEntries = info.VideoFrameHeader.NumColormapEntries;
  if isfield(info,'AudioStreamHeader')
    outinfo.AudioFormat = info.AudioStreamHeader.Format;
    outinfo.AudioRate = info.AudioStreamHeader.SampleRate;
    outinfo.NumAudioChannels = info.AudioStreamHeader.NumChannels;
  end
elseif strcmpi(outputType,'robust')
  outinfo = info;  
end
return;

% ------------------------------------------------------------------------
function avih = readAVIHeader(fid)
	
msg = getString(message('MATLAB:audiovideo:aviinfo:unableToReadAVIHeader'));

% Read the micro-seconds per frame field and convert to fps.
[MicroSecPerFrame, count] = freadWithCheck(fid,1,'uint32',msg);
avih.FramesPerSecond = 1/(MicroSecPerFrame*10^-6);

% Read MaxBytePerSec.
avih.MaxBytePerSec =  freadWithCheck(fid,1,'uint32',msg);

% Read Reserved.
reserved =  freadWithCheck(fid,1,'uint32',msg);

% Read Flags.
flags =  freadWithCheck(fid,1,'uint32',msg);
flagbits = find(bitget(flags,1:32));
for i = 1:length(flagbits)
  switch flagbits(i)
   case 5
    avih.HasIndex = 'True';
   case 6
    avih.MustUseIndex = 'True';
   case 9
    avih.IsInterleaved = 'True';
   case 12
    avi.TrustCKType = 'True';
   case 17
    avih.WasCaptureFile = 'True';
   case 18
    avih.Copywrited = 'True';
  end
end

% Read TotalFrames.
avih.TotalFrames = freadWithCheck(fid,1,'uint32',msg);

% Read InitialFrames.
InitialFrames =  freadWithCheck(fid,1,'uint32',msg);

% Read NumStreams.
avih.NumStreams = freadWithCheck(fid,1,'uint32',msg);

% Read SuggestedBufferSize.
SuggestedBufferSize =  freadWithCheck(fid,1,'uint32',msg);

% Read Width.
avih.Width = freadWithCheck(fid,1,'uint32',msg);

% Read Height.
height = freadWithCheck(fid,1,'int32',msg);
% Handle the case of an AVI that's written top down
if height < 0
    height = -height;
end
avih.Height = height;

% Read Scale.
avih.Scale = freadWithCheck(fid,1,'uint32',msg);

% Read Rate.
avih.Rate = freadWithCheck(fid,1,'uint32',msg);

% Read Start.
start =  freadWithCheck(fid,1,'uint32',msg);

% Read Length, (value is typically not set properly).
len =  freadWithCheck(fid,1,'uint32',msg);
return;

% ------------------------------------------------------------------------
function strh = read64ByteHeader(fid)
% Purpose:  To read a stream header.
% Inputs:   A file identifier at the position 
%           of the stream header.
%
% Outputs:  A structure with fields corresponding 
%           pertinent information in the header.

msg = getString(message('MATLAB:audiovideo:aviinfo:unableToReadStreamHeader'));

% Read Compression handler.
[handler, count] = freadWithCheck(fid,4,'uchar',msg);
strh.Compression = char(handler)';

% Read Flags.
flags =  freadWithCheck(fid,1,'uint32',msg);
flagbits = find(bitget(flags,1:32));
for i = 1:length(flagbits)
  switch flagbits(i)
   case 17
    strh.PaletteChanges = 'True';
   case 1
    strh.DataRendering = 'Manual';
  end
end

% Read Reserved.
Reserved =  freadWithCheck(fid,1,'uint32',msg);

% Read InitialFrames.
strh.InitialFrames =  freadWithCheck(fid,1,'uint32',msg);

% Read Scale.
strh.Scale = freadWithCheck(fid,1,'uint32',msg);

% Read Rate.
strh.Rate  = freadWithCheck(fid,1,'uint32',msg);

% Read Start.
strh.StartTime = freadWithCheck(fid,1,'uint32',msg);

% Read Length (stream length units are in frames or seconds).
strh.Length = freadWithCheck(fid,1,'uint32',msg);

% Read SuggestedBufferSize.
strh.SuggestedBufferSize = freadWithCheck(fid,1,'uint32',msg);

% Read Quality.
strh.Quality = freadWithCheck(fid,1,'uint32',msg);

% Read SampleSize.
strh.SampleSize =  freadWithCheck(fid,1,'uint32',msg);

% Read Rect.
rect = freadWithCheck(fid,4,'uint32',msg);
return;

% ------------------------------------------------------------------------
function strh = read56ByteHeader(fid)

msg = getString(message('MATLAB:audiovideo:aviinfo:unableToReadStreamHeader'));
compression = freadWithCheck(fid,4,'char',msg);
strh.CompressionHandler = char(compression)';

flags = freadWithCheck(fid,1,'uint32',msg);
flagbits = find(bitget(flags,1:32));
for i = 1:length(flagbits)
  switch flagbits(i)
   case 17
    strh.PaletteChanges = 'True';
   case 1
    strh.DataRendering = 'Manual';
  end
end

strh.Reserved = freadWithCheck(fid,1,'uint32',msg);  
strh.InitialFrames = freadWithCheck(fid,1,'uint32',msg);
strh.Scale = freadWithCheck(fid,1,'uint32',msg);    
strh.Rate = freadWithCheck(fid,1,'uint32',msg);     
strh.Start = freadWithCheck(fid,1,'uint32',msg);     
strh.Length = freadWithCheck(fid,1,'uint32',msg);   
strh.SuggestedBufferSize = freadWithCheck(fid,1,'uint32',msg);
strh.Quality = freadWithCheck(fid,1,'uint32',msg);   
strh.SampleSize = freadWithCheck(fid,1,'uint32',msg);

% Read Rect.
Rect = freadWithCheck(fid,4,'uint16',msg);
return;

% ------------------------------------------------------------------------
function strh = read48ByteHeader(fid)
msg = getString(message('MATLAB:audiovideo:aviinfo:unableToReadStreamHeader'));
% Read fccHandler.
compression = freadWithCheck(fid,4,'char',msg);
strh.CompressionHandler = char(compression)';

flags = freadWithCheck(fid,1,'uint32',msg);
flagbits = find(bitget(flags,1:32));
for i = 1:length(flagbits)
  switch flagbits(i)
   case 17
    strh.PaletteChanges = 'True';
   case 1
    strh.DataRendering = 'Manual';
  end
end

strh.Reserved = freadWithCheck(fid,1,'uint32',msg);  
strh.InitialFrames = freadWithCheck(fid,1,'uint32',msg);
strh.Scale = freadWithCheck(fid,1,'uint32',msg);    
strh.Rate = freadWithCheck(fid,1,'uint32',msg);     
strh.Start = freadWithCheck(fid,1,'uint32',msg);     
strh.Length = freadWithCheck(fid,1,'uint32',msg);   
strh.SuggestedBufferSize = freadWithCheck(fid,1,'uint32',msg);
strh.Quality = freadWithCheck(fid,1,'uint32',msg);   
strh.SampleSize = freadWithCheck(fid,1,'uint32',msg);
return;

% ------------------------------------------------------------------------
function strf = readBitmapHeader(fid, chunksize)
% Purpose: To read the BITMAPINFO header information.

Compression = '';
msg = getString(message('MATLAB:audiovideo:aviinfo:unableToReadBITMAPINFOHEADER'));

% Get the starting file position for this header.
fPosHeaderStart = ftell(fid);
if (fPosHeaderStart == -1)
  error(message('MATLAB:audiovideo:aviinfo:invalidFileChunkSize'));
end

% Read header size.
strf.BitmapHeaderSize = freadWithCheck(fid,1,'uint32',msg);

% Read Width.
strf.Width = freadWithCheck(fid,1,'int32',msg);

% Read Height.
strf.Height = freadWithCheck(fid,1,'int32',msg);

% Read Planes.
strf.Planes = freadWithCheck(fid,1,'uint16',msg);

% Read BitCount.
strf.BitDepth = freadWithCheck(fid,1,'uint16',msg);

% Read Compression.
compress = freadWithCheck(fid,1,'uint32',msg);
switch compress
 case 0
  Compression = 'none';
 case 1
  Compression = '8-bit RLE';
 case 2
  Compression = '4-bit RLE';
 case 3
  Compression = 'bitfields';
end

if isempty(Compression)
  code = getfourcc(compress);
  switch lower(code)
   case 'none'
    Compression = 'None';
   case 'rgb '
    Compression = 'None';
   case 'raw '
    Compression = 'None';  
   case '    '
    Compression = 'None';
   case 'rle '
    Compression = 'RLE';
   case 'cvid'
    Compression = 'Cinepak';
   case 'iv32'
    Compression = 'Indeo3';
   case 'iv50'
    Compression = 'Indeo5';
   case 'msvc'
    Compression = 'MSVC';
   case 'cram'
    Compression = 'MSVC';
   otherwise
    Compression = code;
  end
end

strf.CompressionType = Compression;

strf.Bitmapsize = freadWithCheck(fid,1,'uint32',msg);

strf.HorzResoltion = freadWithCheck(fid,1,'uint32',msg);

strf.VertResolution = freadWithCheck(fid,1,'uint32',msg);

strf.NumColorsUsed = freadWithCheck(fid,1,'uint32',msg);

strf.NumImportantColors = freadWithCheck(fid,1,'uint32',msg);

% Get the current file position.
fPosHeader = ftell(fid);
if (fPosHeader == -1)
  error(message('MATLAB:audiovideo:aviinfo:invalidFileChunkSize'));
end

% Calculate the remaining bytes in this header.
headerBytesRead = fPosHeader - fPosHeaderStart;
headerBytesRemaining = chunksize - headerBytesRead;

% Skip over the remainder of the header.
if headerBytesRemaining > 0
    if (fseek(fid, headerBytesRemaining, 0) == -1 )  
	  error(message('MATLAB:audiovideo:aviinfo:invalidFileChunkSize'));
    end
end
return

% ------------------------------------------------------------------------
function strf = readAudioFormat(fid, chunksize)
% Read WAV format chunk information.

msg = getString(message('MATLAB:audiovideo:aviinfo:unableToReadAudioStreamHeader'));
% Read format tag.
formatTag = freadWithCheck(fid,1,'uint16',msg);

% Complete list of formats can be found in Microsoft Platform SDK header
% file "MMReg.h" or in MSDN Library (search for "registered wave formats").
switch formatTag
 case  1
  strf.Format = 'PCM';
 case 2
  strf.Format = 'Microsoft ADPCM';
 case 6
  strf.Format = 'CCITT a-law';
 case 7
  strf.Format = 'CCITT mu-law';
 case 17
  strf.Format = 'IMA ADPCM';   
 case 34
  strf.Format = 'DSP Group TrueSpeech TM';
 case 49
  strf.Format = 'GSM 6.10';
 case 50
  strf.Format = 'MSN Audio';
 otherwise
  strf.Format = ['Format # 0x' dec2hex(formatTag)];
end

% Read number of channels.
strf.NumChannels = freadWithCheck(fid,1,'uint16',msg);

% Read samples per second.
strf.SampleRate = freadWithCheck(fid,1,'uint32',msg);

% Read buffer estimation.
avgBytesPerSec = freadWithCheck(fid,1,'uint32',msg);

% Read block size of data.
blockAlign = freadWithCheck(fid,1,'uint16',msg);

% If this strf chunk is larger than 14 bytes, then
% it uses the newer extended wave format.  Read in
% the extra information.
if chunksize > 14
  % Read bits per sample.
  bitsPerSample = freadWithCheck(fid,1,'uint16',msg);
  % Non-PCM formats have additional header information.
  if chunksize > 16
   % Read the size (in bytes) of the additional information.
   size = freadWithCheck(fid,1,'uint16',msg);
    if size > 0
     % Skip over extra unneeded bytes based on the above size.
     if (fseek(fid, size, 0) == -1 )
      error(message('MATLAB:audiovideo:aviinfo:invalidFileChunkSize'));
     end
    end
  end
end
return

% ------------------------------------------------------------------------
function errorWithFileClose(msgID,msg,fid)
% Close open file the error.
if ~isempty(msgID)
  fclose(fid);
  error(msgID,msg);
end
return;

% ------------------------------------------------------------------------
function [value,count] = freadWithCheck(fid,size,precision,msgID,msg)
% A wrapper around FREAD to make sure the correct amount of data was read.
[value, count] = fread(fid,size,precision);
if count ~= size
  errorWithFileClose(msgID,msg,fid);
end
return;
