You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Some major refactoing of the FFmpegReader, to allow for audio and video packets that are out of order.
This commit is contained in:
@@ -44,9 +44,15 @@ namespace openshot {
|
||||
/// Check for the existance of a frame in the cache
|
||||
bool Exists(int frame_number);
|
||||
|
||||
/// Return the front frame and remove it
|
||||
Frame Pop();
|
||||
|
||||
/// Get a frame from the cache
|
||||
Frame GetFrame(int frame_number);
|
||||
|
||||
/// Get the front frame in this cache
|
||||
Frame GetFront();
|
||||
|
||||
/// Clear the cache of all frames
|
||||
void Clear();
|
||||
|
||||
|
||||
@@ -33,6 +33,14 @@ using namespace std;
|
||||
|
||||
namespace openshot
|
||||
{
|
||||
/// This struct holds the associated video frame and starting sample # for an audio packet.
|
||||
/// Because audio packets do not match up with video frames, this helps determine exactly
|
||||
/// where the audio packet's samples belong.
|
||||
struct audio_packet_location
|
||||
{
|
||||
int frame;
|
||||
int sample_start;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief This class uses the FFmpeg libraries, to open video files and audio files, and return
|
||||
@@ -56,39 +64,55 @@ namespace openshot
|
||||
AVPacket packet;
|
||||
AVFrame *pFrame;
|
||||
|
||||
Frame new_frame;
|
||||
Cache final_cache;
|
||||
Cache working_cache;
|
||||
|
||||
bool is_seeking;
|
||||
int seeking_pts;
|
||||
int seeking_frame;
|
||||
bool is_video_seek;
|
||||
|
||||
bool found_pts_offset;
|
||||
int pts_offset;
|
||||
int audio_pts_offset;
|
||||
int video_pts_offset;
|
||||
|
||||
int last_video_frame;
|
||||
int last_audio_frame;
|
||||
|
||||
bool found_frame;
|
||||
int current_frame;
|
||||
int current_pts;
|
||||
int audio_position;
|
||||
bool needs_packet;
|
||||
|
||||
/// Open File - which is called by the constructor automatically
|
||||
void Open();
|
||||
|
||||
/// Convert image to RGB format
|
||||
Frame convert_image(AVPicture *copyFrame, int original_width, int original_height, PixelFormat pix_fmt);
|
||||
void convert_image(int current_frame, AVPicture *copyFrame, int width, int height, PixelFormat pix_fmt);
|
||||
|
||||
/// Convert PTS into Frame Number
|
||||
int ConvertPTStoFrame(int pts);
|
||||
/// Get the PTS for the current video packet
|
||||
int GetVideoPTS();
|
||||
|
||||
/// Convert Frame Number into PTS
|
||||
int ConvertFrameToPTS(int frame_number);
|
||||
/// Update PTS Offset (if any)
|
||||
void UpdatePTSOffset(bool is_video);
|
||||
|
||||
/// Calculate Starting video frame for an audio PTS
|
||||
int GetFrameFromAudioPTS(int pts);
|
||||
/// Convert Video PTS into Frame Number
|
||||
int ConvertVideoPTStoFrame(int pts);
|
||||
|
||||
/// Convert Frame Number into Video PTS
|
||||
int ConvertFrameToVideoPTS(int frame_number);
|
||||
|
||||
/// Convert Frame Number into Audio PTS
|
||||
int ConvertFrameToAudioPTS(int frame_number);
|
||||
|
||||
/// Calculate Starting video frame and sample # for an audio PTS
|
||||
audio_packet_location GetAudioPTSLocation(int pts);
|
||||
|
||||
/// Calculate the # of samples per video frame
|
||||
int GetSamplesPerFrame();
|
||||
|
||||
/// Create a new Frame (or return an existing one) and add it to the working queue.
|
||||
Frame CreateFrame(int requested_frame);
|
||||
|
||||
/// Check the working queue, and move finished frames to the finished queue
|
||||
void CheckWorkingFrames(bool end_of_stream);
|
||||
|
||||
|
||||
public:
|
||||
/// Constructor for FFmpegReader. This automatically opens the media file and loads
|
||||
/// frame 1, or it throws one of the following exceptions.
|
||||
@@ -119,19 +143,16 @@ namespace openshot
|
||||
void ProcessVideoPacket(int requested_frame);
|
||||
|
||||
/// Process an audio packet
|
||||
void ProcessAudioPacket(int requested_frame);
|
||||
void ProcessAudioPacket(int requested_frame, int target_frame, int starting_sample);
|
||||
|
||||
/// Get the next packet (if any)
|
||||
int GetNextPacket();
|
||||
|
||||
/// Set the frame number and current pts
|
||||
void SetFrameNumber();
|
||||
|
||||
/// Get an AVFrame (if any)
|
||||
bool GetAVFrame();
|
||||
|
||||
/// Check the current seek position and determine if we need to seek again
|
||||
bool CheckSeek();
|
||||
bool CheckSeek(bool is_video);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace openshot
|
||||
|
||||
public:
|
||||
int number; ///< This is the frame number (starting at 1)
|
||||
int pts; ///< This is the presentation timestamp (in frames)
|
||||
|
||||
/// Constructor - blank frame (300x200 blank image, 48kHz audio silence)
|
||||
Frame();
|
||||
@@ -45,7 +44,7 @@ namespace openshot
|
||||
Frame(int number, int width, int height, string color);
|
||||
|
||||
/// Constructor - image only from pixel array (48kHz audio silence)
|
||||
Frame(int number, int pts, int width, int height, const string map, const Magick::StorageType type, const void *pixels_);
|
||||
Frame(int number, int width, int height, const string map, const Magick::StorageType type, const void *pixels_);
|
||||
|
||||
/// Constructor - audio only (300x200 blank image)
|
||||
Frame(int number, int samples, int channels);
|
||||
@@ -89,6 +88,9 @@ namespace openshot
|
||||
/// Save the frame image
|
||||
void Save();
|
||||
|
||||
/// Add (or replace) pixel data to the frame
|
||||
void AddImage(int width, int height, const string map, const Magick::StorageType type, const void *pixels_);
|
||||
|
||||
/// Add audio samples to a specific channel
|
||||
void AddAudio(int destChannel, int destStartSample, const float* source, int numSamples, float gainToApplyToSource);
|
||||
|
||||
|
||||
@@ -46,6 +46,23 @@ bool Cache::Exists(int frame_number)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the front frame and remove it
|
||||
Frame Cache::Pop()
|
||||
{
|
||||
// Remove the oldest frame
|
||||
int frame_to_remove = frame_numbers.front();
|
||||
|
||||
// Get the front frame
|
||||
Frame f = GetFrame(frame_to_remove);
|
||||
|
||||
// Remove frame_number and frame
|
||||
frame_numbers.pop();
|
||||
frames.erase(frame_to_remove);
|
||||
|
||||
// Return the frame
|
||||
return f;
|
||||
}
|
||||
|
||||
// Get a frame from the cache
|
||||
Frame Cache::GetFrame(int frame_number)
|
||||
{
|
||||
@@ -60,6 +77,13 @@ Frame Cache::GetFrame(int frame_number)
|
||||
throw OutOfBoundsFrame("Frame not found in the cache", frame_number, -1);
|
||||
}
|
||||
|
||||
// Get the front frame in this cache
|
||||
Frame Cache::GetFront()
|
||||
{
|
||||
// Return oldest frame in the cache
|
||||
return GetFrame(frame_numbers.front());
|
||||
}
|
||||
|
||||
// Clear the cache of all frames
|
||||
void Cache::Clear()
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ using namespace std;
|
||||
using namespace openshot;
|
||||
|
||||
// Constructor - blank frame (300x200 blank image, 48kHz audio silence)
|
||||
Frame::Frame() : number(1), pts(0), image(0), audio(0)
|
||||
Frame::Frame() : number(1), image(0), audio(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(Magick::Geometry(300,200), Magick::Color("red"));
|
||||
@@ -21,7 +21,7 @@ Frame::Frame() : number(1), pts(0), image(0), audio(0)
|
||||
};
|
||||
|
||||
// Constructor - image only (48kHz audio silence)
|
||||
Frame::Frame(int number, int width, int height, string color) : number(number), pts(0), image(0), audio(0)
|
||||
Frame::Frame(int number, int width, int height, string color) : number(number), image(0), audio(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(Magick::Geometry(width, height), Magick::Color(color));
|
||||
@@ -32,8 +32,8 @@ Frame::Frame(int number, int width, int height, string color) : number(number),
|
||||
};
|
||||
|
||||
// Constructor - image only from pixel array (48kHz audio silence)
|
||||
Frame::Frame(int number, int pts, int width, int height, const string map, const Magick::StorageType type, const void *pixels)
|
||||
: number(number), pts(pts), image(0), audio(0)
|
||||
Frame::Frame(int number, int width, int height, const string map, const Magick::StorageType type, const void *pixels)
|
||||
: number(number), image(0), audio(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(width, height, map, type, pixels);
|
||||
@@ -44,7 +44,7 @@ Frame::Frame(int number, int pts, int width, int height, const string map, const
|
||||
};
|
||||
|
||||
// Constructor - audio only (300x200 blank image)
|
||||
Frame::Frame(int number, int samples, int channels) : number(number), pts(0), image(0), audio(0)
|
||||
Frame::Frame(int number, int samples, int channels) : number(number), image(0), audio(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(Magick::Geometry(300, 200), Magick::Color("white"));
|
||||
@@ -55,7 +55,7 @@ Frame::Frame(int number, int samples, int channels) : number(number), pts(0), im
|
||||
};
|
||||
|
||||
// Constructor - image & audio
|
||||
Frame::Frame(int number, int width, int height, string color, int samples, int channels) : number(number), pts(0), image(0), audio(0)
|
||||
Frame::Frame(int number, int width, int height, string color, int samples, int channels) : number(number), image(0), audio(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(Magick::Geometry(width, height), Magick::Color(color));
|
||||
@@ -176,6 +176,17 @@ void Frame::Save()
|
||||
image->write(file.str());
|
||||
}
|
||||
|
||||
// Add (or replace) pixel data to the frame
|
||||
void Frame::AddImage(int width, int height, const string map, const Magick::StorageType type, const void *pixels)
|
||||
{
|
||||
// Deallocate image memory
|
||||
delete image;
|
||||
image = NULL;
|
||||
|
||||
// Create new image object, and fill with pixel data
|
||||
image = new Magick::Image(width, height, map, type, pixels);
|
||||
}
|
||||
|
||||
// Add audio samples to a specific channel
|
||||
void Frame::AddAudio(int destChannel, int destStartSample, const float* source, int numSamples, float gainToApplyToSource = 1.0f)
|
||||
{
|
||||
|
||||
@@ -28,11 +28,8 @@ int main()
|
||||
// Display debug info
|
||||
r.DisplayInfo();
|
||||
|
||||
r.GetFrame(300);
|
||||
r.GetFrame(300);
|
||||
r.GetFrame(301);
|
||||
r.GetFrame(302);
|
||||
r.GetFrame(5);
|
||||
r.GetFrame(1).Display();
|
||||
r.GetFrame(300).Display();
|
||||
|
||||
//Player g;
|
||||
//g.SetReader(&r);
|
||||
|
||||
@@ -121,3 +121,56 @@ TEST(Cache_GetFrame)
|
||||
CHECK_EQUAL(3, c.GetFrame(3).number);
|
||||
}
|
||||
|
||||
TEST(Cache_GetFront)
|
||||
{
|
||||
// Create cache object (with a max of 10 items)
|
||||
Cache c(10);
|
||||
|
||||
// Create 3 frames
|
||||
Frame red(1, 300, 300, "red");
|
||||
Frame blue(2, 400, 400, "blue");
|
||||
Frame green(3, 500, 500, "green");
|
||||
|
||||
// Add frames to cache
|
||||
c.Add(red.number, red);
|
||||
c.Add(blue.number, blue);
|
||||
c.Add(green.number, green);
|
||||
|
||||
// Check if frame 1 is the front
|
||||
CHECK_EQUAL(1, c.GetFront().number);
|
||||
|
||||
// Check if frame 1 is STILL the front
|
||||
CHECK_EQUAL(1, c.GetFront().number);
|
||||
|
||||
// Erase frame 1
|
||||
c.Pop();
|
||||
|
||||
// Check if frame 2 is the front
|
||||
CHECK_EQUAL(2, c.GetFront().number);
|
||||
}
|
||||
|
||||
TEST(Cache_Pop)
|
||||
{
|
||||
// Create cache object (with a max of 10 items)
|
||||
Cache c(10);
|
||||
|
||||
// Create 3 frames
|
||||
Frame red(1, 300, 300, "red");
|
||||
Frame blue(2, 400, 400, "blue");
|
||||
Frame green(3, 500, 500, "green");
|
||||
|
||||
// Add frames to cache
|
||||
c.Add(red.number, red);
|
||||
c.Add(blue.number, blue);
|
||||
c.Add(green.number, green);
|
||||
|
||||
// Check if frame 1 is the front
|
||||
Frame pop1 = c.Pop();
|
||||
CHECK_EQUAL(1, pop1.number);
|
||||
|
||||
Frame pop2 = c.Pop();
|
||||
CHECK_EQUAL(2, pop2.number);
|
||||
|
||||
Frame pop3 = c.Pop();
|
||||
CHECK_EQUAL(3, pop3.number);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user