【问题标题】:Extract correct creation date from avi file从 avi 文件中提取正确的创建日期
【发布时间】:2019-08-22 12:10:51
【问题描述】:

我使用的是 Delphi 10 和 Windows 10 Home Edition 64 位。我有一个名为 MVI_0640.AVI 的视频文件。文件资源管理器中显示的日期是 15/04/04,对应于“属性”窗口中的媒体创建日期。

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

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;

结果如下:

没有任何日期反映媒体创建日期。如何提取它?

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

【问题讨论】:

标签: file date delphi extract avi


【解决方案1】:

您要查找的日期位于名为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 Video\2008-08-31\MVI_1279.AVI', s);
  Memo1.Lines.Add(s);
end;

并导致 Memo1

Sun Aug 31 12:15:22 2008

【讨论】:

  • 有机会提供一些 Delphi 示例代码的链接吗?
  • 元数据块不应该在他们自己的 INFO 顶级块中吗?您的屏幕截图甚至显示了 INFO 块。 en.wikipedia.org/wiki/…
  • 不,抱歉@Rudi 我没有这样的链接(我现在也不能发布任何东西)。但是,如果您在 SO 中搜索 read riff,可能会出现问题。您能否确认文件中存在IDIT 标签并且它们看起来相似,即日期和时间格式相同?
  • @Amigo 我想这是合理的,但我越是查看有关RIFF 的各种文档,它似乎就越多地被各种供应商自己的想法所修改。但是当您参考我的图像时,我也会这样做:您可以清楚地看到IDIT 标签不在INFO 块中,不,之前也没有其他INFO 标签。
  • Rudi,@Sertac 引用的文档还列出了与DateTimeOriginal 相关的另一个标签(与IDIT 相同),即DTIM。我在我的任何文件中都没有看到这一点,但我只是想我应该提到它可能与我在回答中所说的有偏差。另外,我不知道这样的标签是否会在 Windows 资源管理器中显示 Media Created Date
【解决方案2】:

我找到了我的问题的答案。可能不是很优雅或保存,但它确实是工作。我在 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;

【讨论】:

  • 嘿嘿。谈巧合。只需几分钟。
猜你喜欢
  • 2022-11-10
  • 2014-01-28
  • 2016-02-03
  • 1970-01-01
  • 2013-04-17
  • 2012-01-11
  • 2014-06-28
  • 2010-11-11
  • 1970-01-01
相关资源
最近更新 更多