从 avi 文件中提取正确的创建日期

Extract correct creation date from avi file

我正在使用 Delphi 10 和 Windows 10 家庭版 64 位。我有一个名为 MVI_0640.AVI 的视频文件。文件资源管理器中显示的日期是 15/04/04,对应于属性 window.

中的媒体创建日期

我正在使用以下代码提取日期。

procedure TForm1.Button1Click(Sender: TObject);
var
  ADate: TDateTime;
  FlHandle: integer;
  MyData: TWin32FindData;
  FlTime: TFileTime;
  MySysTime: TSystemTime;
begin
  {get date using GetCreationTime}
  ADate := TFile.GetCreationTime(FlName);
  Memo1.Lines.Add('GetCreationTime ' + DateToStr(ADate));

  {get date using FileGetDate}
  FlHandle := FileOpen(FlName,fmOpenRead);
  ADate := FileDateToDateTime(FileGetDate(FlHandle));
  FileClose(FlHandle);
  Memo1.Lines.Add('FileGetDate ' + DateToStr(ADate));

  {get date using FindFirstFile}
  FindFirstFile(PChar(FlName), MyData);

  FlTime := MyData.ftCreationTime;
  FileTimeToSystemTime(FlTime, MySysTime);
  ADate := SystemTimeToDateTime(MySysTime);
  Memo1.Lines.Add('ftCreationTime ' + DateToStr(ADate));

  FlTime := MyData.ftLastAccessTime;
  FileTimeToSystemTime(FlTime, MySysTime);
  ADate := SystemTimeToDateTime(MySysTime);
  Memo1.Lines.Add('ftLastAccessTime ' + DateToStr(ADate));

  FlTime := MyData.ftLastWriteTime;
  FileTimeToSystemTime(FlTime, MySysTime);
  ADate := SystemTimeToDateTime(MySysTime);
  Memo1.Lines.Add('ftLastWriteTime ' + DateToStr(ADate));

end;

结果如下所示:

None 的日期反映了媒体创建日期。我怎样才能提取它?

为了回答 Tom Brunberg 的评论,我附上了用十六进制编辑器截取的文件的摘录。

您要查找的日期位于名为 IDIT 的数据块中。它被称为例如在 this document

结构简单,(来自我的文件的示例数据):

chunk id:     IDIT  // 4 ASCII chars
chunk length: 0000001A // 26 bytes
chunk data:   Sun Aug 31 12:15:22 2008/n/0 // date as ascii string

AVI 文件的结构是 outlined by Microsoft,如下所示,如果存在 IDIT 块的位置

RIFF ('AVI '
      LIST ('hdrl'
            'avih'(<Main AVI Header>)
            LIST ('strl'
                  'strh'(<Stream header>)
                  'strf'(<Stream format>)
                  [ 'strd'(<Additional header data>) ]
                  [ 'strn'(<Stream name>) ]
                  ...
                 )
             ... (note, if present, the IDIT chunk appears here)
           )
      LIST ('movi'
            {SubChunk | LIST ('rec '
                              SubChunk1
                              SubChunk2
                              ...
                             )
               ...
            }
            ...
           )
      ['idx1' (<AVI Index>) ]
     )

上述文档还概​​述了各种结构。

示例数据:

获取日期(如果存在于文件中)的函数如下所示:

// Note! finetuned to search only the main TLIST 'hdrl' 
function GetOriginalDate(AviFileName: TFileName; out s: string): boolean;
type
  TChunkId = array[0..3] of AnsiChar;

  TChunk = record
    chid: TChunkId;
    size: cardinal;
    form: TChunkId;
  end;
var
  fs: TFileStream;
  Root: TChunk;
  Chnk: TChunk;
  Done: boolean;
  Date: ansistring;
  endpos: integer;
begin
  s := 'not found';
  Done := False;
  result := False;

  fs:= TFileStream.Create(AviFileName, fmOpenRead or fmShareDenyWrite);

  try
    fs.Read(Root, SizeOf(Root));
    if Root.chid <> 'RIFF' then exit;
    if Root.form <> 'AVI ' then exit;

    fs.Read(Chnk, SizeOf(TChunk)); // main LIST
    if Chnk.chid <> 'LIST' then exit;
    if Chnk.form <> 'hdrl' then exit;

    endpos := fs.Position + Chnk.size;
    repeat
      fs.Read(Chnk, SizeOf(TChunk));
      if Chnk.chid = 'IDIT' then
      begin
        fs.Seek(-4, TSeekOrigin.soCurrent);
        SetLength(Date, Chnk.size);
        fs.Read(Date[1], Length(Date));
        s := Date;
        Done := True;
      end
      else
        fs.Seek(Chnk.size-4, TSeekOrigin.soCurrent);
    until Done or (fs.Position > endpos);

  finally
    fs.Free;
  end;
end;

调用它,例如:

procedure TForm1.Button2Click(Sender: TObject);
var
  s: string;
begin
  GetOriginalDate('F:\My Video08-08-31\MVI_1279.AVI', s);
  Memo1.Lines.Add(s);
end;

并生成 Memo1

Sun Aug 31 12:15:22 2008

我找到了问题的答案。可能不是很优雅或节省,但它确实起作用了。我在 100 多个文件上对其进行了测试,并且没有问题。这是我的回答:

function TForm1.GetAviMediaCreationDate(AFile: string): TDateTime;
var
  FS: TFileStream;
  NumOfChar: integer;
  i,d: integer;
  ABuffer: array of byte;
  AStr: string;
  DateStr: string;
  sdp: integer; //start date position
  dn,mn,yn: integer; //used to encode date
begin
  sdp := 0;
  FS := TFileStream.Create(AFile,fmOpenRead);
  NumOfChar := 400;
  SetLength(ABuffer,NumOfChar);
  FS.Read(Pointer(ABuffer)^, NumOfChar);
  {find IDIT}
  for i := 0 to NumOfChar-1 do
  begin
    AStr := Char(ABuffer[i]) +
            Char(ABuffer[i+1]) +
            Char(ABuffer[i+2]) +
            Char(ABuffer[i+3]);
    if AStr = 'IDIT' then sdp := i+7;
  end;
  {extract date}
  for d := 1 to 24 do
  DateStr := DateStr + Char(ABuffer[sdp+d]);
  {assemble TDateTime}
  //123456789 123456789 123456789
  //Sun Jun 28 10:13:39 2015
  dn := StrToInt(Copy(DateStr,9,2));
  mn := IndexText(Copy(DateStr,5,3),ShortMonthNames)+1;
  yn := StrToInt(Copy(DateStr,21,4));
  Result := EncodeDate(yn, mn, dn);
  FS.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  ADate: TDateTime;
begin
  ADate := GetAviMediaCreationDate(FlName);
  Memo1.Lines.Add(DateToStr(ADate));
end;