Skip to content
This repository has been archived by the owner on Nov 19, 2020. It is now read-only.

Commit

Permalink
GH-948: Accord.Video.FFMPEG.VideoFileReader should provide frame-base…
Browse files Browse the repository at this point in the history
…d random access
  • Loading branch information
cesarsouza committed Oct 13, 2017
1 parent e7368f4 commit 713ec35
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 15 deletions.
57 changes: 47 additions & 10 deletions Sources/Extras/Accord.Video.FFMPEG.GPL/VideoFileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ namespace Accord {
libffmpeg::AVCodecContext* CodecContext;
libffmpeg::AVFrame* VideoFrame;
struct libffmpeg::SwsContext* ConvertContext;
unsigned long int nextFrameIndex;

libffmpeg::AVPacket* Packet;
int uint8_tsRemaining;
Expand All @@ -69,6 +70,7 @@ namespace Accord {

Packet = nullptr;
uint8_tsRemaining = 0;
nextFrameIndex = 0;
}
};

Expand Down Expand Up @@ -203,9 +205,19 @@ namespace Accord {
data = nullptr;
}

// Read next video frame of the current video file
Bitmap^ VideoFileReader::ReadVideoFrame()
int64_t FrameToPTS(libffmpeg::AVStream* stream, int frame)
{
return (int64_t(frame) * stream->r_frame_rate.den * stream->time_base.den)
/ (int64_t(stream->r_frame_rate.num) * stream->time_base.num);
}



Bitmap^ VideoFileReader::readVideoFrame(int frameIndex, BitmapData^ output)
{
if (frameIndex == -1)
frameIndex = data->nextFrameIndex;

CheckIfDisposed();

if (data == nullptr)
Expand All @@ -215,6 +227,16 @@ namespace Accord {
Bitmap^ bitmap = nullptr;
int uint8_tsDecoded;
bool exit = false;
bool needsToSeek = false;

if (frameIndex != this->data->nextFrameIndex)
{
needsToSeek = true;
libffmpeg::avcodec_flush_buffers(data->VideoStream->codec);
int error = libffmpeg::av_seek_frame(data->FormatContext, data->VideoStream->index, frameIndex, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);
if (error < 0)
throw gcnew VideoException("Error while seeking frame.");
}

while (true)
{
Expand All @@ -232,7 +254,13 @@ namespace Accord {

// did we finish the current frame? Then we can return
if (frameFinished)
return DecodeVideoFrame();
{
if (!needsToSeek || data->VideoFrame->pts >= FrameToPTS(data->VideoStream, frameIndex))
{
data->nextFrameIndex = frameIndex + 1;
return DecodeVideoFrame(output);
}
}
}

// read the next packet, skipping all packets that aren't for this stream
Expand Down Expand Up @@ -273,19 +301,25 @@ namespace Accord {

// is there a frame
if (frameFinished)
bitmap = DecodeVideoFrame();
bitmap = DecodeVideoFrame(output);

return bitmap;
}

// Decodes video frame into managed Bitmap
Bitmap^ VideoFileReader::DecodeVideoFrame()
Bitmap^ VideoFileReader::DecodeVideoFrame(BitmapData^ bitmapData)
{
Bitmap^ bitmap = gcnew Bitmap(data->CodecContext->width, data->CodecContext->height, PixelFormat::Format24bppRgb);
Bitmap^ bitmap = nullptr;

// lock the bitmap
BitmapData^ bitmapData = bitmap->LockBits(System::Drawing::Rectangle(0, 0, data->CodecContext->width, data->CodecContext->height),
ImageLockMode::ReadOnly, PixelFormat::Format24bppRgb);
if (bitmapData == nullptr)
{
// create a new Bitmap with format 24-bpp RGB
bitmap = gcnew Bitmap(data->CodecContext->width, data->CodecContext->height, PixelFormat::Format24bppRgb);

// lock the bitmap
bitmapData = bitmap->LockBits(System::Drawing::Rectangle(0, 0, data->CodecContext->width, data->CodecContext->height),
ImageLockMode::ReadWrite, PixelFormat::Format24bppRgb);
}

uint8_t* srcData[4] = { static_cast<uint8_t*>(static_cast<void*>(bitmapData->Scan0)),
nullptr, nullptr, nullptr };
Expand All @@ -295,9 +329,12 @@ namespace Accord {
libffmpeg::sws_scale(data->ConvertContext, data->VideoFrame->data, data->VideoFrame->linesize, 0,
data->CodecContext->height, srcData, srcLinesize);

bitmap->UnlockBits(bitmapData);
if (bitmap != nullptr)
bitmap->UnlockBits(bitmapData); // unlock only if we have created the bitmap ourselves
return bitmap;
}


}
}
}
56 changes: 53 additions & 3 deletions Sources/Extras/Accord.Video.FFMPEG.GPL/VideoFileReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ namespace Accord {
/// <remarks><para>The class allows to read video files using <a href="http://www.ffmpeg.org/">FFmpeg</a> library.</para>
///
/// <para><note>Make sure you have <b>FFmpeg</b> binaries (DLLs) in the output folder of your application in order
/// to use this class successfully. <b>FFmpeg</b> binaries can be found in Externals folder provided with AForge.NET
/// to use this class successfully. <b>FFmpeg</b> binaries can be found in Externals folder provided with Accord.NET
/// framework's distribution.</note></para>
///
/// <para>Sample usage:</para>
Expand Down Expand Up @@ -88,7 +88,9 @@ namespace Accord {
ReaderPrivateData^ data;
bool disposed;

Bitmap^ DecodeVideoFrame();
Bitmap^ DecodeVideoFrame(BitmapData^ bitmapData);

Bitmap^ readVideoFrame(int frameIndex, BitmapData^ output);

// Checks if video file was opened
void CheckIfVideoFileIsOpen()
Expand Down Expand Up @@ -257,7 +259,55 @@ namespace Accord {
/// <exception cref="System::IO::IOException">Thrown if no video file was open.</exception>
/// <exception cref="VideoException">A error occurred while reading next video frame. See exception message.</exception>
///
Bitmap^ ReadVideoFrame();
Bitmap^ ReadVideoFrame()
{
return ReadVideoFrame(-1);
}

/// <summary>
/// Read the given video frame of the currently opened video file.
/// </summary>
///
/// <returns>Returns the desired frame of the opened file or <see langword="null"/> if end of
/// file was reached. The returned video frame has 24 bpp color format.</returns>
///
/// <exception cref="System::IO::IOException">Thrown if no video file was open.</exception>
/// <exception cref="VideoException">A error occurred while reading next video frame. See exception message.</exception>
///
Bitmap^ ReadVideoFrame(int frameIndex)
{
return readVideoFrame(frameIndex, nullptr);
}

/// <summary>
/// Read next video frame of the currently opened video file.
/// </summary>
///
/// <returns>Returns next video frame of the opened file or <see langword="null"/> if end of
/// file was reached. The returned video frame has 24 bpp color format.</returns>
///
/// <exception cref="System::IO::IOException">Thrown if no video file was open.</exception>
/// <exception cref="VideoException">A error occurred while reading next video frame. See exception message.</exception>
///
void ReadVideoFrame(BitmapData^ output)
{
readVideoFrame(-1, output);
}

/// <summary>
/// Read the given video frame of the currently opened video file.
/// </summary>
///
/// <returns>Returns the desired frame of the opened file or <see langword="null"/> if end of
/// file was reached. The returned video frame has 24 bpp color format.</returns>
///
/// <exception cref="System::IO::IOException">Thrown if no video file was open.</exception>
/// <exception cref="VideoException">A error occurred while reading next video frame. See exception message.</exception>
///
void ReadVideoFrame(int frameIndex, BitmapData^ output)
{
readVideoFrame(frameIndex, output);
}

/// <summary>
/// Close currently opened video file if any.
Expand Down
4 changes: 2 additions & 2 deletions Sources/Extras/Accord.Video.FFMPEG.GPL/VideoFileWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ namespace Accord {
if (!(outputFormat->flags & AVFMT_NOFILE))
{
int err = libffmpeg::avio_open(&data->FormatContext->pb, nativeFileName, AVIO_FLAG_WRITE);


if (err < 0)
{
System::String^ msg = GetErrorMessage(err, fileName);
Expand All @@ -481,7 +481,7 @@ namespace Accord {
System::String^ VideoFileWriter::GetErrorMessage(int err, System::String ^ fileName)
{
char buff[AV_ERROR_MAX_STRING_SIZE];
libffmpeg::av_make_error_string(&buff[0], AV_ERROR_MAX_STRING_SIZE, err);
libffmpeg::av_make_error_string(&buff[0], AV_ERROR_MAX_STRING_SIZE, err);
System::String^ msg = System::Runtime::InteropServices::Marshal::PtrToStringAnsi((IntPtr)&buff[0]);
return msg;
}
Expand Down

0 comments on commit 713ec35

Please sign in to comment.