From bc4e58c601efc620933e72df2b7c16a4ec5a5c72 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 29 Mar 2014 15:39:43 -0500 Subject: [PATCH] 1) Reduced all openmp to use /2 the available CPUs (for performance reasons) 2) Improved detection of pixel format (enabled GIF support.. although it is still flawed a bit) 3) Improved error reporting when video encoding issues happen --- include/FFmpegUtilities.h | 2 ++ src/FFmpegReader.cpp | 4 +-- src/FFmpegWriter.cpp | 61 +++++++++++++++++++++++++++++++-------- src/Main.cpp | 18 +++++++----- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/include/FFmpegUtilities.h b/include/FFmpegUtilities.h index abb7310e..e33e88f9 100644 --- a/include/FFmpegUtilities.h +++ b/include/FFmpegUtilities.h @@ -40,6 +40,8 @@ #include #include #include + #include + #include // libavutil changed folders at some point #if LIBAVFORMAT_VERSION_MAJOR >= 53 diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 10175c65..ef853622 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -159,7 +159,7 @@ void FFmpegReader::Open() throw(InvalidFile, NoStreamsFound, InvalidCodec) pCodecCtx = pFormatCtx->streams[videoStream]->codec; // Set number of threads equal to number of processors + 1 - pCodecCtx->thread_count = omp_get_num_procs(); + pCodecCtx->thread_count = omp_get_num_procs() / 2; // Find the decoder for the video stream AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id); @@ -188,7 +188,7 @@ void FFmpegReader::Open() throw(InvalidFile, NoStreamsFound, InvalidCodec) aCodecCtx = pFormatCtx->streams[audioStream]->codec; // Set number of threads equal to number of processors + 1 - aCodecCtx->thread_count = omp_get_num_procs(); + aCodecCtx->thread_count = omp_get_num_procs() / 2; // Find the decoder for the audio stream AVCodec *aCodec = avcodec_find_decoder(aCodecCtx->codec_id); diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 6d80de74..4c864687 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -341,7 +341,7 @@ void FFmpegWriter::write_queued_frames() spooled_video_frames.clear(); spooled_audio_frames.clear(); - omp_set_num_threads(4); + omp_set_num_threads(omp_get_num_procs() / 2); omp_set_nested(true); #pragma omp parallel { @@ -410,10 +410,9 @@ void FFmpegWriter::write_queued_frames() // Get AVFrame AVFrame *av_frame = av_frames[frame]; - // deallocate AVFrame + // deallocate AVPicture and AVFrame av_free(av_frame->data[0]); av_free(av_frame); - av_frames.erase(frame); } @@ -812,21 +811,49 @@ AVStream* FFmpegWriter::add_video_stream() c->time_base.num = info.video_timebase.num; c->time_base.den = info.video_timebase.den; c->gop_size = 12; /* TODO: add this to "info"... emit one intra frame every twelve frames at most */ - c->pix_fmt = PIX_FMT_YUV420P; - if (c->codec_id == CODEC_ID_MPEG2VIDEO) { + if (c->codec_id == CODEC_ID_MPEG2VIDEO) /* just for testing, we also add B frames */ c->max_b_frames = 2; - } - if (c->codec_id == CODEC_ID_MPEG1VIDEO) { + if (c->codec_id == CODEC_ID_MPEG1VIDEO) /* Needed to avoid using macroblocks in which some coeffs overflow. This does not happen with normal video, it just happens here as the motion of the chroma plane does not match the luma plane. */ c->mb_decision = 2; - } // some formats want stream headers to be separate if (oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= CODEC_FLAG_GLOBAL_HEADER; + // Find all supported pixel formats for this codec + const PixelFormat* supported_pixel_formats = codec->pix_fmts; + while (supported_pixel_formats != NULL && *supported_pixel_formats != PIX_FMT_NONE) { + cout << "supported pixel format: " << av_get_pix_fmt_name(*supported_pixel_formats) << endl; + // Assign the 1st valid pixel format (if one is missing) + if (c->pix_fmt == PIX_FMT_NONE) + c->pix_fmt = *supported_pixel_formats; + ++supported_pixel_formats; + } + + // Codec doesn't have any pix formats? + if (c->pix_fmt == PIX_FMT_NONE) { + if(fmt->video_codec == CODEC_ID_RAWVIDEO) { + // Raw video should use RGB24 + c->pix_fmt = PIX_FMT_RGB24; + // Set raw picture flag (so we don't encode this video) + oc->oformat->flags |= AVFMT_RAWPICTURE; + } else { + // Set the default codec + c->pix_fmt = PIX_FMT_YUV420P; + } + } + + // Override Gif Support (TODO: Find a better way to accomplish this) + if (c->codec_id == CODEC_ID_GIF) { + // Force rgb24 which seems to be required for GIF + c->pix_fmt = PIX_FMT_RGB24; + // Set raw picture flag (so we don't encode the image) + oc->oformat->flags |= AVFMT_RAWPICTURE; + } + return st; } @@ -837,7 +864,7 @@ void FFmpegWriter::open_audio(AVFormatContext *oc, AVStream *st) audio_codec = st->codec; // Set number of threads equal to number of processors + 1 - audio_codec->thread_count = omp_get_num_procs(); + audio_codec->thread_count = omp_get_num_procs() / 2; // Find the audio encoder codec = avcodec_find_encoder(audio_codec->codec_id); @@ -888,7 +915,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) video_codec = st->codec; // Set number of threads equal to number of processors + 1 - video_codec->thread_count = omp_get_num_procs(); + video_codec->thread_count = omp_get_num_procs() / 2; /* find the video encoder */ codec = avcodec_find_encoder(video_codec->codec_id); @@ -1166,15 +1193,24 @@ void FFmpegWriter::process_video_packet(tr1::shared_ptr frame) frame_source = allocate_avframe(PIX_FMT_RGB24, source_image_width, source_image_height, &bytes_source); AVFrame *frame_final = allocate_avframe(video_codec->pix_fmt, info.width, info.height, &bytes_final); + // Determine how many colors we are copying (3 or 4) + int step = 3; // rgb + if ( video_st->codec->pix_fmt == PIX_FMT_RGBA || video_st->codec->pix_fmt == PIX_FMT_ARGB || video_st->codec->pix_fmt == PIX_FMT_BGRA ) + step = 4; // rgba + // Fill the AVFrame with RGB image data int source_total_pixels = source_image_width * source_image_height; - for (int packet = 0, row = 0; packet < source_total_pixels; packet++, row+=3) + for (int packet = 0, row = 0; packet < source_total_pixels; packet++, row+=step) { // Update buffer (which is already linked to the AVFrame: pFrameRGB) // Each color needs to be 8 bit (so I'm bit shifting the 16 bit ints) frame_source->data[0][row] = pixel_packets[packet].red >> 8; frame_source->data[0][row+1] = pixel_packets[packet].green >> 8; frame_source->data[0][row+2] = pixel_packets[packet].blue >> 8; + + // Copy alpha channel (if needed) + if (step == 4) + frame_source->data[0][row+3] = pixel_packets[packet].opacity >> 8; } // Resize & convert pixel format @@ -1203,7 +1239,7 @@ void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* fra pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index= video_st->index; - pkt.data= (uint8_t *)frame_final; + pkt.data= (uint8_t*)frame_final; pkt.size= sizeof(AVPicture); // Increment PTS (in frames and scaled to the codec's timebase) @@ -1215,6 +1251,7 @@ void FFmpegWriter::write_video_packet(tr1::shared_ptr frame, AVFrame* fra if (error_code != 0) { string error_description = av_err2str(error_code); + cout << "error: " << error_code << ": " << error_description << endl; throw ErrorEncodingVideo("Error while writing raw video frame", frame->number); } diff --git a/src/Main.cpp b/src/Main.cpp index b23b3196..013fe0f2 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -40,6 +40,8 @@ using namespace tr1; int main(int argc, char* argv[]) { + /* + FFmpegReader sinelReader("/home/jonathan/Videos/sintel_trailer-720p.mp4"); sinelReader.Open(); @@ -89,6 +91,8 @@ int main(int argc, char* argv[]) //c1.GetFrame(150)->Save("test.bmp", 1.0); return 0; + */ + // SDLPlayer p; // p.Reader(&r2); @@ -112,12 +116,12 @@ int main(int argc, char* argv[]) // Reader - FFmpegReader r1("/home/jonathan/colors-24-converted-to-29-97-fps-pulldown-advanced.mp4"); + FFmpegReader r1("/home/jonathan/Videos/New_OpenShot_Timeline.webm"); r1.Open(); // FrameMapper - FrameMapper r(&r1, Fraction(24,1), PULLDOWN_ADVANCED); - r.PrintMapping(); + //FrameMapper r(&r1, Fraction(24,1), PULLDOWN_ADVANCED); + //r.PrintMapping(); /* WRITER ---------------- */ FFmpegWriter w("/home/jonathan/output.mp4"); @@ -126,7 +130,7 @@ int main(int argc, char* argv[]) //w.SetAudioOptions(true, "libvorbis", 48000, 2, 188000); //w.SetAudioOptions(true, "libmp3lame", 44100, 2, 128000); //w.SetVideoOptions(true, "libvpx", Fraction(24,1), 1280, 720, Fraction(1,1), false, false, 30000000); - w.SetVideoOptions(true, "mpeg4", r.info.fps, 1280, 720, Fraction(1,1), false, false, 3000000); + w.SetVideoOptions(true, "mpeg4", openshot::Fraction(3,1), 720, 360, Fraction(1,1), false, false, 3000000); // Prepare Streams w.PrepareStreams(); @@ -138,7 +142,7 @@ int main(int argc, char* argv[]) w.OutputStreamInfo(); //for (int frame = 3096; frame <= 3276; frame++) - for (int frame = 1; frame <= 20; frame++) + for (int frame = 1; frame <= 100; frame++) { // tr1::shared_ptr f(new Frame(frame, 1280, 720, "#000000", 44100, 2)); // if (frame % 2 == 0) @@ -150,7 +154,7 @@ int main(int argc, char* argv[]) // cout << f->number << endl; // w.WriteFrame(f); - tr1::shared_ptr f = r.GetFrame(frame); + tr1::shared_ptr f = r1.GetFrame(frame); if (f) { //if (frame >= 250) @@ -159,7 +163,7 @@ int main(int argc, char* argv[]) //f->Display(); // Write frame - f->Display(); + //f->Display(); cout << "queue frame " << frame << " (" << f->number << ", " << f << ")" << endl; w.WriteFrame(f); }