You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Refactored FFmpegReader code a bit, fixed many, many memory leaks, and many valgrind errors. Now the memory seems very stable, and the seeking and stream walking seems very solid.
This commit is contained in:
@@ -60,7 +60,6 @@ namespace openshot
|
||||
AVFormatContext *pFormatCtx;
|
||||
int i, videoStream, audioStream;
|
||||
AVCodecContext *pCodecCtx, *aCodecCtx;
|
||||
AVCodec *pCodec, *aCodec;
|
||||
AVStream *pStream, *aStream;
|
||||
AVPacket *packet;
|
||||
AVFrame *pFrame;
|
||||
|
||||
@@ -38,9 +38,6 @@ namespace openshot
|
||||
int sample_rate;
|
||||
int channels;
|
||||
|
||||
map<int, bool> channels_complete;
|
||||
bool image_complete;
|
||||
|
||||
public:
|
||||
int number; ///< This is the frame number (starting at 1)
|
||||
|
||||
|
||||
@@ -8,12 +8,15 @@ FFmpegReader::FFmpegReader(string path) throw(InvalidFile, NoStreamsFound, Inval
|
||||
is_video_seek(true), check_interlace(false), check_fps(false), init_settings(false),
|
||||
enable_seek(true) {
|
||||
|
||||
// Initialize FFMpeg, and register all formats and codecs
|
||||
av_register_all();
|
||||
|
||||
// Open the file (if possible)
|
||||
Open();
|
||||
|
||||
// Check for the correct frames per second (only once)
|
||||
if (info.has_video)
|
||||
CheckFPS();
|
||||
//if (info.has_video)
|
||||
// CheckFPS();
|
||||
|
||||
// Get 1st frame
|
||||
GetFrame(1);
|
||||
@@ -22,10 +25,7 @@ FFmpegReader::FFmpegReader(string path) throw(InvalidFile, NoStreamsFound, Inval
|
||||
void FFmpegReader::Open()
|
||||
{
|
||||
// Initialize format context
|
||||
pFormatCtx = avformat_alloc_context();
|
||||
|
||||
// Register all formats and codecs
|
||||
av_register_all();
|
||||
pFormatCtx = NULL;
|
||||
|
||||
// Open video file
|
||||
if (avformat_open_input(&pFormatCtx, path.c_str(), NULL, NULL) != 0)
|
||||
@@ -70,7 +70,7 @@ void FFmpegReader::Open()
|
||||
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
|
||||
|
||||
// Find the decoder for the video stream
|
||||
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
|
||||
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
|
||||
if (pCodec == NULL) {
|
||||
throw InvalidCodec("A valid video codec could not be found for this file.", path);
|
||||
}
|
||||
@@ -93,7 +93,7 @@ void FFmpegReader::Open()
|
||||
aCodecCtx = pFormatCtx->streams[audioStream]->codec;
|
||||
|
||||
// Find the decoder for the audio stream
|
||||
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
|
||||
AVCodec *aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
|
||||
if (aCodec == NULL) {
|
||||
throw InvalidCodec("A valid audio codec could not be found for this file.", path);
|
||||
}
|
||||
@@ -111,12 +111,23 @@ void FFmpegReader::Close()
|
||||
{
|
||||
// Close the codec
|
||||
if (info.has_video)
|
||||
{
|
||||
avcodec_flush_buffers(pCodecCtx);
|
||||
avcodec_close(pCodecCtx);
|
||||
}
|
||||
if (info.has_audio)
|
||||
{
|
||||
avcodec_flush_buffers(aCodecCtx);
|
||||
avcodec_close(aCodecCtx);
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
working_cache.Clear();
|
||||
final_cache.Clear();
|
||||
|
||||
// Close the video file
|
||||
avformat_close_input(&pFormatCtx);
|
||||
av_freep(&pFormatCtx);
|
||||
}
|
||||
|
||||
void FFmpegReader::UpdateAudioInfo()
|
||||
@@ -209,7 +220,13 @@ void FFmpegReader::UpdateVideoInfo()
|
||||
|
||||
// Override an invalid framerate
|
||||
if (info.fps.ToFloat() > 120.0f)
|
||||
info.has_video = false;
|
||||
{
|
||||
// Set a few important default video settings (so audio can be divided into frames)
|
||||
info.fps.num = 30;
|
||||
info.fps.den = 1;
|
||||
info.video_timebase.num = 1;
|
||||
info.video_timebase.den = 30;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -392,6 +409,11 @@ int FFmpegReader::GetNextPacket()
|
||||
|
||||
// Update current packet pointer
|
||||
packet = packets[next_packet];
|
||||
}else
|
||||
{
|
||||
// Free packet, since it's unused
|
||||
av_free_packet(next_packet);
|
||||
av_freep(next_packet);
|
||||
}
|
||||
|
||||
// Return if packet was found (or error number)
|
||||
@@ -601,10 +623,13 @@ void FFmpegReader::ProcessAudioPacket(int requested_frame, int target_frame, int
|
||||
int16_t *audio_buf = NULL;
|
||||
int16_t *converted_audio = NULL;
|
||||
float *channel_buffer = NULL;
|
||||
ReSampleContext *resampleCtx = NULL;
|
||||
int pts = my_packet->pts;
|
||||
|
||||
#pragma omp task firstprivate(requested_frame, target_frame, my_cache, my_packet, pts, audio_buf, converted_audio, channel_buffer, resampleCtx, starting_sample)
|
||||
// Add audio frame to list of processing audio frames
|
||||
#pragma omp critical (processing_list)
|
||||
processing_audio_frames[target_frame] = target_frame;
|
||||
|
||||
#pragma omp task firstprivate(requested_frame, target_frame, my_cache, my_packet, pts, audio_buf, converted_audio, channel_buffer, starting_sample)
|
||||
{
|
||||
// Allocate audio buffer
|
||||
audio_buf = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
|
||||
@@ -647,7 +672,7 @@ void FFmpegReader::ProcessAudioPacket(int requested_frame, int target_frame, int
|
||||
if(aCodecCtx->sample_fmt != AV_SAMPLE_FMT_S16) {
|
||||
// Audio needs to be converted
|
||||
// Create an audio resample context object (used to convert audio samples)
|
||||
resampleCtx = av_audio_resample_init(
|
||||
ReSampleContext *resampleCtx = av_audio_resample_init(
|
||||
info.channels,
|
||||
info.channels,
|
||||
info.sample_rate,
|
||||
@@ -723,7 +748,7 @@ void FFmpegReader::ProcessAudioPacket(int requested_frame, int target_frame, int
|
||||
if (samples > remaining_samples)
|
||||
samples = remaining_samples;
|
||||
|
||||
// Add video frame to list of processing video frames
|
||||
// Add audio frame to list of processing audio frames
|
||||
#pragma omp critical (processing_list)
|
||||
processing_audio_frames[starting_frame_number] = starting_frame_number;
|
||||
|
||||
@@ -754,15 +779,15 @@ void FFmpegReader::ProcessAudioPacket(int requested_frame, int target_frame, int
|
||||
}
|
||||
|
||||
// clear channel buffer
|
||||
delete channel_buffer;
|
||||
delete[] channel_buffer;
|
||||
channel_buffer = NULL;
|
||||
iterate_channel_buffer = NULL;
|
||||
}
|
||||
|
||||
// Clean up some arrays
|
||||
delete audio_buf;
|
||||
delete[] audio_buf;
|
||||
audio_buf = NULL;
|
||||
delete converted_audio;
|
||||
delete[] converted_audio;
|
||||
converted_audio = 0;
|
||||
|
||||
// Add video frame to list of processing video frames
|
||||
@@ -773,6 +798,7 @@ void FFmpegReader::ProcessAudioPacket(int requested_frame, int target_frame, int
|
||||
processing_audio_frames.erase(f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -811,6 +837,9 @@ void FFmpegReader::Seek(int requested_frame)
|
||||
|
||||
// Calculate seek target
|
||||
seek_target = ConvertFrameToVideoPTS(requested_frame - 3);
|
||||
|
||||
// Flush video buffer
|
||||
avcodec_flush_buffers(pCodecCtx);
|
||||
}
|
||||
else if (info.has_audio)
|
||||
{
|
||||
@@ -820,6 +849,9 @@ void FFmpegReader::Seek(int requested_frame)
|
||||
|
||||
// Calculate seek target
|
||||
seek_target = ConvertFrameToAudioPTS(requested_frame - 3); // Seek a few frames prior to the requested frame (to avoid missing some samples)
|
||||
|
||||
// Flush audio buffer
|
||||
avcodec_flush_buffers(aCodecCtx);
|
||||
}
|
||||
|
||||
// If seeking to frame 1, we need to close and re-open the file (this is more reliable than seeking)
|
||||
@@ -857,11 +889,7 @@ void FFmpegReader::Seek(int requested_frame)
|
||||
}
|
||||
}
|
||||
|
||||
// Flush buffers
|
||||
if (info.has_video)
|
||||
avcodec_flush_buffers(pCodecCtx);
|
||||
if (info.has_audio)
|
||||
avcodec_flush_buffers(aCodecCtx);
|
||||
|
||||
}
|
||||
|
||||
// Get the PTS for the current video packet
|
||||
@@ -1135,7 +1163,7 @@ void FFmpegReader::CheckFPS()
|
||||
|
||||
// Give up (if threshold exceeded)
|
||||
if (iterations > threshold)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
// Double check that all counters have greater than zero (or give up)
|
||||
@@ -1219,7 +1247,7 @@ int FFmpegReader::GetSmallestVideoFrame()
|
||||
{
|
||||
// Loop through frame numbers
|
||||
map<int, int>::iterator itr;
|
||||
int smallest_frame = 0;
|
||||
int smallest_frame = -1;
|
||||
for(itr = processing_video_frames.begin(); itr != processing_video_frames.end(); ++itr)
|
||||
{
|
||||
if (itr->first < smallest_frame || smallest_frame == -1)
|
||||
@@ -1235,7 +1263,7 @@ int FFmpegReader::GetSmallestAudioFrame()
|
||||
{
|
||||
// Loop through frame numbers
|
||||
map<int, int>::iterator itr;
|
||||
int smallest_frame = 0;
|
||||
int smallest_frame = -1;
|
||||
for(itr = processing_audio_frames.begin(); itr != processing_audio_frames.end(); ++itr)
|
||||
{
|
||||
if (itr->first < smallest_frame || smallest_frame == -1)
|
||||
|
||||
@@ -10,8 +10,7 @@ using namespace std;
|
||||
using namespace openshot;
|
||||
|
||||
// Constructor - blank frame (300x200 blank image, 48kHz audio silence)
|
||||
Frame::Frame() : number(1), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000),
|
||||
image_complete(false), channels(2)
|
||||
Frame::Frame() : number(1), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000), channels(2)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(Magick::Geometry(300,200), Magick::Color("red"));
|
||||
@@ -23,8 +22,7 @@ Frame::Frame() : number(1), image(0), audio(0), pixel_ratio(1,1), sample_rate(48
|
||||
|
||||
// Constructor - image only (48kHz audio silence)
|
||||
Frame::Frame(int number, int width, int height, string color)
|
||||
: number(number), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000),
|
||||
image_complete(false), channels(2)
|
||||
: number(number), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000), channels(2)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(Magick::Geometry(width, height), Magick::Color(color));
|
||||
@@ -36,8 +34,7 @@ Frame::Frame(int number, int width, int height, string color)
|
||||
|
||||
// Constructor - image only from pixel array (48kHz audio silence)
|
||||
Frame::Frame(int number, int width, int height, const string map, const Magick::StorageType type, const void *pixels)
|
||||
: number(number), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000),
|
||||
image_complete(false), channels(2)
|
||||
: number(number), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000), channels(2)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(width, height, map, type, pixels);
|
||||
@@ -49,8 +46,7 @@ Frame::Frame(int number, int width, int height, const string map, const Magick::
|
||||
|
||||
// Constructor - audio only (300x200 blank image)
|
||||
Frame::Frame(int number, int samples, int channels) :
|
||||
number(number), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000),
|
||||
image_complete(false), channels(channels)
|
||||
number(number), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000), channels(channels)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(Magick::Geometry(300, 200), Magick::Color("white"));
|
||||
@@ -62,8 +58,7 @@ Frame::Frame(int number, int samples, int channels) :
|
||||
|
||||
// Constructor - image & audio
|
||||
Frame::Frame(int number, int width, int height, string color, int samples, int channels)
|
||||
: number(number), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000),
|
||||
image_complete(false), channels(channels)
|
||||
: number(number), image(0), audio(0), pixel_ratio(1,1), sample_rate(48000), channels(channels)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
image = new Magick::Image(Magick::Geometry(width, height), Magick::Color(color));
|
||||
@@ -112,8 +107,6 @@ void Frame::DeepCopy(const Frame& other)
|
||||
pixel_ratio = Fraction(other.pixel_ratio.num, other.pixel_ratio.den);
|
||||
sample_rate = other.sample_rate;
|
||||
channels = other.channels;
|
||||
image_complete = other.image_complete;
|
||||
channels_complete = other.channels_complete;
|
||||
}
|
||||
|
||||
// Deallocate image and audio memory
|
||||
|
||||
15
src/Main.cpp
15
src/Main.cpp
@@ -16,11 +16,11 @@ int main()
|
||||
// openshot::FFmpegReader r("/home/jonathan/apps/libopenshot/src/examples/test.mp4");
|
||||
// openshot::FFmpegReader r("/home/jonathan/apps/libopenshot/src/examples/test1.mp4");
|
||||
// openshot::FFmpegReader r("/home/jonathan/apps/libopenshot/src/examples/piano.wav");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Videos/sintel-1024-stereo.mp4");
|
||||
openshot::FFmpegReader r("/home/jonathan/Videos/sintel-1024-stereo.mp4");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Videos/00001.mts");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Videos/sintel_trailer-720p.mp4");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Aptana Studio Workspace/OpenShotLibrary/src/examples/piano.wav");
|
||||
openshot::FFmpegReader r("/home/jonathan/Music/Army of Lovers/Crucified/Army of Lovers - Crucified [Single Version].mp3");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Music/Army of Lovers/Crucified/Army of Lovers - Crucified [Single Version].mp3");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Documents/OpenShot Art/test.jpeg");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Videos/60fps.mp4");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Aptana Studio Workspace/OpenShotLibrary/src/examples/asdf.wdf");
|
||||
@@ -31,19 +31,16 @@ int main()
|
||||
for (int repeat = 0; repeat <= 10; repeat++)
|
||||
{
|
||||
cout << "----------- REPEAT READER " << repeat << " ---------------" << endl;
|
||||
for (int frame = 1; frame <= 400; frame++)
|
||||
for (int frame = 300; frame <= 400; frame++)
|
||||
{
|
||||
Frame f = r.GetFrame(frame);
|
||||
//f.Play();
|
||||
//f.Display();
|
||||
//f.DisplayWaveform(false);
|
||||
f.Play();
|
||||
f.Display();
|
||||
f.DisplayWaveform(false);
|
||||
}
|
||||
|
||||
cout << "SLEEPING..." << endl;
|
||||
sleep(3);
|
||||
|
||||
// Seek to frame 1
|
||||
r.GetFrame(1);
|
||||
}
|
||||
|
||||
//Player g;
|
||||
|
||||
Reference in New Issue
Block a user