streamtools: Refactor options parsing and RGB->I420 conversion
[jpcrr.git] / streamtools / output-drv.cpp
blob5230bf4f1a2c83109001cb833165463f0d3467d0
1 #include "output-drv.hpp"
2 #include <stdexcept>
3 #include <list>
4 #include <cstdio>
5 #include <sstream>
6 #include "dedup.hpp"
7 #include "rgbtorgb.hh"
9 class audio_dyncall_null : public audio_dyncall_base
11 void operator()(short left, short right)
16 class audio_end_dyncall_null : public audio_end_dyncall_base
18 void operator()()
23 class video_dyncall_null : public video_dyncall_base
25 void operator()(uint64_t timestamp, const uint8_t* raw_rgbx_data)
30 class subtitle_dyncall_null : public subtitle_dyncall_base
32 void operator()(uint64_t basetime, uint64_t duration, const uint8_t* text)
37 audio_settings::audio_settings(uint32_t _rate)
39 rate = _rate;
42 uint32_t audio_settings::get_rate() const
44 return rate;
47 audio_dyncall_base::~audio_dyncall_base()
51 audio_end_dyncall_base::~audio_end_dyncall_base()
55 video_settings::video_settings(uint32_t _width, uint32_t _height, uint32_t _rate_num, uint32_t _rate_denum)
57 width = _width;
58 height = _height;
59 rate_num = _rate_num;
60 rate_denum = _rate_denum;
63 uint32_t video_settings::get_width() const
65 return width;
68 uint32_t video_settings::get_height() const
70 return height;
73 uint32_t video_settings::get_rate_num() const
75 return rate_num;
78 uint32_t video_settings::get_rate_denum() const
80 return rate_denum;
83 video_dyncall_base::~video_dyncall_base()
87 subtitle_settings::subtitle_settings()
91 subtitle_dyncall_base::~subtitle_dyncall_base()
95 const audio_settings& output_driver::get_audio_settings()
97 return asettings;
100 const video_settings& output_driver::get_video_settings()
102 return vsettings;
105 const subtitle_settings& output_driver::get_subtitle_settings()
107 return ssettings;
110 output_driver::output_driver()
111 : asettings(0), vsettings(0, 0, 0, 0), ssettings()
113 audio_handler = new audio_dyncall_null();
114 audio_end_handler = new audio_end_dyncall_null();
115 video_handler = new video_dyncall_null();
116 subtitle_handler = new subtitle_dyncall_null();
119 output_driver::~output_driver()
121 delete audio_handler;
122 delete audio_end_handler;
123 delete video_handler;
124 delete subtitle_handler;
127 void output_driver::do_audio_callback(short left, short right)
129 (*audio_handler)(left, right);
132 void output_driver::do_audio_end_callback()
134 (*audio_end_handler)();
137 void output_driver::do_video_callback(uint64_t timestamp, const uint8_t* raw_rgbx_data)
139 (*video_handler)(timestamp, raw_rgbx_data);
142 void output_driver::do_subtitle_callback(uint64_t basetime, uint64_t duration, const uint8_t* text)
144 (*subtitle_handler)(basetime, duration, text);
147 void output_driver::set_audio_settings(audio_settings a)
149 asettings = a;
152 void output_driver::set_video_settings(video_settings v)
154 vsettings = v;
157 void output_driver::set_subtitle_settings(subtitle_settings s)
159 ssettings = s;
162 std::map<std::string, output_driver_factory*>* output_driver_factory::factories;
164 output_driver_factory::~output_driver_factory()
168 output_driver_factory::output_driver_factory(const std::string& type)
170 if(!factories)
171 factories = new std::map<std::string, output_driver_factory*>();
172 (*factories)[type] = this;
175 output_driver& output_driver_factory::make_by_type(const std::string& type, const std::string& name,
176 const std::string& parameters)
178 if(!factories || !factories->count(type))
179 throw std::runtime_error("Unknown output driver type");
180 return (*factories)[type]->make(type, name, parameters);
183 namespace
185 std::list<output_driver*> drivers;
186 audio_settings asettings(0);
187 video_settings vsettings(0, 0, 0, 0);
188 subtitle_settings ssettings;
189 dedup dedupper(0, 0, 0);
192 void distribute_audio_callback(short left, short right)
194 for(std::list<output_driver*>::iterator i = drivers.begin(); i != drivers.end(); ++i)
195 (*i)->do_audio_callback(left, right);
197 void distribute_video_callback(uint64_t timestamp, const uint8_t* raw_rgbx_data)
199 for(std::list<output_driver*>::iterator i = drivers.begin(); i != drivers.end(); ++i)
200 (*i)->do_video_callback(timestamp, raw_rgbx_data);
203 void distribute_subtitle_callback(uint64_t basetime, uint64_t duration, const uint8_t* text)
205 for(std::list<output_driver*>::iterator i = drivers.begin(); i != drivers.end(); ++i)
206 (*i)->do_subtitle_callback(basetime, duration, text);
209 void set_audio_parameters(audio_settings a)
211 asettings = a;
214 void set_video_parameters(video_settings v)
216 vsettings = v;
219 void set_subtitle_parameters(subtitle_settings s)
221 ssettings = s;
224 void add_output_driver(const std::string& type, const std::string& name, const std::string& parameters)
226 output_driver& driver = output_driver_factory::make_by_type(type, name, parameters);
227 drivers.push_back(&driver);
228 driver.set_audio_settings(asettings);
229 driver.set_video_settings(vsettings);
230 driver.set_subtitle_settings(ssettings);
231 driver.ready();
234 void close_output_drivers()
236 for(std::list<output_driver*>::iterator i = drivers.begin(); i != drivers.end(); ++i)
237 (*i)->do_audio_end_callback();
238 for(std::list<output_driver*>::iterator i = drivers.begin(); i != drivers.end(); ++i)
239 delete *i;
240 drivers.clear();
243 void distribute_audio_callback(npair<short> sample)
245 distribute_audio_callback(sample.get_x(), sample.get_y());
248 void distribute_subtitle_callback(struct packet& p)
250 uint64_t basetime = p.rp_timestamp;
251 uint64_t duration = 0;
252 uint8_t* text = NULL;
253 if(p.rp_major != 4 || p.rp_minor != 0 || p.rp_payload.size() < 8)
254 return; //Bad subtitle packet.
255 for(size_t i = 0; i < 8; i++)
256 duration |= ((uint64_t)p.rp_payload[i] << (56 - 8 * i));
257 text = &p.rp_payload[8];
258 distribute_subtitle_callback(basetime, duration, text);
261 std::string get_output_driver_list()
263 bool first = true;
264 if(!output_driver_factory::factories)
265 return "";
266 std::string c;
267 std::map<std::string, output_driver_factory*>& f = *output_driver_factory::factories;
268 for(std::map<std::string, output_driver_factory*>::iterator i = f.begin(); i != f.end(); ++i) {
269 if(first)
270 c = i->first;
271 else
272 c = c + " " + i->first;
273 first = false;
275 return c;
278 std::string expand_arguments_common(std::string opts, std::string commaexpand, std::string equalsexpand)
280 bool insert = true;
281 bool first = true;
282 std::ostringstream ret;
283 for(size_t i = 0; i < opts.length(); i++) {
284 if(insert) {
285 if(first)
286 ret << commaexpand;
287 else
288 ret << " " << commaexpand;
290 first = false;
291 insert = false;
292 switch(opts[i]) {
293 case ',':
294 insert = true;
295 break;
296 case '=':
297 ret << equalsexpand;
298 break;
299 default:
300 ret << opts[i];
303 ret << " ";
304 return ret.str();
307 namespace
309 template <class T>
310 void I420_convert_common(const uint8_t* raw_rgbx_data, uint32_t width, uint32_t height, bool uvswap,
311 void (*writer)(T target, const uint8_t* buffer, size_t bufsize), T target)
313 size_t framesize = 4 * (size_t)width * height;
314 std::vector<unsigned char> tmp(framesize * 3 / 8);
315 size_t primarysize = framesize / 4;
316 size_t offs1 = 0;
317 size_t offs2 = primarysize / 4;
318 if(uvswap)
319 std::swap(offs1, offs2);
320 Convert32To_I420Frame(raw_rgbx_data, &tmp[0], framesize / 4, width);
321 writer(target, &tmp[0], primarysize);
322 writer(target, &tmp[primarysize + offs1], primarysize / 4);
323 writer(target, &tmp[primarysize + offs2], primarysize / 4);
326 void writer_stdio(FILE* target, const uint8_t* buffer, size_t bufsize)
328 size_t r;
329 if((r = fwrite(buffer, 1, bufsize, target)) < bufsize) {
330 std::stringstream str;
331 str << "Error writing frame to output (requested " << bufsize << ", got " << r << ")";
332 throw std::runtime_error(str.str());
336 void writer_iostream(std::ostream* target, const uint8_t* buffer, size_t bufsize)
338 target->write((const char*)buffer, bufsize);
339 if(!*target) {
340 std::stringstream str;
341 str << "Error writing frame to output (requested " << bufsize << ")";
342 throw std::runtime_error(str.str());
347 void I420_convert_common(const uint8_t* raw_rgbx_data, uint32_t width, uint32_t height, FILE* out, bool uvswap)
349 I420_convert_common(raw_rgbx_data, width, height, uvswap, writer_stdio, out);
352 void I420_convert_common(const uint8_t* raw_rgbx_data, uint32_t width, uint32_t height, std::ostream& out,
353 bool uvswap)
355 I420_convert_common(raw_rgbx_data, width, height, uvswap, writer_iostream, &out);