首页 > 代码库 > 分享一段代码,基于 ffmpeg 的视频转化为图片

分享一段代码,基于 ffmpeg 的视频转化为图片

代码如下:

function Video2Bmp(const strVideoFileName, strSavePath: string): Boolean;
var
  pfc1          : PAVFormatContext;
  pfc2          : PAVFormatContext;
  intVideoStreamIndex: Integer;
  pps          : PPAVStream;
  III          : Integer;
  pcc          : PAVCodecContext;
  pCodec          : PAVCodec;
  pFrameVdo          : PAVFrame;
  pFrameRGB          : PAVFrame;
  psc          : PSwsContext;
  avp          : TAVPacket;
  intRemain          : Integer;
  bFirst          : Boolean;
  intDecoded         : Integer;
  intFrameFinished   : Integer;
  bmp          : TBitmap;
label
  lbexit;
begin
  Result := False;

  { 1. 注册所有容器格式和CODEC }
  av_register_all();

  { 2. 打开文件 }
  pfc1 := nil;
  if avformat_open_input(pfc1, PAnsiChar(AnsiString(strVideoFileName)), nil, nil) <> 0 then
  begin
    ShowMessage('打开文件失败,请检查你的文件!');
    Exit;
  end;

  { 3. 从文件中提取流信息 }
  if avformat_find_stream_info(pfc1, nil) < 0 then
  begin
    ShowMessage('不存在任何音频或视频流!');
    Exit;
  end;

  { 4. 穷举所有的流,查找是否包含视频流 }
  intVideoStreamIndex := -1;
  pfc2          := pfc1;
  pps          := pfc1^.streams;
  for III          := 0 to pfc1^.nb_streams - 1 do
  begin
    if pps^.codec.codec_type = AVMEDIA_TYPE_VIDEO then
    begin
      intVideoStreamIndex := III;
    end;
    inc(pps);
  end;

  if intVideoStreamIndex = -1 then
  begin
    ShowMessage('不存在视频流!');
    Exit;
  end;

  { 5. 定位到视频流 }
  inc(pfc1^.streams, intVideoStreamIndex);
  pcc    := pfc1^.streams^.codec;
  pCodec := avcodec_find_decoder(pcc^.codec_id);
  if pCodec = nil then
  begin
    ShowMessage('找不到视频流!');
    Exit;
  end;

  { 6. 修正视频流 }
  if (pCodec^.capabilities and CODEC_CAP_TRUNCATED) = CODEC_CAP_TRUNCATED then
    pcc^.flags := pcc.flags or CODEC_FLAG_TRUNCATED;
  if avcodec_open2(pcc, pCodec, nil) < 0 then
  begin
    ShowMessage('打开视频流发生错误!');
    Exit;
  end;

  { 7. 视频流转化为BMP }
  if (pcc^.time_base.num > 1000) and (pcc^.time_base.den = 1) then
    pcc^.time_base.den := 1000;
  pFrameVdo          := avcodec_alloc_frame();
  pFrameRGB          := avcodec_alloc_frame;
  { 按视频帧的原始大小输出图片 }
  avpicture_alloc(PAVPicture(pFrameRGB), AV_PIX_FMT_RGB32, pcc^.Width, pcc^.Height);
  psc       := sws_getContext(pcc^.Width, pcc^.Height, pcc^.pix_fmt, pcc^.Width, pcc^.Height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nil, nil, nil);
  intRemain := 0;
  bFirst    := True;

  { 查找视频的每一帧 }
  while True do
  begin
    if bFirst then
    begin
      bFirst   := False;
      avp.data := nil;
    end;

    while True do
    begin
      while intRemain > 0 do
      begin
        intDecoded := avcodec_decode_video2(pcc, pFrameVdo, intFrameFinished, @avp);
        if intDecoded < 0 then
          break;

        dec(intRemain, intDecoded);

        { 找到完整的一帧 }
        if intFrameFinished <> 0 then
        begin
          { 按原图输出 }
          sws_scale(psc, @pFrameVdo^.data, @pFrameVdo^.linesize, 0, pcc^.Height, @pFrameRGB^.data, @pFrameRGB^.linesize);

          { 输出位图 }
          bmp := TBitmap.Create;
          try
          bmp.PixelFormat := pf32bit;
          bmp.Width       := pcc^.Width;
          bmp.Height      := pcc^.Height;
          for III         := 0 to bmp.Height - 1 do
          begin
          CopyMemory(bmp.ScanLine[III], Pointer(Integer(pFrameRGB.data[0]) + bmp.Width * 4 * III), bmp.Width * 4);
          end;
          bmp.SaveToFile(Format('%s\%d.bmp', [strSavePath, GetTickCount]));
          finally
          bmp.free;
          end;

          { 寻找下一帧 }
          Continue;
        end;
      end;

      repeat
        if av_read_frame(pfc2, @avp) < 0 then
          goto lbexit;

      until avp.stream_index = intVideoStreamIndex;
      intRemain := avp.size;
    end;

    lbexit:
    avcodec_decode_video2(pcc, pFrameVdo, intFrameFinished, @avp);

    if avp.data <> nil then
      av_free_packet(@avp);

    if intFrameFinished = 0 then
      break;
  end;

  { 8. 释放资源 }
  sws_freeContext(psc);
  avpicture_free(PAVPicture(pFrameRGB));
  av_free(pFrameRGB);
  av_free(pFrameVdo);
  avcodec_close(pcc);
  avformat_close_input(pfc1);

  { 9. 返回成功 }
  Result := True;
end;


分享一段代码,基于 ffmpeg 的视频转化为图片