]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/util.cpp
2.0. ffmpeg_producer: Can't use YUV as separate alpha, convert it to BGRA until suppo...
[casparcg] / modules / ffmpeg / producer / util.cpp
1 #include "../../stdafx.h"\r
2 \r
3 #include "util.h"\r
4 \r
5 #include <tbb/concurrent_unordered_map.h>\r
6 #include <tbb/concurrent_queue.h>\r
7 \r
8 #include <core/producer/frame/image_transform.h>\r
9 #include <core/producer/frame/frame_factory.h>\r
10 #include <core/producer/frame_producer.h>\r
11 #include <core/mixer/write_frame.h>\r
12 \r
13 #include <common/exception/exceptions.h>\r
14 \r
15 #include <tbb/parallel_for.h>\r
16 \r
17 #if defined(_MSC_VER)\r
18 #pragma warning (push)\r
19 #pragma warning (disable : 4244)\r
20 #endif\r
21 extern "C" \r
22 {\r
23         #include <libswscale/swscale.h>\r
24         #include <libavcodec/avcodec.h>\r
25 }\r
26 #if defined(_MSC_VER)\r
27 #pragma warning (pop)\r
28 #endif\r
29 \r
30 namespace caspar {\r
31 \r
32 core::video_mode::type get_mode(AVFrame& frame)\r
33 {\r
34         if(!frame.interlaced_frame)\r
35                 return core::video_mode::progressive;\r
36 \r
37         return frame.top_field_first ? core::video_mode::upper : core::video_mode::lower;\r
38 }\r
39 \r
40 core::pixel_format::type get_pixel_format(PixelFormat pix_fmt)\r
41 {\r
42         switch(pix_fmt)\r
43         {\r
44         case PIX_FMT_GRAY8:             return core::pixel_format::gray;\r
45         case PIX_FMT_BGRA:              return core::pixel_format::bgra;\r
46         case PIX_FMT_ARGB:              return core::pixel_format::argb;\r
47         case PIX_FMT_RGBA:              return core::pixel_format::rgba;\r
48         case PIX_FMT_ABGR:              return core::pixel_format::abgr;\r
49         case PIX_FMT_YUV444P:   return core::pixel_format::ycbcr;\r
50         case PIX_FMT_YUV422P:   return core::pixel_format::ycbcr;\r
51         case PIX_FMT_YUV420P:   return core::pixel_format::ycbcr;\r
52         case PIX_FMT_YUV411P:   return core::pixel_format::ycbcr;\r
53         case PIX_FMT_YUV410P:   return core::pixel_format::ycbcr;\r
54         case PIX_FMT_YUVA420P:  return core::pixel_format::ycbcra;\r
55         default:                                return core::pixel_format::invalid;\r
56         }\r
57 }\r
58 \r
59 PixelFormat get_ffmpeg_pixel_format(const core::pixel_format_desc& format_desc)\r
60 {\r
61         switch(format_desc.pix_fmt)\r
62         {\r
63         case core::pixel_format::gray: return PIX_FMT_GRAY8;\r
64         case core::pixel_format::bgra: return PIX_FMT_BGRA;\r
65         case core::pixel_format::argb: return PIX_FMT_ARGB;\r
66         case core::pixel_format::rgba: return PIX_FMT_RGBA;\r
67         case core::pixel_format::abgr: return PIX_FMT_ABGR;\r
68         case core::pixel_format::ycbcra: return PIX_FMT_YUVA420P;\r
69         case core::pixel_format::ycbcr:\r
70                 auto planes = format_desc.planes;\r
71                 if(planes[0].height == planes[1].height)\r
72                 {\r
73                         if(planes[0].width == planes[1].width)\r
74                                 return PIX_FMT_YUV444P;\r
75                         else if(planes[0].width/2 == planes[1].width)\r
76                                 return PIX_FMT_YUV422P;\r
77                         else if(planes[0].width/4 == planes[1].width)\r
78                                 return PIX_FMT_YUV411P;\r
79                 }\r
80                 if(planes[0].height/2 == planes[1].height)\r
81                 {\r
82                         if(planes[0].width/2 == planes[1].width)\r
83                                 return PIX_FMT_YUV420P;\r
84                 }\r
85                 if(planes[0].height/4 == planes[1].height)\r
86                 {\r
87                         if(planes[0].width/4 == planes[1].width)\r
88                                 return PIX_FMT_YUV410P;\r
89                 }\r
90         }\r
91         return PIX_FMT_NONE;\r
92 }\r
93 \r
94 safe_ptr<AVFrame> as_av_frame(const safe_ptr<core::write_frame>& frame)\r
95 {\r
96         auto desc = frame->get_pixel_format_desc();\r
97         safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);     \r
98         avcodec_get_frame_defaults(av_frame.get());\r
99 \r
100         for(size_t n = 0; n < desc.planes.size(); ++n)\r
101         {       \r
102                 av_frame->data[n]               = frame->image_data(n).begin();\r
103                 av_frame->linesize[n]   = desc.planes[n].width;\r
104         }\r
105 \r
106         av_frame->format        = get_ffmpeg_pixel_format(desc);\r
107         av_frame->width         = desc.planes[0].width;\r
108         av_frame->height        = desc.planes[0].height;\r
109 \r
110         if(frame->get_type() != core::video_mode::progressive)\r
111         {\r
112                 av_frame->interlaced_frame = 1;\r
113                 av_frame->top_field_first = frame->get_type() == core::video_mode::upper ? 1 : 0;\r
114         }\r
115 \r
116         return av_frame;\r
117 }\r
118 \r
119 core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, size_t height)\r
120 {\r
121         // Get linesizes\r
122         AVPicture dummy_pict;   \r
123         avpicture_fill(&dummy_pict, nullptr, pix_fmt, width, height);\r
124 \r
125         core::pixel_format_desc desc;\r
126         desc.pix_fmt = get_pixel_format(pix_fmt);\r
127                 \r
128         switch(desc.pix_fmt)\r
129         {\r
130         case core::pixel_format::gray:\r
131                 {\r
132                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0], height, 1));                                               \r
133                         return desc;\r
134                 }\r
135         case core::pixel_format::bgra:\r
136         case core::pixel_format::argb:\r
137         case core::pixel_format::rgba:\r
138         case core::pixel_format::abgr:\r
139                 {\r
140                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0]/4, height, 4));                                             \r
141                         return desc;\r
142                 }\r
143         case core::pixel_format::ycbcr:\r
144         case core::pixel_format::ycbcra:\r
145                 {               \r
146                         // Find chroma height\r
147                         size_t size2 = dummy_pict.data[2] - dummy_pict.data[1];\r
148                         size_t h2 = size2/dummy_pict.linesize[1];                       \r
149 \r
150                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0], height, 1));\r
151                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[1], h2, 1));\r
152                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[2], h2, 1));\r
153 \r
154                         if(desc.pix_fmt == core::pixel_format::ycbcra)                                          \r
155                                 desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[3], height, 1));       \r
156                         return desc;\r
157                 }               \r
158         default:                \r
159                 desc.pix_fmt = core::pixel_format::invalid;\r
160                 return desc;\r
161         }\r
162 }\r
163 \r
164 bool try_make_gray(const safe_ptr<AVFrame>& frame)\r
165 {\r
166         auto pix_fmt = get_pixel_format(static_cast<PixelFormat>(frame->format));\r
167         if(pix_fmt == PIX_FMT_RGBA || pix_fmt == PIX_FMT_BGRA)\r
168                 frame->format = PIX_FMT_GRAY8;\r
169 \r
170         return pix_fmt != core::pixel_format::invalid;\r
171 }\r
172 \r
173 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, int hints)\r
174 {                       \r
175         static tbb::concurrent_unordered_map<size_t, tbb::concurrent_queue<std::shared_ptr<SwsContext>>> sws_contexts_;\r
176         \r
177         const auto width  = decoded_frame->width;\r
178         const auto height = decoded_frame->height;\r
179         auto desc                 = get_pixel_format_desc(static_cast<PixelFormat>(decoded_frame->format), width, height);\r
180         \r
181         // TODO: Can't handle YUV as alpha\r
182         if(hints & core::frame_producer::ALPHA_HINT && (desc.pix_fmt == core::pixel_format::ycbcr || desc.pix_fmt == core::pixel_format::ycbcra))\r
183                 desc.pix_fmt = core::pixel_format::invalid;\r
184 \r
185         if(desc.pix_fmt == core::pixel_format::invalid)\r
186         {\r
187                 auto pix_fmt = static_cast<PixelFormat>(decoded_frame->format);\r
188 \r
189                 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
190                 write->set_type(get_mode(*decoded_frame));\r
191 \r
192                 std::shared_ptr<SwsContext> sws_context;\r
193 \r
194                 //CASPAR_LOG(warning) << "Hardware accelerated color transform not supported.";\r
195 \r
196                 size_t key = width << 20 | height << 8 | pix_fmt;\r
197                         \r
198                 auto& pool = sws_contexts_[key];\r
199                                                 \r
200                 if(!pool.try_pop(sws_context))\r
201                 {\r
202                         double param;\r
203                         sws_context.reset(sws_getContext(width, height, pix_fmt, width, height, PIX_FMT_BGRA, SWS_BILINEAR, nullptr, nullptr, &param), sws_freeContext);\r
204                 }\r
205                         \r
206                 if(!sws_context)\r
207                 {\r
208                         BOOST_THROW_EXCEPTION(operation_failed() << msg_info("Could not create software scaling context.") << \r
209                                                                         boost::errinfo_api_function("sws_getContext"));\r
210                 }       \r
211 \r
212                 // Use sws_scale when provided colorspace has no hw-accel.\r
213                 safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);     \r
214                 avcodec_get_frame_defaults(av_frame.get());                     \r
215                 avpicture_fill(reinterpret_cast<AVPicture*>(av_frame.get()), write->image_data().begin(), PIX_FMT_BGRA, width, height);\r
216                  \r
217                 sws_scale(sws_context.get(), decoded_frame->data, decoded_frame->linesize, 0, height, av_frame->data, av_frame->linesize);      \r
218                 pool.push(sws_context);\r
219 \r
220                 write->commit();\r
221 \r
222                 return write;\r
223         }\r
224         else\r
225         {\r
226                 if(hints & core::frame_producer::ALPHA_HINT)\r
227                         desc = get_pixel_format_desc(PIX_FMT_GRAY8, width, height);\r
228                 \r
229                 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
230                 write->set_type(get_mode(*decoded_frame));\r
231 \r
232                 for(int n = 0; n < static_cast<int>(desc.planes.size()); ++n)\r
233                 {\r
234                         auto plane            = desc.planes[n];\r
235                         auto result           = write->image_data(n).begin();\r
236                         auto decoded          = decoded_frame->data[n];\r
237                         auto decoded_linesize = decoded_frame->linesize[n];\r
238                                 \r
239                         // Copy line by line since ffmpeg sometimes pads each line.\r
240                         tbb::affinity_partitioner ap;\r
241                         tbb::parallel_for(tbb::blocked_range<size_t>(0, static_cast<int>(desc.planes[n].height)), [&](const tbb::blocked_range<size_t>& r)\r
242                         {\r
243                                 for(size_t y = r.begin(); y != r.end(); ++y)\r
244                                         memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize);\r
245                         }, ap);\r
246 \r
247                         write->commit(n);\r
248                 }\r
249 \r
250                 //for(int n = 0; n < static_cast<int>(desc.planes.size()); ++n)\r
251                 //{\r
252                 //      auto plane            = desc.planes[n];\r
253                 //      auto result           = write->image_data(n).begin();\r
254                 //      auto decoded          = decoded_frame->data[n];\r
255                 //      auto decoded_linesize = decoded_frame->linesize[n];\r
256                 //              \r
257                 //      for(size_t y = 0; y < static_cast<int>(desc.planes[n].height); ++y)\r
258                 //              fast_memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize);\r
259 \r
260                 //      write->commit(n);\r
261                 //}\r
262 \r
263                 return write;\r
264         }\r
265 }\r
266 \r
267 }