diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 86670cbd..a7699353 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -31,7 +31,7 @@ FFmpegWriter::FFmpegWriter(string path) throw (InvalidFile, InvalidFormat, Inval path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), audio_pts(0), video_pts(0), samples(NULL), audio_outbuf(NULL), audio_outbuf_size(0), audio_input_frame_size(0), audio_input_position(0), initial_audio_input_frame_size(0), resampler(NULL), img_convert_ctx(NULL), cache_size(8), num_of_rescalers(32), - rescaler_position(0), video_codec(NULL), audio_codec(NULL), is_writing(false), write_video_count(0), write_audio_count(0) + rescaler_position(0), video_codec(NULL), audio_codec(NULL), is_writing(false), write_video_count(1), write_audio_count(1) { // Init FileInfo struct (clear all values) @@ -440,16 +440,17 @@ void FFmpegWriter::WriteTrailer() // ignore the final frames. if (last_frame) { + // Flush remaining packets + //av_write_frame(oc, NULL); + av_interleaved_write_frame(oc, NULL); + // Create black frame - tr1::shared_ptr padding_frame(new Frame(999999, last_frame->GetWidth(), last_frame->GetHeight(), "#000000", last_frame->GetAudioSamplesCount(), last_frame->GetAudioChannelsCount())); - padding_frame->AddColor(last_frame->GetWidth(), last_frame->GetHeight(), "#000000"); + //tr1::shared_ptr padding_frame(new Frame(999999, last_frame->GetWidth(), last_frame->GetHeight(), "#000000", last_frame->GetAudioSamplesCount(), last_frame->GetAudioChannelsCount())); + //padding_frame->AddColor(last_frame->GetWidth(), last_frame->GetHeight(), "#000000"); // Add the black frame many times //for (int p = 0; p < 25; p++) // WriteFrame(padding_frame); - - // Flush remaining packets - av_write_frame(oc, NULL); } // Write any remaining queued frames to video file @@ -506,8 +507,8 @@ void FFmpegWriter::Close() } // Reset frame counters - write_video_count = 0; - write_audio_count = 0; + write_video_count = 1; + write_audio_count = 1; // Free the stream av_free(oc); @@ -546,6 +547,9 @@ AVStream* FFmpegWriter::add_audio_stream() if (!st) throw OutOfMemory("Could not allocate memory for the audio stream.", path); + // Set default values + avcodec_get_context_defaults3(st->codec, codec); + c = st->codec; c->codec_id = codec->id; c->codec_type = AVMEDIA_TYPE_AUDIO; @@ -555,11 +559,11 @@ AVStream* FFmpegWriter::add_audio_stream() c->channels = info.channels; // Check for valid timebase - if (c->time_base.den == 0 || c->time_base.num == 0) - { - c->time_base.num = st->time_base.num; - c->time_base.den = st->time_base.den; - } +// if (c->time_base.den == 0 || c->time_base.num == 0) +// { +// c->time_base.num = st->time_base.num; +// c->time_base.den = st->time_base.den; +// } // Set valid sample rate (or throw error) if (codec->supported_samplerates) { @@ -632,6 +636,9 @@ AVStream* FFmpegWriter::add_video_stream() if (!st) throw OutOfMemory("Could not allocate memory for the video stream.", path); + // Set default values + avcodec_get_context_defaults3(st->codec, codec); + c = st->codec; c->codec_id = codec->id; c->codec_type = AVMEDIA_TYPE_VIDEO; @@ -773,7 +780,6 @@ void FFmpegWriter::write_audio_packets() channels_in_frame = frame->GetAudioChannelsCount(); // Get audio sample array - //float* frame_samples_float = new float(total_frame_samples); float* frame_samples_float = frame->GetInterleavedAudioSamples(info.sample_rate, new_sampler, &samples_in_frame); // Calculate total samples @@ -799,7 +805,7 @@ void FFmpegWriter::write_audio_packets() int samples_position = 0; - // Re-sample audio samples (into additinal channels or changing the sample format / number format) + // Re-sample audio samples (into additional channels or changing the sample format / number format) // The sample rate has already been resampled using the GetInterleavedAudioSamples method. if (audio_codec->sample_fmt != AV_SAMPLE_FMT_S16 || info.channels != channels_in_frame) { @@ -857,33 +863,66 @@ void FFmpegWriter::write_audio_packets() // Not enough samples to encode... so wait until the next frame break; + + // Increment PTS (in samples and scaled to the codec's timebase) + write_audio_count += av_rescale_q(audio_codec->frame_size, AVRational{1, info.sample_rate}, audio_codec->time_base); + + // Create AVFrame (and fill it with samples) + AVFrame *frame_final = avcodec_alloc_frame(); + frame_final->nb_samples = audio_codec->frame_size; + frame_final->pts = write_audio_count; // Set the AVFrame's PTS + avcodec_fill_audio_frame(frame_final, audio_codec->channels, audio_codec->sample_fmt, (uint8_t *) samples, + audio_input_position * av_get_bytes_per_sample(audio_codec->sample_fmt) * audio_codec->channels, 0); + // Init the packet AVPacket pkt; av_init_packet(&pkt); + pkt.data = NULL; + pkt.size = 0; - // Increment counter, and set AVFrame PTS - write_audio_count++; + // Set the packet's PTS prior to encoding + pkt.pts = pkt.dts = write_audio_count; - // Encode audio data - pkt.size = avcodec_encode_audio(audio_codec, audio_outbuf, audio_outbuf_size, (short *) samples); + /* encode the audio samples */ + int got_packet_ptr = 0; + int error_code = avcodec_encode_audio2(audio_codec, &pkt, frame_final, &got_packet_ptr); - if (audio_codec->coded_frame && audio_codec->coded_frame->pts != AV_NOPTS_VALUE) - // Set the correct rescaled timestamp - pkt.pts = av_rescale_q(audio_codec->coded_frame->pts, audio_codec->time_base, audio_st->time_base); - pkt.flags |= AV_PKT_FLAG_KEY; - pkt.stream_index = audio_st->index; - pkt.data = audio_outbuf; + /* if zero size, it means the image was buffered */ + if (error_code == 0 && got_packet_ptr) { - /* write the compressed frame in the media file */ - int averror = 0; - averror = av_interleaved_write_frame(oc, &pkt); - if (averror != 0) - { - string error_description = av_err2str(averror); - cout << "error: " << averror << ": " << error_description << endl; - throw ErrorEncodingAudio("Error while writing audio frame", -1); + // Since the PTS can change during encoding, set the value again. This seems like a huge hack, + // but it fixes lots of PTS related issues when I do this. + pkt.pts = pkt.dts = write_audio_count; + + // Scale the PTS to the audio stream timebase (which is sometimes different than the codec's timebase) + if (pkt.pts != AV_NOPTS_VALUE) + pkt.pts = av_rescale_q(pkt.pts, audio_codec->time_base, audio_st->time_base); + if (pkt.dts != AV_NOPTS_VALUE) + pkt.dts = av_rescale_q(pkt.dts, audio_codec->time_base, audio_st->time_base); + if (pkt.duration > 0) + pkt.duration = av_rescale_q(pkt.duration, audio_codec->time_base, audio_st->time_base); + + // set stream + pkt.stream_index = audio_st->index; + pkt.flags |= AV_PKT_FLAG_KEY; + + /* write the compressed frame in the media file */ + int averror = av_interleaved_write_frame(oc, &pkt); + if (averror != 0) + { + string error_description = av_err2str(averror); + cout << "error: " << averror << ": " << error_description << endl; + throw ErrorEncodingAudio("Error while writing compressed audio frame", write_audio_count); + } } + if (error_code < 0) + cout << "Error encoding audio: " << error_code << endl; + + // deallocate AVFrame + //av_free(frame_final->data[0]); + av_free(frame_final); + // deallocate memory for packet av_free_packet(&pkt); @@ -1008,6 +1047,10 @@ void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* fra pkt.data= (uint8_t *)frame_final; pkt.size= sizeof(AVPicture); + // Increment PTS (1 per frame) + write_video_count += 1; + pkt.pts = write_video_count; + /* write the compressed frame in the media file */ int averror = 0; averror = av_interleaved_write_frame(oc, &pkt); @@ -1026,9 +1069,10 @@ void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* fra av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; + pkt.pts = pkt.dts = AV_NOPTS_VALUE; - // Increment video write counter - write_video_count++; + // Increment PTS (in frames and scaled to the codec's timebase) + write_video_count += av_rescale_q(1, AVRational{info.fps.den, info.fps.num}, video_codec->time_base); // Assign the initial AVFrame PTS from the frame counter frame_final->pts = write_video_count; @@ -1043,13 +1087,11 @@ void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* fra // set the timestamp if (pkt.pts != AV_NOPTS_VALUE) pkt.pts = av_rescale_q(pkt.pts, video_codec->time_base, video_st->time_base); - //if (pkt.dts != AV_NOPTS_VALUE) - // pkt.dts = av_rescale_q(pkt.dts, video_codec->time_base, video_st->time_base); - //pkt.pts = pkt.dts = AV_NOPTS_VALUE; - -// pkt.stream_index = video_st->index; -// pkt.pts = av_rescale_q(write_video_count, video_codec->time_base, video_st->time_base); -// pkt.dts = AV_NOPTS_VALUE; + if (pkt.dts != AV_NOPTS_VALUE) + pkt.dts = av_rescale_q(pkt.dts, video_codec->time_base, video_st->time_base); + if (pkt.duration > 0) + pkt.duration = av_rescale_q(pkt.duration, video_codec->time_base, video_st->time_base); + pkt.stream_index = video_st->index; /* write the compressed frame in the media file */ //int averror = av_write_frame(oc, &pkt); diff --git a/src/Frame.cpp b/src/Frame.cpp index 812868ee..3c9279c0 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -243,7 +243,7 @@ void Frame::ClearWaveform() const Magick::PixelPacket* Frame::GetWaveformPixels(int width, int height) { // Get audio wave form image - tr1::shared_ptr wave_image = GetWaveform(width, height); + wave_image = GetWaveform(width, height); // Return array of pixel packets return wave_image->getConstPixels(0,0, wave_image->columns(), wave_image->rows()); diff --git a/src/Main.cpp b/src/Main.cpp index 3566c673..87ed6f30 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -16,119 +16,101 @@ void FrameReady(int number) int main() { -// Keyframe time; -// //time.AddPoint(1, 300); -// time.AddPoint(1, 500, LINEAR); -// time.AddPoint(400, 100); -// time.AddPoint(500, 500); -// time.PrintValues(); +// // Create timeline +// Timeline t(640, 360, Framerate(24,1)); // -// return 0; - - -// openshot::FFmpegReader r1("/home/jonathan/Videos/sintel-1024-stereo.mp4"); -// r1.Open(); -// FrameMapper map(&r1, Framerate(30,1), PULLDOWN_NONE); -// map.PrintMapping(); +// // Add some clips +// Clip c1(new FFmpegReader("/home/jonathan/Videos/sintel_trailer-720p.mp4")); +// c1.Position(0.0); // -// return 0; - - - // Create timeline - Timeline t(640, 360, Framerate(24,1)); - - // Add some clips - Clip c1(new FFmpegReader("/home/jonathan/Videos/sintel_trailer-720p.mp4")); - c1.Position(0.0); - - // LINEAR Reverse - //c1.time.AddPoint(1, 500, LINEAR); - //c1.time.AddPoint(500, 1, LINEAR); - - // LINEAR Slow Reverse (sounds wavy, due to periodic repeated frames) - //c1.time.AddPoint(1, 500, LINEAR); - //c1.time.AddPoint(500, 100, LINEAR); - - // LINEAR Slow Reverse X2 (smooth) - c1.time.AddPoint(1, 500, LINEAR); - c1.time.AddPoint(500, 250, LINEAR); - - // LINEAR Fast Reverse (sounds wavy, due to periodic repeated frames) - //c1.time.AddPoint(1, 600, LINEAR); - //c1.time.AddPoint(500, 1, LINEAR); - - // LINEAR Slow Forward - //c1.time.AddPoint(1, 1000, LINEAR); - //c1.time.AddPoint(500, 1, LINEAR); - -// // BEZIER Reverse -// openshot::Point p1(1,500); -// p1.handle_left = Coordinate(1,500); -// p1.handle_right = Coordinate(250,500); +// // LINEAR Reverse +// //c1.time.AddPoint(1, 500, LINEAR); +// //c1.time.AddPoint(500, 1, LINEAR); // -// openshot::Point p2(500,100); -// p1.handle_left = Coordinate(500,350); -// p1.handle_right = Coordinate(500,100); +// // LINEAR Slow Reverse (sounds wavy, due to periodic repeated frames) +// //c1.time.AddPoint(1, 500, LINEAR); +// //c1.time.AddPoint(500, 100, LINEAR); // -// c1.time.AddPoint(p1); -// c1.time.AddPoint(p2); - +// // LINEAR Slow Reverse X2 (smooth) // c1.time.AddPoint(1, 500, LINEAR); -// c1.time.AddPoint(500, 1, LINEAR); -// c1.time.AddPoint(1, 500, LINEAR); -// c1.time.AddPoint(200, 200); -// c1.time.AddPoint(500, 500, LINEAR); - c1.time.PrintValues(); - - // Add clips - t.AddClip(&c1); - - - // Create a writer - FFmpegWriter w("/home/jonathan/output.webm"); - w.DisplayInfo(); - - // Set options - //w.SetAudioOptions(true, "libmp3lame", 44100, 2, 128000, false); - w.SetAudioOptions(true, "libvorbis", 44100, 2, 128000, false); - w.SetVideoOptions(true, "libvpx", Fraction(24, 1), 640, 360, Fraction(1,1), false, false, 2000000); - - // Prepare Streams - w.PrepareStreams(); - - // Write header - w.WriteHeader(); - - // Output stream info - w.OutputStreamInfo(); - - for (int frame = 1; frame <= 500; frame++) - { - tr1::shared_ptr f = t.GetFrame(frame); - if (f) - { - //f->AddOverlayNumber(0); - //if (frame >= 1 && frame <= 22) - // f->DisplayWaveform(); - - // Write frame - //cout << "queue frame " << frame << endl; - cout << "queue frame " << frame << " (" << f->number << ", " << f << ")" << endl; - w.WriteFrame(f); - } - } - - // Write Footer - w.WriteTrailer(); - - // Close writer & reader - w.Close(); - - // Close timeline - t.Close(); - - cout << "Successfully Finished Timeline DEMO" << endl; - return 0; +// c1.time.AddPoint(500, 250, LINEAR); +// +// // LINEAR Fast Reverse (sounds wavy, due to periodic repeated frames) +// //c1.time.AddPoint(1, 600, LINEAR); +// //c1.time.AddPoint(500, 1, LINEAR); +// +// // LINEAR Slow Forward +// //c1.time.AddPoint(1, 1000, LINEAR); +// //c1.time.AddPoint(500, 1, LINEAR); +// +//// // BEZIER Reverse +//// openshot::Point p1(1,500); +//// p1.handle_left = Coordinate(1,500); +//// p1.handle_right = Coordinate(250,500); +//// +//// openshot::Point p2(500,100); +//// p1.handle_left = Coordinate(500,350); +//// p1.handle_right = Coordinate(500,100); +//// +//// c1.time.AddPoint(p1); +//// c1.time.AddPoint(p2); +// +//// c1.time.AddPoint(1, 500, LINEAR); +//// c1.time.AddPoint(500, 1, LINEAR); +//// c1.time.AddPoint(1, 500, LINEAR); +//// c1.time.AddPoint(200, 200); +//// c1.time.AddPoint(500, 500, LINEAR); +// c1.time.PrintValues(); +// +// // Add clips +// t.AddClip(&c1); +// +// +// // Create a writer +// FFmpegWriter w("/home/jonathan/output.webm"); +// w.DisplayInfo(); +// +// // Set options +// //w.SetAudioOptions(true, "libmp3lame", 44100, 2, 128000, false); +// w.SetAudioOptions(true, "libvorbis", 44100, 2, 128000, false); +// w.SetVideoOptions(true, "libvpx", Fraction(24, 1), 640, 360, Fraction(1,1), false, false, 2000000); +// +// // Prepare Streams +// w.PrepareStreams(); +// +// // Write header +// w.WriteHeader(); +// +// // Output stream info +// w.OutputStreamInfo(); +// +// for (int frame = 1; frame <= 500; frame++) +// { +// tr1::shared_ptr f = t.GetFrame(frame); +// if (f) +// { +// //f->AddOverlayNumber(0); +// //if (frame >= 1 && frame <= 22) +// // f->DisplayWaveform(); +// +// // Write frame +// //cout << "queue frame " << frame << endl; +// cout << "queue frame " << frame << " (" << f->number << ", " << f << ")" << endl; +// w.WriteFrame(f); +// } +// } +// +// // Write Footer +// w.WriteTrailer(); +// +// // Close writer & reader +// w.Close(); +// +// // Close timeline +// t.Close(); +// +// cout << "Successfully Finished Timeline DEMO" << endl; +// return 0; @@ -167,7 +149,7 @@ int main() // openshot::FFmpegReader r("../../src/examples/piano.wav"); // openshot::FFmpegReader r("/home/jonathan/Videos/big-buck-bunny_trailer.webm"); - openshot::FFmpegReader r1("/home/jonathan/Videos/sintel-1024-stereo.mp4"); + openshot::FFmpegReader r("/home/jonathan/Desktop/test2.flv"); // openshot::FFmpegReader r("/home/jonathan/Videos/OpenShot_Now_In_3d.mp4"); // openshot::FFmpegReader r("/home/jonathan/Videos/sintel_trailer-720p.mp4"); // openshot::FFmpegReader r("/home/jonathan/Aptana Studio Workspace/OpenShotLibrary/src/examples/piano.wav"); @@ -177,65 +159,78 @@ int main() // openshot::FFmpegReader r("/home/jonathan/Aptana Studio Workspace/OpenShotLibrary/src/examples/asdf.wdf"); -// openshot::FrameMapper r(&r1, Framerate(30,1), PULLDOWN_CLASSIC); -// -// // Display debug info -// r.Open(); -// r.DisplayInfo(); -// -// // Create a writer -// FFmpegWriter w("/home/jonathan/output.webm"); -// w.DisplayInfo(); -// -// // Set options -// w.SetAudioOptions(true, "libvorbis", 44100, 2, 128000, false); -// w.SetVideoOptions(true, "libvpx", Fraction(30, 1), 640, 360, Fraction(1,1), false, false, 2000000); -// -// // Prepare Streams -// w.PrepareStreams(); -// -// // Set Options -//// w.SetOption(VIDEO_STREAM, "quality", "good"); -//// w.SetOption(VIDEO_STREAM, "g", "120"); -//// w.SetOption(VIDEO_STREAM, "qmin", "11"); -//// w.SetOption(VIDEO_STREAM, "qmax", "51"); -//// w.SetOption(VIDEO_STREAM, "profile", "0"); -//// w.SetOption(VIDEO_STREAM, "speed", "0"); -//// w.SetOption(VIDEO_STREAM, "level", "216"); -//// w.SetOption(VIDEO_STREAM, "rc_lookahead", "16"); -//// w.SetOption(VIDEO_STREAM, "rc_min_rate", "100000"); -//// w.SetOption(VIDEO_STREAM, "rc_max_rate", "24000000"); -//// w.SetOption(VIDEO_STREAM, "slices", "4"); -//// w.SetOption(VIDEO_STREAM, "arnr_max_frames", "7"); -//// w.SetOption(VIDEO_STREAM, "arnr_strength", "5"); -//// w.SetOption(VIDEO_STREAM, "arnr_type", "3"); -// -// // Write header -// w.WriteHeader(); -// -// // Output stream info -// w.OutputStreamInfo(); -// -// //Frame *f = r.GetFrame(1); -// -// //for (int frame = 131; frame >= 1; frame--) -// for (int frame = 1; frame <= 500; frame++) + + // Display debug info + r.Open(); + r.DisplayInfo(); + +// for (int frame = 1; frame <= 300; frame++) // { // tr1::shared_ptr f = r.GetFrame(frame); -// //f->AddOverlayNumber(0); -// //f->Display(); -// -// // Write frame -// cout << "queue frame " << frame << endl; -// w.WriteFrame(f); // } -// -// // Write Footer -// w.WriteTrailer(); -// -// // Close writer & reader -// w.Close(); -// r.Close(); +// return 0; + + + + + + // Create a writer + FFmpegWriter w("/home/jonathan/output.mp4"); + w.DisplayInfo(); + + // Set options + //w.SetAudioOptions(true, "libvorbis", 44100, 2, 128000, false); + w.SetAudioOptions(true, "libmp3lame", 44100, 2, 128000, false); + + //w.SetVideoOptions(true, "libvpx", Fraction(25,1), 320, 240, Fraction(1,1), false, false, 2000000); + w.SetVideoOptions(true, "libx264", Fraction(25,1), 320, 240, Fraction(1,1), false, false, 2000000); + //w.SetVideoOptions(true, "libtheora", Fraction(25,1), 320, 240, Fraction(1,1), false, false, 2000000); + + // Prepare Streams + w.PrepareStreams(); + + // Set Options +// w.SetOption(VIDEO_STREAM, "quality", "good"); +// w.SetOption(VIDEO_STREAM, "g", "120"); +// w.SetOption(VIDEO_STREAM, "qmin", "11"); +// w.SetOption(VIDEO_STREAM, "qmax", "51"); +// w.SetOption(VIDEO_STREAM, "profile", "0"); +// w.SetOption(VIDEO_STREAM, "speed", "0"); +// w.SetOption(VIDEO_STREAM, "level", "216"); +// w.SetOption(VIDEO_STREAM, "rc_lookahead", "16"); +// w.SetOption(VIDEO_STREAM, "rc_min_rate", "100000"); +// w.SetOption(VIDEO_STREAM, "rc_max_rate", "24000000"); +// w.SetOption(VIDEO_STREAM, "slices", "4"); +// w.SetOption(VIDEO_STREAM, "arnr_max_frames", "7"); +// w.SetOption(VIDEO_STREAM, "arnr_strength", "5"); +// w.SetOption(VIDEO_STREAM, "arnr_type", "3"); + + // Write header + w.WriteHeader(); + + // Output stream info + w.OutputStreamInfo(); + + //Frame *f = r.GetFrame(1); + + //for (int frame = 131; frame >= 1; frame--) + for (int frame = 1; frame <= 300; frame++) + { + tr1::shared_ptr f = r.GetFrame(frame); + //f->AddOverlayNumber(0); + //f->Display(); + + // Write frame + cout << "queue frame " << frame << endl; + w.WriteFrame(f); + } + + // Write Footer + w.WriteTrailer(); + + // Close writer & reader + w.Close(); + r.Close(); cout << "Successfully executed Main.cpp!" << endl;