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