]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/util.h
2.0.0.2: ffmpeg_producer: Improved input buffering.
[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         #include <libswscale/swscale.h>\r
18         #include <libavcodec/avcodec.h>\r
19         #include <libavfilter/avfilter.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 PixelFormat get_ffmpeg_pixel_format(const core::pixel_format_desc& format_desc)\r
47 {\r
48         switch(format_desc.pix_fmt)\r
49         {\r
50         case core::pixel_format::gray: return PIX_FMT_GRAY8;\r
51         case core::pixel_format::bgra: return PIX_FMT_BGRA;\r
52         case core::pixel_format::argb: return PIX_FMT_ARGB;\r
53         case core::pixel_format::rgba: return PIX_FMT_RGBA;\r
54         case core::pixel_format::abgr: return PIX_FMT_ABGR;\r
55         case core::pixel_format::ycbcra: return PIX_FMT_YUVA420P;\r
56         case core::pixel_format::ycbcr:\r
57                 auto planes = format_desc.planes;\r
58                 if(planes[0].height == planes[1].height)\r
59                 {\r
60                         if(planes[0].width == planes[1].width)\r
61                                 return PIX_FMT_YUV444P;\r
62                         else if(planes[0].width/2 == planes[1].width)\r
63                                 return PIX_FMT_YUV422P;\r
64                         else if(planes[0].width/4 == planes[1].width)\r
65                                 return PIX_FMT_YUV411P;\r
66                 }\r
67                 if(planes[0].height/2 == planes[1].height)\r
68                 {\r
69                         if(planes[0].width/2 == planes[1].width)\r
70                                 return PIX_FMT_YUV420P;\r
71                 }\r
72                 if(planes[0].height/4 == planes[1].height)\r
73                 {\r
74                         if(planes[0].width/4 == planes[1].width)\r
75                                 return PIX_FMT_YUV410P;\r
76                 }\r
77         }\r
78         return PIX_FMT_NONE;\r
79 }\r
80 \r
81 static safe_ptr<AVFrame> as_av_frame(const safe_ptr<core::write_frame>& frame)\r
82 {\r
83         auto desc = frame->get_pixel_format_desc();\r
84         safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);     \r
85         avcodec_get_frame_defaults(av_frame.get());\r
86 \r
87         for(size_t n = 0; n < desc.planes.size(); ++n)\r
88         {       \r
89                 av_frame->data[n]               = frame->image_data(n).begin();\r
90                 av_frame->linesize[n]   = desc.planes[n].width;\r
91         }\r
92 \r
93         av_frame->format        = get_ffmpeg_pixel_format(desc);\r
94         av_frame->width         = desc.planes[0].width;\r
95         av_frame->height        = desc.planes[0].height;\r
96 \r
97         if(frame->get_type() != core::video_mode::progressive)\r
98         {\r
99                 av_frame->interlaced_frame = 1;\r
100                 av_frame->top_field_first = frame->get_type() == core::video_mode::upper ? 1 : 0;\r
101         }\r
102 \r
103         return av_frame;\r
104 }\r
105 \r
106 static core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, size_t height)\r
107 {\r
108         // Get linesizes\r
109         AVPicture dummy_pict;   \r
110         avpicture_fill(&dummy_pict, nullptr, pix_fmt, width, height);\r
111 \r
112         core::pixel_format_desc desc;\r
113         desc.pix_fmt = get_pixel_format(pix_fmt);\r
114                 \r
115         switch(desc.pix_fmt)\r
116         {\r
117         case core::pixel_format::gray:\r
118                 {\r
119                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0]/4, height, 1));                                             \r
120                         return desc;\r
121                 }\r
122         case core::pixel_format::bgra:\r
123         case core::pixel_format::argb:\r
124         case core::pixel_format::rgba:\r
125         case core::pixel_format::abgr:\r
126                 {\r
127                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0]/4, height, 4));                                             \r
128                         return desc;\r
129                 }\r
130         case core::pixel_format::ycbcr:\r
131         case core::pixel_format::ycbcra:\r
132                 {               \r
133                         // Find chroma height\r
134                         size_t size2 = dummy_pict.data[2] - dummy_pict.data[1];\r
135                         size_t h2 = size2/dummy_pict.linesize[1];                       \r
136 \r
137                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0], height, 1));\r
138                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[1], h2, 1));\r
139                         desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[2], h2, 1));\r
140 \r
141                         if(desc.pix_fmt == core::pixel_format::ycbcra)                                          \r
142                                 desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[3], height, 1));       \r
143                         return desc;\r
144                 }               \r
145         default:                \r
146                 desc.pix_fmt = core::pixel_format::invalid;\r
147                 return desc;\r
148         }\r
149 }\r
150 \r
151 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
152 {                       \r
153         static tbb::concurrent_unordered_map<size_t, tbb::concurrent_queue<std::shared_ptr<SwsContext>>> sws_contexts_;\r
154 \r
155         auto width   = decoded_frame->width;\r
156         auto height  = decoded_frame->height;\r
157         auto pix_fmt = static_cast<PixelFormat>(decoded_frame->format);\r
158         auto desc        = get_pixel_format_desc(pix_fmt, width, height);\r
159                                 \r
160         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
161         if(decoded_frame->interlaced_frame)\r
162                 write->set_type(decoded_frame->top_field_first ? core::video_mode::upper : core::video_mode::lower);\r
163         else\r
164                 write->set_type(core::video_mode::progressive);\r
165 \r
166         if(desc.pix_fmt == core::pixel_format::invalid)\r
167         {\r
168                 std::shared_ptr<SwsContext> sws_context;\r
169 \r
170                 //CASPAR_LOG(warning) << "Hardware accelerated color transform not supported.";\r
171 \r
172                 size_t key = width << 20 | height << 8 | pix_fmt;\r
173                         \r
174                 auto& pool = sws_contexts_[key];\r
175                                                 \r
176                 if(!pool.try_pop(sws_context))\r
177                 {\r
178                         double param;\r
179                         sws_context.reset(sws_getContext(width, height, pix_fmt, width, height, PIX_FMT_BGRA, SWS_BILINEAR, nullptr, nullptr, &param), sws_freeContext);\r
180                 }\r
181                         \r
182                 if(!sws_context)\r
183                 {\r
184                         BOOST_THROW_EXCEPTION(operation_failed() << msg_info("Could not create software scaling context.") << \r
185                                                                         boost::errinfo_api_function("sws_getContext"));\r
186                 }       \r
187 \r
188                 // Use sws_scale when provided colorspace has no hw-accel.\r
189                 safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);     \r
190                 avcodec_get_frame_defaults(av_frame.get());                     \r
191                 avpicture_fill(reinterpret_cast<AVPicture*>(av_frame.get()), write->image_data().begin(), PIX_FMT_BGRA, width, height);\r
192                  \r
193                 sws_scale(sws_context.get(), decoded_frame->data, decoded_frame->linesize, 0, height, av_frame->data, av_frame->linesize);      \r
194                 pool.push(sws_context);\r
195 \r
196                 write->commit();\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                                         memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize);\r
212                         });\r
213 \r
214                         write->commit(n);\r
215                 });\r
216         }\r
217 \r
218         return write;\r
219 }\r
220 \r
221 }