]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/util.h
2.0. ffmpeg_producer: Refactored.
[casparcg] / modules / ffmpeg / producer / util.h
1 #pragma once\r
2 \r
3 #include <tbb/concurrent_unordered_map.h>\r
4 #include <tbb/concurrent_queue.h>\r
5 \r
6 #include <core/producer/frame/pixel_format.h>\r
7 #include <core/producer/frame/image_transform.h>\r
8 #include <core/producer/frame/frame_factory.h>\r
9 #include <core/mixer/write_frame.h>\r
10 \r
11 #if defined(_MSC_VER)\r
12 #pragma warning (push)\r
13 #pragma warning (disable : 4244)\r
14 #endif\r
15 extern "C" \r
16 {\r
17         #define __STDC_CONSTANT_MACROS\r
18         #define __STDC_LIMIT_MACROS\r
19         #include <libswscale/swscale.h>\r
20 }\r
21 #if defined(_MSC_VER)\r
22 #pragma warning (pop)\r
23 #endif\r
24 \r
25 namespace caspar {\r
26 \r
27 static core::pixel_format::type get_pixel_format(PixelFormat pix_fmt)\r
28 {\r
29         switch(pix_fmt)\r
30         {\r
31         case PIX_FMT_GRAY8:             return core::pixel_format::gray;\r
32         case PIX_FMT_BGRA:              return core::pixel_format::bgra;\r
33         case PIX_FMT_ARGB:              return core::pixel_format::argb;\r
34         case PIX_FMT_RGBA:              return core::pixel_format::rgba;\r
35         case PIX_FMT_ABGR:              return core::pixel_format::abgr;\r
36         case PIX_FMT_YUV444P:   return core::pixel_format::ycbcr;\r
37         case PIX_FMT_YUV422P:   return core::pixel_format::ycbcr;\r
38         case PIX_FMT_YUV420P:   return core::pixel_format::ycbcr;\r
39         case PIX_FMT_YUV411P:   return core::pixel_format::ycbcr;\r
40         case PIX_FMT_YUV410P:   return core::pixel_format::ycbcr;\r
41         case PIX_FMT_YUVA420P:  return core::pixel_format::ycbcra;\r
42         default:                                return core::pixel_format::invalid;\r
43         }\r
44 }\r
45 \r
46 static core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, size_t height)\r
47 {\r
48         // Get linesizes\r
49         AVPicture dummy_pict;   \r
50         avpicture_fill(&dummy_pict, nullptr, pix_fmt, width, height);\r
51 \r
52         core::pixel_format_desc desc;\r
53         desc.pix_fmt = get_pixel_format(pix_fmt);\r
54                 \r
55         switch(desc.pix_fmt)\r
56         {\r
57         case core::pixel_format::gray:\r
58                 {\r
59                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0]/4, height, 1));                                             \r
60                         return desc;\r
61                 }\r
62         case core::pixel_format::bgra:\r
63         case core::pixel_format::argb:\r
64         case core::pixel_format::rgba:\r
65         case core::pixel_format::abgr:\r
66                 {\r
67                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0]/4, height, 4));                                             \r
68                         return desc;\r
69                 }\r
70         case core::pixel_format::ycbcr:\r
71         case core::pixel_format::ycbcra:\r
72                 {               \r
73                         // Find chroma height\r
74                         size_t size2 = dummy_pict.data[2] - dummy_pict.data[1];\r
75                         size_t h2 = size2/dummy_pict.linesize[1];                       \r
76 \r
77                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0], height, 1));\r
78                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[1], h2, 1));\r
79                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[2], h2, 1));\r
80 \r
81                         if(desc.pix_fmt == core::pixel_format::ycbcra)                                          \r
82                                 desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[3], height, 1));       \r
83                         return desc;\r
84                 }               \r
85         default:                \r
86                 desc.pix_fmt = core::pixel_format::invalid;\r
87                 return desc;\r
88         }\r
89 }\r
90 \r
91 safe_ptr<core::write_frame> make_write_frame(const void* tag, const safe_ptr<AVFrame>& decoded_frame, const safe_ptr<core::frame_factory>& frame_factory)\r
92 {                       \r
93         static tbb::concurrent_unordered_map<size_t, tbb::concurrent_queue<std::shared_ptr<SwsContext>>> sws_contexts_;\r
94 \r
95         // We don't know what the filter output might give until we received the first frame. Initialize everything on first frame.\r
96         auto width   = decoded_frame->width;\r
97         auto height  = decoded_frame->height;\r
98         auto pix_fmt = static_cast<PixelFormat>(decoded_frame->format);\r
99         auto desc        = get_pixel_format_desc(pix_fmt, width, height);\r
100                                 \r
101         auto write = frame_factory->create_frame(tag, desc.pix_fmt != core::pixel_format::invalid ? desc : get_pixel_format_desc(PIX_FMT_BGRA, width, height));\r
102         write->set_is_interlaced(decoded_frame->interlaced_frame != 0);\r
103 \r
104         if(desc.pix_fmt == core::pixel_format::invalid)\r
105         {\r
106                 std::shared_ptr<SwsContext> sws_context;\r
107 \r
108                 CASPAR_LOG(warning) << "Hardware accelerated color transform not supported.";\r
109 \r
110                 size_t key = width << 20 | height << 8 | pix_fmt;\r
111                         \r
112                 auto& pool = sws_contexts_[key];\r
113                                                 \r
114                 if(!pool.try_pop(sws_context))\r
115                 {\r
116                         double param;\r
117                         sws_context.reset(sws_getContext(width, height, pix_fmt, width, height, PIX_FMT_BGRA, SWS_BILINEAR, nullptr, nullptr, &param), sws_freeContext);\r
118                 }\r
119                         \r
120                 if(!sws_context)\r
121                 {\r
122                         BOOST_THROW_EXCEPTION(operation_failed() << msg_info("Could not create software scaling context.") << \r
123                                                                         boost::errinfo_api_function("sws_getContext"));\r
124                 }       \r
125 \r
126                 // Use sws_scale when provided colorspace has no hw-accel.\r
127                 safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);     \r
128                 avcodec_get_frame_defaults(av_frame.get());                     \r
129                 avpicture_fill(reinterpret_cast<AVPicture*>(av_frame.get()), write->image_data().begin(), PIX_FMT_BGRA, width, height);\r
130                  \r
131                 sws_scale(sws_context.get(), decoded_frame->data, decoded_frame->linesize, 0, height, av_frame->data, av_frame->linesize);      \r
132                 pool.push(sws_context);\r
133 \r
134                 write->commit();\r
135         }\r
136         else\r
137         {\r
138                 tbb::parallel_for(0, static_cast<int>(desc.planes.size()), 1, [&](int n)\r
139                 {\r
140                         auto plane            = desc.planes[n];\r
141                         auto result           = write->image_data(n).begin();\r
142                         auto decoded          = decoded_frame->data[n];\r
143                         auto decoded_linesize = decoded_frame->linesize[n];\r
144                                 \r
145                         // Copy line by line since ffmpeg sometimes pads each line.\r
146                         tbb::parallel_for(tbb::blocked_range<size_t>(0, static_cast<int>(desc.planes[n].height)), [&](const tbb::blocked_range<size_t>& r)\r
147                         {\r
148                                 for(size_t y = r.begin(); y != r.end(); ++y)\r
149                                         memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize);\r
150                         });\r
151 \r
152                         write->commit(n);\r
153                 });\r
154         }\r
155                 \r
156         // Fix field-order if needed. DVVIDEO is in lower field. Make it upper field if needed.\r
157         if(decoded_frame->interlaced_frame)\r
158         {\r
159                 switch(frame_factory->get_video_format_desc().mode)\r
160                 {\r
161                 case core::video_mode::upper:\r
162                         if(!decoded_frame->top_field_first)\r
163                                 write->get_image_transform().set_fill_translation(0.0f, 0.5/static_cast<double>(height));\r
164                         break;\r
165                 case core::video_mode::lower:\r
166                         if(decoded_frame->top_field_first)\r
167                                 write->get_image_transform().set_fill_translation(0.0f, -0.5/static_cast<double>(height));\r
168                         break;\r
169                 }\r
170         }\r
171 \r
172         return write;\r
173 }\r
174 \r
175 }