X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Futil%2Futil.cpp;h=82f5e661ea81e9dc48a3950e2404ac91df2fa730;hb=435cf4b385c5099270bee44f89c3e2615af30521;hp=7b81fcfb69aa3f8f3829d7bd176dcd9c2f7603a2;hpb=acdd8abe7f0f1897db2e51536d683706f46d7712;p=casparcg diff --git a/modules/ffmpeg/producer/util/util.cpp b/modules/ffmpeg/producer/util/util.cpp index 7b81fcfb6..82f5e661e 100644 --- a/modules/ffmpeg/producer/util/util.cpp +++ b/modules/ffmpeg/producer/util/util.cpp @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2011 Sveriges Television AB +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Robert Nagy, ronag89@gmail.com +*/ + #include "../../stdafx.h" #include "util.h" @@ -10,20 +31,21 @@ #include #include -#include -#include +#include +#include +#include #include -#include -#include -#include -#include +#include #include +#include #include #include +#include + #if defined(_MSC_VER) #pragma warning (push) #pragma warning (disable : 4244) @@ -64,7 +86,7 @@ std::shared_ptr empty_video() return video; } -core::field_mode::type get_mode(const AVFrame& frame) +core::field_mode get_mode(const AVFrame& frame) { if(!frame.interlaced_frame) return core::field_mode::progressive; @@ -72,7 +94,7 @@ core::field_mode::type get_mode(const AVFrame& frame) return frame.top_field_first ? core::field_mode::upper : core::field_mode::lower; } -core::pixel_format::type get_pixel_format(PixelFormat pix_fmt) +core::pixel_format get_pixel_format(PixelFormat pix_fmt) { switch(pix_fmt) { @@ -92,16 +114,15 @@ core::pixel_format::type get_pixel_format(PixelFormat pix_fmt) } } -core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, size_t height) +core::pixel_format_desc pixel_format_desc(PixelFormat pix_fmt, int width, int height) { // Get linesizes AVPicture dummy_pict; 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); + core::pixel_format_desc desc = get_pixel_format(pix_fmt); - switch(desc.pix_fmt) + switch(desc.format.value()) { case core::pixel_format::gray: case core::pixel_format::luma: @@ -121,26 +142,26 @@ core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, case core::pixel_format::ycbcra: { // Find chroma height - size_t size2 = dummy_pict.data[2] - dummy_pict.data[1]; - size_t h2 = size2/dummy_pict.linesize[1]; + int size2 = static_cast(dummy_pict.data[2] - dummy_pict.data[1]); + int h2 = size2/dummy_pict.linesize[1]; desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0], height, 1)); desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[1], h2, 1)); desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[2], h2, 1)); - if(desc.pix_fmt == core::pixel_format::ycbcra) + if(desc.format == core::pixel_format::ycbcra) desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[3], height, 1)); return desc; } default: - desc.pix_fmt = core::pixel_format::invalid; + desc.format = core::pixel_format::invalid; return desc; } } int make_alpha_format(int format) { - switch(get_pixel_format(static_cast(format))) + switch(get_pixel_format(static_cast(format)).value()) { case core::pixel_format::ycbcr: case core::pixel_format::ycbcra: @@ -150,41 +171,54 @@ int make_alpha_format(int format) } } -safe_ptr make_write_frame(const void* tag, const safe_ptr& decoded_frame, const safe_ptr& frame_factory, int hints) +core::mutable_frame make_frame(const void* tag, const spl::shared_ptr& decoded_frame, double fps, const spl::shared_ptr& frame_factory, int flags) { - static tbb::concurrent_unordered_map>> sws_contexts_; + static tbb::concurrent_unordered_map>> sws_contexts_; if(decoded_frame->width < 1 || decoded_frame->height < 1) - return make_safe(tag); + return frame_factory->create_frame(tag, core::pixel_format_desc(core::pixel_format::invalid)); const auto width = decoded_frame->width; const auto height = decoded_frame->height; - auto desc = get_pixel_format_desc(static_cast(decoded_frame->format), width, height); + auto desc = 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); - - std::shared_ptr write; - - if(desc.pix_fmt == core::pixel_format::invalid) + if(flags & core::frame_producer::flags::alpha_only) + desc = pixel_format_desc(static_cast(make_alpha_format(decoded_frame->format)), width, height); + + if(desc.format == core::pixel_format::invalid) { auto pix_fmt = static_cast(decoded_frame->format); + auto target_pix_fmt = PIX_FMT_BGRA; + + if(pix_fmt == PIX_FMT_UYVY422) + target_pix_fmt = PIX_FMT_YUV422P; + else if(pix_fmt == PIX_FMT_YUYV422) + target_pix_fmt = PIX_FMT_YUV422P; + else if(pix_fmt == PIX_FMT_UYYVYY411) + target_pix_fmt = PIX_FMT_YUV411P; + else if(pix_fmt == PIX_FMT_YUV420P10) + target_pix_fmt = PIX_FMT_YUV420P; + else if(pix_fmt == PIX_FMT_YUV422P10) + target_pix_fmt = PIX_FMT_YUV422P; + else if(pix_fmt == PIX_FMT_YUV444P10) + target_pix_fmt = PIX_FMT_YUV444P; + + auto target_desc = pixel_format_desc(target_pix_fmt, width, height); - write = frame_factory->create_frame(tag, get_pixel_format_desc(PIX_FMT_BGRA, width, height)); - write->set_type(get_mode(*decoded_frame)); + auto write = frame_factory->create_frame(tag, target_desc, fps, get_mode(*decoded_frame)); std::shared_ptr sws_context; //CASPAR_LOG(warning) << "Hardware accelerated color transform not supported."; - size_t key = width << 20 | height << 8 | pix_fmt; + int key = ((width << 22) & 0xFFC00000) | ((height << 6) & 0x003FC000) | ((pix_fmt << 7) & 0x00007F00) | ((target_pix_fmt << 0) & 0x0000007F); auto& pool = sws_contexts_[key]; if(!pool.try_pop(sws_context)) { double param; - sws_context.reset(sws_getContext(width, height, pix_fmt, width, height, PIX_FMT_BGRA, SWS_BILINEAR, nullptr, nullptr, ¶m), sws_freeContext); + sws_context.reset(sws_getContext(width, height, pix_fmt, width, height, target_pix_fmt, SWS_BILINEAR, nullptr, nullptr, ¶m), sws_freeContext); } if(!sws_context) @@ -192,63 +226,123 @@ safe_ptr make_write_frame(const void* tag, const safe_ptr av_frame(avcodec_alloc_frame(), av_free); + + spl::shared_ptr av_frame(avcodec_alloc_frame(), av_free); avcodec_get_frame_defaults(av_frame.get()); - auto size = avpicture_fill(reinterpret_cast(av_frame.get()), write->image_data().begin(), PIX_FMT_BGRA, width, height); - CASPAR_VERIFY(size == write->image_data().size()); + if(target_pix_fmt == PIX_FMT_BGRA) + { + auto size = avpicture_fill(reinterpret_cast(av_frame.get()), write.image_data(0).begin(), PIX_FMT_BGRA, width, height); + CASPAR_VERIFY(size == write.image_data(0).size()); + } + else + { + av_frame->width = width; + av_frame->height = height; + for(int n = 0; n < target_desc.planes.size(); ++n) + { + av_frame->data[n] = write.image_data(n).begin(); + av_frame->linesize[n] = target_desc.planes[n].linesize; + } + } sws_scale(sws_context.get(), decoded_frame->data, decoded_frame->linesize, 0, height, av_frame->data, av_frame->linesize); - pool.push(sws_context); + pool.push(sws_context); - write->commit(); + return std::move(write); } else { - write = frame_factory->create_frame(tag, desc); - write->set_type(get_mode(*decoded_frame)); - + auto write = frame_factory->create_frame(tag, desc, fps, get_mode(*decoded_frame)); + for(int n = 0; n < static_cast(desc.planes.size()); ++n) { auto plane = desc.planes[n]; - auto result = write->image_data(n).begin(); + auto result = write.image_data(n).begin(); auto decoded = decoded_frame->data[n]; auto decoded_linesize = decoded_frame->linesize[n]; CASPAR_ASSERT(decoded); - CASPAR_ASSERT(write->image_data(n).begin()); + CASPAR_ASSERT(write.image_data(n).begin()); - if(decoded_linesize != static_cast(plane.width)) - { - // Copy line by line since ffmpeg sometimes pads each line. - tbb::parallel_for(0, desc.planes[n].height, [&](size_t y) - { - fast_memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize); - }); - } - else + // Copy line by line since ffmpeg sometimes pads each line. + tbb::affinity_partitioner ap; + tbb::parallel_for(tbb::blocked_range(0, desc.planes[n].height), [&](const tbb::blocked_range& r) { - fast_memcpy(result, decoded, plane.size); - } - - write->commit(n); + for(int y = r.begin(); y != r.end(); ++y) + A_memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize); + }, ap); } + + return std::move(write); } +} - if(decoded_frame->height == 480) // NTSC DV - { - write->get_frame_transform().fill_translation[1] += 2.0/static_cast(frame_factory->get_video_format_desc().height); - write->get_frame_transform().fill_scale[1] = 1.0 - 6.0*1.0/static_cast(frame_factory->get_video_format_desc().height); - } +spl::shared_ptr make_av_frame(core::mutable_frame& frame) +{ + std::array data = {}; + for(int n = 0; n < frame.pixel_format_desc().planes.size(); ++n) + data[n] = frame.image_data(n).begin(); + + return make_av_frame(data, frame.pixel_format_desc()); +} + +spl::shared_ptr make_av_frame(std::array data, const core::pixel_format_desc& pix_desc) +{ + spl::shared_ptr av_frame(avcodec_alloc_frame(), av_free); + avcodec_get_frame_defaults(av_frame.get()); - // Fix field-order if needed - if(write->get_type() == core::field_mode::lower && frame_factory->get_video_format_desc().field_mode == core::field_mode::upper) - write->get_frame_transform().fill_translation[1] += 1.0/static_cast(frame_factory->get_video_format_desc().height); - else if(write->get_type() == core::field_mode::upper && frame_factory->get_video_format_desc().field_mode == core::field_mode::lower) - write->get_frame_transform().fill_translation[1] -= 1.0/static_cast(frame_factory->get_video_format_desc().height); + auto planes = pix_desc.planes; + auto format = pix_desc.format.value(); - return make_safe_ptr(write); + av_frame->width = planes[0].width; + av_frame->height = planes[0].height; + for(int n = 0; n < planes.size(); ++n) + { + av_frame->data[n] = data[n]; + av_frame->linesize[n] = planes[n].linesize; + } + switch(format) + { + case core::pixel_format::rgba: + av_frame->format = PIX_FMT_RGBA; + break; + case core::pixel_format::argb: + av_frame->format = PIX_FMT_ARGB; + break; + case core::pixel_format::bgra: + av_frame->format = PIX_FMT_BGRA; + break; + case core::pixel_format::abgr: + av_frame->format = PIX_FMT_ABGR; + break; + case core::pixel_format::gray: + av_frame->format = PIX_FMT_GRAY8; + break; + case core::pixel_format::ycbcr: + { + int y_w = planes[0].width; + int y_h = planes[0].height; + int c_w = planes[1].width; + int c_h = planes[1].height; + + if(c_h == y_h && c_w == y_w) + av_frame->format = PIX_FMT_YUV444P; + else if(c_h == y_h && c_w*2 == y_w) + av_frame->format = PIX_FMT_YUV422P; + else if(c_h == y_h && c_w*4 == y_w) + av_frame->format = PIX_FMT_YUV411P; + else if(c_h*2 == y_h && c_w*2 == y_w) + av_frame->format = PIX_FMT_YUV420P; + else if(c_h*2 == y_h && c_w*4 == y_w) + av_frame->format = PIX_FMT_YUV410P; + + break; + } + case core::pixel_format::ycbcra: + av_frame->format = PIX_FMT_YUVA420P; + break; + } + return av_frame; } bool is_sane_fps(AVRational time_base) @@ -285,7 +379,7 @@ double read_fps(AVFormatContext& context, double fail_value) AVRational time_base = video_context->time_base; - if(boost::filesystem2::path(context.filename).extension() == ".flv") + if(boost::filesystem::path(context.filename).extension().string() == ".flv") { try { @@ -323,7 +417,7 @@ double read_fps(AVFormatContext& context, double fail_value) 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)); + auto format = core::video_format_desc(core::video_format(n)); double diff1 = std::abs(format.fps - fps); double diff2 = std::abs(closest_fps - fps); @@ -347,7 +441,7 @@ void fix_meta_data(AVFormatContext& context) auto video_stream = context.streams[video_index]; auto video_context = context.streams[video_index]->codec; - if(boost::filesystem2::path(context.filename).extension() == ".flv") + if(boost::filesystem::path(context.filename).extension().string() == ".flv") { try { @@ -359,14 +453,20 @@ void fix_meta_data(AVFormatContext& context) } else { - video_stream->nb_frames = (video_stream->duration*video_stream->time_base.num*video_context->time_base.den)/(video_stream->time_base.den*video_context->time_base.num*video_context->ticks_per_frame); + auto stream_time = video_stream->time_base; + auto duration = video_stream->duration; + auto codec_time = video_context->time_base; + auto ticks = video_context->ticks_per_frame; + + if(video_stream->nb_frames == 0) + video_stream->nb_frames = (duration*stream_time.num*codec_time.den)/(stream_time.den*codec_time.num*ticks); } } } -safe_ptr create_packet() +spl::shared_ptr create_packet() { - safe_ptr packet(new AVPacket, [](AVPacket* p) + spl::shared_ptr packet(new AVPacket, [](AVPacket* p) { av_free_packet(p); delete p; @@ -376,7 +476,7 @@ safe_ptr create_packet() return packet; } -safe_ptr open_codec(AVFormatContext& context, enum AVMediaType type, int& index) +spl::shared_ptr open_codec(AVFormatContext& context, enum AVMediaType type, int& index) { AVCodec* decoder; index = THROW_ON_ERROR2(av_find_best_stream(&context, type, -1, -1, &decoder, 0), ""); @@ -384,18 +484,61 @@ safe_ptr open_codec(AVFormatContext& context, enum AVMediaType t // decoder = decoder->next; THROW_ON_ERROR2(tbb_avcodec_open(context.streams[index]->codec, decoder), ""); - return safe_ptr(context.streams[index]->codec, tbb_avcodec_close); + return spl::shared_ptr(context.streams[index]->codec, tbb_avcodec_close); } -safe_ptr open_input(const std::wstring& filename) +spl::shared_ptr open_input(const std::wstring& filename) { AVFormatContext* weak_context = nullptr; - THROW_ON_ERROR2(avformat_open_input(&weak_context, narrow(filename).c_str(), nullptr, nullptr), filename); - safe_ptr context(weak_context, av_close_input_file); + THROW_ON_ERROR2(avformat_open_input(&weak_context, u8(filename).c_str(), nullptr, nullptr), filename); + spl::shared_ptr context(weak_context, av_close_input_file); THROW_ON_ERROR2(avformat_find_stream_info(weak_context, nullptr), filename); fix_meta_data(*context); return context; } + +std::wstring print_mode(int width, int height, double fps, bool interlaced) +{ + std::wostringstream fps_ss; + fps_ss << std::fixed << std::setprecision(2) << (!interlaced ? fps : 2.0 * fps); + + return boost::lexical_cast(width) + L"x" + boost::lexical_cast(height) + (!interlaced ? L"p" : L"i") + fps_ss.str(); +} + +bool is_valid_file(const std::wstring filename) +{ + if(boost::filesystem::path(filename).extension() == ".m2t") + return true; + + std::ifstream file(filename); + + std::vector buf; + for(auto file_it = std::istreambuf_iterator(file); file_it != std::istreambuf_iterator() && buf.size() < 2048; ++file_it) + buf.push_back(*file_it); + + if(buf.empty()) + return nullptr; + + AVProbeData pb; + pb.filename = u8(filename).c_str(); + pb.buf = buf.data(); + pb.buf_size = static_cast(buf.size()); + + int score = 0; + return av_probe_input_format2(&pb, true, &score) != nullptr; +} + +std::wstring probe_stem(const std::wstring stem) +{ + auto stem2 = boost::filesystem::path(stem); + auto dir = stem2.parent_path(); + for(auto it = boost::filesystem::directory_iterator(dir); it != boost::filesystem::directory_iterator(); ++it) + { + if(boost::iequals(it->path().stem().wstring(), stem2.filename().wstring()) && is_valid_file(it->path().wstring())) + return it->path().wstring(); + } + return L""; +} // //void av_dup_frame(AVFrame* frame) //{