Recent

Author Topic: Making video thumbnail with ffmpeg  (Read 9928 times)

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Making video thumbnail with ffmpeg
« on: September 24, 2015, 01:10:08 pm »
Hi,

I want to make thumbnail for videos and for that the best way I found is ffmpeg :
Code: [Select]
ffmpeg -i in.avi -vf thumbnail,scale=300:200 -frames:v 1 out.pngbut I dont want to save this to file so I used pip and TProcess :
https://www.ffmpeg.org/ffmpeg-all.html#pipe
http://wiki.freepascal.org/Executing_External_Programs#Reading_large_output
but I still cant understand how TProcess work with this situation and cant get the thumbnail.
Code: [Select]
const
  BUF_SIZE = 2048;
var
  numbytes, bytesread: integer;
var
  Buffer: array[1..BUF_SIZE] of byte;
var
  p: TProcess;
  OutputStream: TStream;   
begin
 p := TProcess.Create(nil);
 p.Executable := 'ffmpeg.exe';
 p.Parameters.add('-i');
 p.Parameters.add('in.avi');
 p.Parameters.add('-vf');
 p.Parameters.add('thumbnail,scale=300:200');
 p.Parameters.add('-frames:v 1');
 p.Parameters.add('-');           
  p.Options := [poUsePipes];
  bytesread := 0;
  OutputStream := TMemoryStream.Create;
  p.Execute;       
  {
  while p.Running do
  begin
    NumBytes := p.Output.Read(Buffer, BUF_SIZE);
    if NumBytes > 0 then
      Inc(BytesRead, NumBytes)
    else
      Sleep(100);
  end;
  }
  repeat
    NumBytes := p.Output.Read(Buffer, BUF_SIZE);
    if NumBytes > 0 then
      Inc(BytesRead, NumBytes);
  until NumBytes <= 0;


  p.Free;

  with TFileStream.Create('output.jpg', fmCreate) do
  begin
    OutputStream.Position := 0; // Required to make sure all data is copied from the start
    CopyFrom(OutputStream, OutputStream.Size);
    Free;
  end;


aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Making video thumbnail with ffmpeg
« Reply #1 on: September 24, 2015, 01:30:43 pm »
There are other ways to solve the new code it.
Can you made an example?

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Making video thumbnail with ffmpeg
« Reply #2 on: September 26, 2015, 11:18:27 am »
Its more about TProcess.When I run and try to read output it will freeze :
Code: [Select]
NumBytes := p.Output.Read(Buffer, BUF_SIZE);
Is there any reason or wrong property?

derek.john.evans

  • Guest
Re: Making video thumbnail with ffmpeg
« Reply #3 on: September 26, 2015, 11:50:12 am »
Its more about TProcess.When I run and try to read output it will freeze :
Code: [Select]
NumBytes := p.Output.Read(Buffer, BUF_SIZE);
Is there any reason or wrong property?

There is a little "gotcha" here. You must check Output.NumBytesAvailable before calling Output.Read().

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Making video thumbnail with ffmpeg
« Reply #4 on: September 26, 2015, 12:32:24 pm »
Thanks Geepster,

I check that but the problem seems in the ffmpeg part so there will never be a output although I checked my command and it works.

Jurassic Pork

  • Hero Member
  • *****
  • Posts: 1228
Re: Making video thumbnail with ffmpeg
« Reply #5 on: September 26, 2015, 12:54:07 pm »
hello,
Quote
but I dont want to save this to file
aradeonas, what do you want to do in your program with the thumbnails after ?
Jurassic computer : Sinclair ZX81 - Zilog Z80A à 3,25 MHz - RAM 1 Ko - ROM 8 Ko

derek.john.evans

  • Guest
Re: Making video thumbnail with ffmpeg
« Reply #6 on: September 26, 2015, 12:57:18 pm »
Thanks Geepster,

I check that but the problem seems in the ffmpeg part so there will never be a output although I checked my command and it works.

Yep, I see the issue. FFmpeg needs you to read from both StdErr and Ouput, otherwise it locks up. I modified my generic TProcess piper (see below).

And then after reading some forums, this seems to work:
Code: Pascal  [Select][+][-]
  1. procedure FFmpegGetVideoFrame(const APicture: TPicture; const AFileName: String; const ASeconds: Double);
  2. var
  3.   LMemoryStream: TMemoryStream;
  4. begin
  5.   LMemoryStream := TMemoryStream.Create;
  6.   try
  7.     ProcessExecute('ffmpeg.exe', '-ss,' + FloatToStr(ASeconds) + ',-i,' + AnsiQuotedStr(AFileName, '"') +
  8.       ',-f,mjpeg,-vframes,1,pipe:1',
  9.       nil, LMemoryStream, nil);
  10.     LMemoryStream.Position := 0;
  11.     APicture.LoadFromStream(LMemoryStream);
  12.   finally
  13.     FreeAndNil(LMemoryStream);
  14.   end;
  15. end;
  16.  

I tested it with this:
Code: Pascal  [Select][+][-]
  1. FFmpegGetVideoFrame(Image1.Picture, 'Resolution (2012).mp4', 60 * 10);  
  2.  


Here is my current generic TProcess function. Now includes 3 streams, which can be nil if not used. (It may need more testing)
Code: Pascal  [Select][+][-]
  1. procedure ProcessExecute(const AExecutable, AParameters: String; const AInput, AOutput, AStderr: TStream);
  2. var
  3.   LBuffer: array[word] of byte;
  4.   LCount: longint;
  5. begin
  6.   with TProcess.Create(nil) do begin
  7.     try
  8.       Options := [poUsePipes];
  9.       ShowWindow := swoHIDE;
  10.       Executable := AExecutable;
  11.       Parameters.CommaText := AParameters;
  12.       Execute;
  13.       if Assigned(AInput) then begin
  14.         while Running and (Input.Write(LBuffer, AInput.Read(LBuffer, SizeOf(LBuffer))) > 0) do begin
  15.           Sleep(10);
  16.         end;
  17.       end;
  18.       CloseInput;
  19.       while Running or (Output.NumBytesAvailable > 0) or (Stderr.NumBytesAvailable > 0) do begin
  20.         if Stderr.NumBytesAvailable > 0 then begin
  21.           LCount := Stderr.Read(LBuffer, SizeOf(LBuffer));
  22.           if Assigned(AStderr) then begin
  23.             AStderr.Write(LBuffer, LCount);
  24.           end;
  25.         end;
  26.         if Output.NumBytesAvailable > 0 then begin
  27.           LCount := Output.Read(LBuffer, SizeOf(LBuffer));
  28.           if Assigned(AOutput) then begin
  29.             AOutput.Write(LBuffer, LCount);
  30.           end;
  31.         end;
  32.         Sleep(10);
  33.       end;
  34.     finally
  35.       Free;
  36.     end;
  37.   end;
  38. end;
  39.  

** EDIT ** Small bug fix. Added "or (Stderr.NumBytesAvailable > 0)"
« Last Edit: October 02, 2015, 03:01:19 am by Geepster »

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Making video thumbnail with ffmpeg
« Reply #7 on: September 26, 2015, 01:51:05 pm »
hello,
Quote
but I dont want to save this to file
aradeonas, what do you want to do in your program with the thumbnails after ?
Some resizing and maybe save to file and I dont want to read and right from file for every thumbnail.

Geepster you are good at it ;) Thank you.
It works but when I try to edit it for thumbnail the output will be nothing.
Code: [Select]
ProcessExecute('ffmpeg.exe','-i,' + AnsiQuotedStr(AFileName, '"') + ',-vf,thumbnail,-frames:v,1,pipe:1', nil, LMemoryStream, nil); 
but this command will work.
Code: [Select]
ffmpeg -i input.avi -vf thumbnail -frames:v 1 out.png

derek.john.evans

  • Guest
Re: Making video thumbnail with ffmpeg
« Reply #8 on: September 26, 2015, 02:00:22 pm »
I dont think the option thumbnail is what you are after. Try playing with the video filter (vf) option "scale".

Code: Pascal  [Select][+][-]
  1.     ProcessExecute('ffmpeg.exe', '-ss,' + FloatToStr(ASeconds) + ',-i,' + AnsiQuotedStr(AFileName, '"') +
  2.       ',-f,mjpeg,-vf,scale=300:200,-vframes,1,pipe:1',
  3.       nil, LMemoryStream, nil);  
  4.  

Note, you need the option "-f mjpeg", to output jpegs, since a pipe doesn't have a file extension.
« Last Edit: October 02, 2015, 03:00:54 am by Geepster »

derek.john.evans

  • Guest

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Making video thumbnail with ffmpeg
« Reply #10 on: September 26, 2015, 04:00:09 pm »
I dont think the option thumbnail is what you are after. Try playing with the video filter (vf) option "scale".

Code: [Select]
    ProcessExecute('ffmpeg.exe', '-ss,' + FloatToStr(ASeconds) + ',-i,' + AnsiQuotedStr(AFileName, '"') +
      ',-f,mjpeg,-vf,scale=300:200,-vframes,1,pipe:1',
      nil, LMemoryStream, nil); 

Note, you need the option "-f mjpeg", to output jpegs, since a pipe doesn't have a file extension.
I think I am.
Becuase it give you a better thumbnail than a simple screenshot but I asked as curiosity that why my command didnt work.
https://ffmpeg.org/ffmpeg-all.html#toc-thumbnail
Thanks for the jpeg note.

derek.john.evans

  • Guest
Re: Making video thumbnail with ffmpeg
« Reply #11 on: September 27, 2015, 01:51:39 am »
I think I am. Because it give you a better thumbnail than a simple screenshot.

O, I see what it does. It picks the best from a batch of 100 frames (default)?

Mmm. I guess that is starting from the seek position? Anyway, good luck with your development.

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Making video thumbnail with ffmpeg
« Reply #12 on: September 27, 2015, 10:14:38 am »
Yes.
But couldn't find out why this one don't work like your example.
Thanks.

aidv

  • Full Member
  • ***
  • Posts: 173
Re: Making video thumbnail with ffmpeg
« Reply #13 on: October 05, 2015, 04:10:44 am »
Did you solve the problem?

If not, here's my code and also a SO question I posted just minutes ago: http://stackoverflow.com/questions/32940247/extracting-thumbnails-with-ffmpeg-is-super-slow-on-large-video-files/32940313#32940313

Here's my code:

Code: Pascal  [Select][+][-]
  1.  
  2. //Returns whatever TProcess spits out. This one works better than the original RunCommand.
  3. function RunCommand_v2(const ExeName: string; const Commands: array of string): string;
  4.   var pr: TProcess;
  5.       sl: TStringList;
  6. begin
  7.   pr := TProcess.Create(nil);
  8.   pr.Options := [poUsePipes, poStderrToOutPut, poWaitOnExit];
  9.   pr.Parameters.AddStrings(Commands);
  10.   pr.Executable := ExeName;
  11.   pr.Execute;
  12.  
  13.   sl := TStringList.Create;
  14.   sl.LoadFromStream(pr.Output);
  15.   result := sl.text;
  16.  
  17.   sl.free;
  18.   pr.free;
  19. end;      
  20.  
  21. ...
  22.  
  23. begin
  24.   RunCommand_v2('ffmpeg', ['-ss', '00:02:35', '-i', 'videoFile.mov', '-vframes:v', '1', 'thumbnail.jpg']);
  25. end;
  26.  
  27. ...
  28.  
  29.  

 

TinyPortal © 2005-2018