X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Futil.cpp;h=79fae01a8cbb9bcae4b8ed949ff3fe1a8dae64dc;hb=ac7b3acb915f90de6b224e54a2240023fc221e5a;hp=d688b54484b9ce805e01e7eaa4e5c8399190d0e5;hpb=16b26eececbd6932e3381e826eb4e123cb8e2c97;p=casparcg diff --git a/modules/ffmpeg/producer/util.cpp b/modules/ffmpeg/producer/util.cpp index d688b5448..79fae01a8 100644 --- a/modules/ffmpeg/producer/util.cpp +++ b/modules/ffmpeg/producer/util.cpp @@ -2,16 +2,22 @@ #include "util.h" +#include "format/flv.h" + #include #include -#include +#include #include #include #include #include -#include + +#include + +#include +#include #if defined(_MSC_VER) #pragma warning (push) @@ -21,105 +27,47 @@ extern "C" { #include #include + #include } #if defined(_MSC_VER) #pragma warning (pop) #endif -namespace caspar { +namespace caspar { namespace ffmpeg { -core::video_mode::type get_mode(AVFrame& frame) +core::field_mode::type get_mode(AVFrame& frame) { if(!frame.interlaced_frame) - return core::video_mode::progressive; + return core::field_mode::progressive; - return frame.top_field_first ? core::video_mode::upper : core::video_mode::lower; + return frame.top_field_first ? core::field_mode::upper : core::field_mode::lower; } core::pixel_format::type get_pixel_format(PixelFormat pix_fmt) { switch(pix_fmt) { - case PIX_FMT_GRAY8: return core::pixel_format::gray; - case PIX_FMT_BGRA: return core::pixel_format::bgra; - case PIX_FMT_ARGB: return core::pixel_format::argb; - case PIX_FMT_RGBA: return core::pixel_format::rgba; - case PIX_FMT_ABGR: return core::pixel_format::abgr; - case PIX_FMT_YUV444P: return core::pixel_format::ycbcr; - case PIX_FMT_YUV422P: return core::pixel_format::ycbcr; - case PIX_FMT_YUV420P: return core::pixel_format::ycbcr; - case PIX_FMT_YUV411P: return core::pixel_format::ycbcr; - case PIX_FMT_YUV410P: return core::pixel_format::ycbcr; - case PIX_FMT_YUVA420P: return core::pixel_format::ycbcra; - default: return core::pixel_format::invalid; - } -} - -PixelFormat get_ffmpeg_pixel_format(const core::pixel_format_desc& format_desc) -{ - switch(format_desc.pix_fmt) - { - case core::pixel_format::gray: return PIX_FMT_GRAY8; - case core::pixel_format::bgra: return PIX_FMT_BGRA; - case core::pixel_format::argb: return PIX_FMT_ARGB; - case core::pixel_format::rgba: return PIX_FMT_RGBA; - case core::pixel_format::abgr: return PIX_FMT_ABGR; - case core::pixel_format::ycbcra: return PIX_FMT_YUVA420P; - case core::pixel_format::ycbcr: - auto planes = format_desc.planes; - if(planes[0].height == planes[1].height) - { - if(planes[0].width == planes[1].width) - return PIX_FMT_YUV444P; - else if(planes[0].width/2 == planes[1].width) - return PIX_FMT_YUV422P; - else if(planes[0].width/4 == planes[1].width) - return PIX_FMT_YUV411P; - } - if(planes[0].height/2 == planes[1].height) - { - if(planes[0].width/2 == planes[1].width) - return PIX_FMT_YUV420P; - } - if(planes[0].height/4 == planes[1].height) - { - if(planes[0].width/4 == planes[1].width) - return PIX_FMT_YUV410P; - } - } - return PIX_FMT_NONE; -} - -safe_ptr as_av_frame(const safe_ptr& frame) -{ - auto desc = frame->get_pixel_format_desc(); - safe_ptr av_frame(avcodec_alloc_frame(), av_free); - avcodec_get_frame_defaults(av_frame.get()); - - for(size_t n = 0; n < desc.planes.size(); ++n) - { - av_frame->data[n] = frame->image_data(n).begin(); - av_frame->linesize[n] = desc.planes[n].width; - } - - av_frame->format = get_ffmpeg_pixel_format(desc); - av_frame->width = desc.planes[0].width; - av_frame->height = desc.planes[0].height; - - if(frame->get_type() != core::video_mode::progressive) - { - av_frame->interlaced_frame = 1; - av_frame->top_field_first = frame->get_type() == core::video_mode::upper ? 1 : 0; + case CASPAR_PIX_FMT_LUMA: return core::pixel_format::luma; + case PIX_FMT_GRAY8: return core::pixel_format::gray; + case PIX_FMT_BGRA: return core::pixel_format::bgra; + case PIX_FMT_ARGB: return core::pixel_format::argb; + case PIX_FMT_RGBA: return core::pixel_format::rgba; + case PIX_FMT_ABGR: return core::pixel_format::abgr; + case PIX_FMT_YUV444P: return core::pixel_format::ycbcr; + case PIX_FMT_YUV422P: return core::pixel_format::ycbcr; + case PIX_FMT_YUV420P: return core::pixel_format::ycbcr; + case PIX_FMT_YUV411P: return core::pixel_format::ycbcr; + case PIX_FMT_YUV410P: return core::pixel_format::ycbcr; + case PIX_FMT_YUVA420P: return core::pixel_format::ycbcra; + default: return core::pixel_format::invalid; } - - return av_frame; } core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, size_t height) { // Get linesizes AVPicture dummy_pict; - avpicture_fill(&dummy_pict, nullptr, pix_fmt, width, height); + avpicture_fill(&dummy_pict, nullptr, pix_fmt == CASPAR_PIX_FMT_LUMA ? PIX_FMT_GRAY8 : pix_fmt, width, height); core::pixel_format_desc desc; desc.pix_fmt = get_pixel_format(pix_fmt); @@ -127,6 +75,7 @@ core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, switch(desc.pix_fmt) { case core::pixel_format::gray: + case core::pixel_format::luma: { desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0], height, 1)); return desc; @@ -160,13 +109,20 @@ core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, } } -bool try_make_gray(const safe_ptr& frame) +int make_alpha_format(int format) { - auto pix_fmt = get_pixel_format(static_cast(frame->format)); - if(pix_fmt != core::pixel_format::invalid) - frame->format = PIX_FMT_GRAY8; - - return pix_fmt != core::pixel_format::invalid; + switch(get_pixel_format(static_cast(format))) + { + case core::pixel_format::luma: + case core::pixel_format::gray: + case core::pixel_format::invalid: + return format; + case core::pixel_format::ycbcr: + case core::pixel_format::ycbcra: + return CASPAR_PIX_FMT_LUMA; + default: + return PIX_FMT_GRAY8; + } } safe_ptr make_write_frame(const void* tag, const safe_ptr& decoded_frame, const safe_ptr& frame_factory, int hints) @@ -177,11 +133,14 @@ safe_ptr make_write_frame(const void* tag, const safe_ptrheight; auto desc = get_pixel_format_desc(static_cast(decoded_frame->format), width, height); + if(hints & core::frame_producer::ALPHA_HINT) + desc = get_pixel_format_desc(static_cast(make_alpha_format(decoded_frame->format)), width, height); + if(desc.pix_fmt == core::pixel_format::invalid) { auto pix_fmt = static_cast(decoded_frame->format); - auto write = frame_factory->create_frame(tag, desc.pix_fmt != core::pixel_format::invalid ? desc : get_pixel_format_desc(PIX_FMT_BGRA, width, height)); + auto write = frame_factory->create_frame(tag, get_pixel_format_desc(PIX_FMT_BGRA, width, height)); write->set_type(get_mode(*decoded_frame)); std::shared_ptr sws_context; @@ -218,13 +177,10 @@ safe_ptr make_write_frame(const void* tag, const safe_ptrcreate_frame(tag, desc.pix_fmt != core::pixel_format::invalid ? desc : get_pixel_format_desc(PIX_FMT_BGRA, width, height)); + auto write = frame_factory->create_frame(tag, desc); write->set_type(get_mode(*decoded_frame)); - tbb::parallel_for(0, static_cast(desc.planes.size()), 1, [&](int n) + for(int n = 0; n < static_cast(desc.planes.size()); ++n) { auto plane = desc.planes[n]; auto result = write->image_data(n).begin(); @@ -232,17 +188,103 @@ safe_ptr make_write_frame(const void* tag, const safe_ptrlinesize[n]; // Copy line by line since ffmpeg sometimes pads each line. + tbb::affinity_partitioner ap; tbb::parallel_for(tbb::blocked_range(0, static_cast(desc.planes[n].height)), [&](const tbb::blocked_range& r) { for(size_t y = r.begin(); y != r.end(); ++y) - fast_memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize); - }); + memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize); + }, ap); write->commit(n); - }); - + } + return write; } } -} \ No newline at end of file +bool is_sane_fps(AVRational time_base) +{ + double fps = static_cast(time_base.den) / static_cast(time_base.num); + return fps > 20.0 && fps < 65.0; +} + +void fix_meta_data(AVFormatContext& context) +{ + auto video_index = av_find_best_stream(&context, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0); + auto audio_index = av_find_best_stream(&context, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0); + + if(video_index < 0) + return; + + auto& video_context = *context.streams[video_index]->codec; + auto& video_stream = *context.streams[video_index]; + + if(boost::filesystem2::path(context.filename).extension() == ".flv") + { + try + { + auto meta = read_flv_meta_info(context.filename); + double fps = boost::lexical_cast(meta["framerate"]); + video_context.time_base.num = 1000000; + video_context.time_base.den = static_cast(fps*1000000.0); + video_stream.nb_frames = static_cast(boost::lexical_cast(meta["duration"])*fps); + } + catch(...){} + } + else + { + if(video_stream.nb_frames == 0) + video_stream.nb_frames = video_stream.duration; + + if(!is_sane_fps(video_context.time_base)) + { + if(video_context.time_base.num == 1) + video_context.time_base.num = static_cast(std::pow(10.0, static_cast(std::log10(static_cast(video_context.time_base.den)))-1)); + + if(!is_sane_fps(video_context.time_base) && audio_index > -1) + { + auto& audio_context = *context.streams[audio_index]->codec; + auto& audio_stream = *context.streams[audio_index]; + + double duration_sec = audio_stream.duration / static_cast(audio_context.sample_rate); + + video_context.time_base.num = static_cast(duration_sec*100000.0); + video_context.time_base.den = static_cast(video_stream.nb_frames*100000); + } + } + + if(audio_index > -1) // Check for invalid double frame-rate + { + auto& audio_context = *context.streams[audio_index]->codec; + auto& audio_stream = *context.streams[audio_index]; + + double duration_sec = audio_stream.duration / static_cast(audio_context.sample_rate); + double fps = static_cast(video_context.time_base.den) / static_cast(video_context.time_base.num); + + double fps_nb_frames = static_cast(duration_sec*fps); + double stream_nb_frames = static_cast(video_stream.nb_frames); + double diff = std::abs(fps_nb_frames - stream_nb_frames*2.0); + if(diff < fps_nb_frames*0.05) + video_context.time_base.num *= 2; + } + } + + double fps = static_cast(video_context.time_base.den) / static_cast(video_context.time_base.num); + + double closest_fps = 0.0; + for(int n = 0; n < core::video_format::count; ++n) + { + auto format = core::video_format_desc::get(static_cast(n)); + + double diff1 = std::abs(format.fps - fps); + double diff2 = std::abs(closest_fps - fps); + + if(diff1 < diff2) + closest_fps = format.fps; + } + + video_context.time_base.num = 1000000; + video_context.time_base.den = static_cast(closest_fps*1000000.0); +} + +}} \ No newline at end of file