26 #define _FILE_OFFSET_BITS 64
33 #define ERRMSGSIZ 1024
35 static int ilog(
unsigned _v)
38 for(ret = 0; _v; ret++)_v >>= 1;
50 ycbcr[0].data =
nullptr;
51 ycbcr[1].data =
nullptr;
52 ycbcr[2].data =
nullptr;
57 delete [] ycbcr[0].data;
58 delete [] ycbcr[1].data;
59 delete [] ycbcr[2].data;
79 return allocateBuffers();
82 bool TheoraRecorder::allocateBuffers()
85 uint16_t yuv_w = (
rawWidth + 15) & ~15;
90 if (!ycbcr[0].data || yuv_w != ycbcr[0].width || yuv_h != ycbcr[0].height)
92 ycbcr[0].width = yuv_w;
93 ycbcr[0].height = yuv_h;
94 ycbcr[0].stride = yuv_w;
108 ycbcr[1].width = (chroma_format == TH_PF_444) ? yuv_w : (yuv_w >> 1);
109 ycbcr[1].stride = ycbcr[1].width;
110 ycbcr[1].height = (chroma_format == TH_PF_420) ? (yuv_h >> 1) : yuv_h;
111 ycbcr[2].width = ycbcr[1].width;
112 ycbcr[2].stride = ycbcr[1].stride;
113 ycbcr[2].height = ycbcr[1].height;
118 delete [] ycbcr[0].data;
119 delete [] ycbcr[1].data;
120 delete [] ycbcr[2].data;
122 ycbcr[0].data =
new uint8_t[ycbcr[0].stride * ycbcr[0].height];
123 ycbcr[1].data =
new uint8_t[ycbcr[1].stride * ycbcr[1].height];
124 ycbcr[2].data =
new uint8_t[ycbcr[2].stride * ycbcr[2].height];
127 ycbcr[0].data =
new uint8_t[ycbcr[0].stride * ycbcr[0].height];
145 snprintf(errmsg,
ERRMSGSIZ,
"Soft rate target requested without a bitrate.");
149 if(video_quality == -1)
156 if(video_quality == -1)
160 if(keyframe_frequency <= 0)
164 keyframe_frequency = twopass ? 256 : 64;
167 ogg_fp = fopen(filename,
"wb");
170 snprintf(errmsg,
ERRMSGSIZ,
"%s: error: could not open output file", filename);
174 srand(time(
nullptr));
175 if(ogg_stream_init(&ogg_os, rand()))
177 snprintf(errmsg,
ERRMSGSIZ,
"%s: error: could not create ogg stream state", filename);
182 ti.frame_width = ((
rawWidth + 15) >> 4) << 4;
183 ti.frame_height = ((
rawHeight + 15) >> 4) << 4;
188 frac(
m_FPS, video_fps_numerator, video_fps_denominator);
189 ti.fps_numerator = video_fps_numerator;
190 ti.fps_denominator = video_fps_denominator;
191 ti.aspect_numerator = video_aspect_numerator;
192 ti.aspect_denominator = video_aspect_denominator;
193 ti.colorspace = TH_CS_UNSPECIFIED;
194 ti.pixel_fmt =
static_cast<th_pixel_fmt
>(chroma_format);
195 ti.target_bitrate = video_rate;
196 ti.quality = video_quality;
197 ti.keyframe_granule_shift = ilog(keyframe_frequency - 1);
199 td = th_encode_alloc(&ti);
203 int ret = th_encode_ctl(td, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE, &keyframe_frequency,
sizeof(keyframe_frequency - 1));
206 snprintf(errmsg,
ERRMSGSIZ,
"Could not set keyframe interval to %d.", (
int)keyframe_frequency);
211 ret = th_encode_ctl(td, TH_ENCCTL_SET_VP3_COMPATIBLE, &vp3_compatible,
sizeof(vp3_compatible));
212 if(ret < 0 || !vp3_compatible)
214 snprintf(errmsg,
ERRMSGSIZ,
"Could not enable strict VP3 compatibility.");
221 int arg = TH_RATECTL_CAP_UNDERFLOW;
222 ret = th_encode_ctl(td, TH_ENCCTL_SET_RATE_FLAGS, &arg,
sizeof(arg));
224 snprintf(errmsg,
ERRMSGSIZ,
"Could not set encoder flags for soft-target");
226 if(!twopass && buf_delay < 0)
228 if((keyframe_frequency * 7 >> 1) > 5 * video_fps_numerator / video_fps_denominator)
229 arg = keyframe_frequency * 7 >> 1;
231 arg = 5 * video_fps_numerator / video_fps_denominator;
232 ret = th_encode_ctl(td, TH_ENCCTL_SET_RATE_BUFFER, &arg,
sizeof(arg));
234 snprintf(errmsg,
ERRMSGSIZ,
"Could not set rate control buffer for soft-target");
243 bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &
buffer,
sizeof(
buffer));
254 if(fseek(twopass_file, 0, SEEK_SET) < 0)
260 if(fwrite(
buffer, 1, bytes, twopass_file) <
static_cast<size_t>(bytes))
262 IDLog(
"Unable to write to two-pass data file.");
266 fflush(twopass_file);
276 if(th_encode_ctl(td, TH_ENCCTL_2PASS_IN,
nullptr, 0) < 0)
278 snprintf(errmsg,
ERRMSGSIZ,
"Could not set up the second pass of two-pass mode.");
283 if(fseek(twopass_file, 0, SEEK_SET) < 0)
285 snprintf(errmsg,
ERRMSGSIZ,
"Unable to seek in two-pass data file.");
292 if(passno != 1 && buf_delay >= 0)
294 ret = th_encode_ctl(td, TH_ENCCTL_SET_RATE_BUFFER, &buf_delay,
sizeof(buf_delay));
297 snprintf(errmsg,
ERRMSGSIZ,
"Warning: could not set desired buffer delay.");
302 th_comment_init(&tc);
304 if(th_encode_flushheader(td, &tc, &op) <= 0)
306 snprintf(errmsg,
ERRMSGSIZ,
"Internal Theora library error.");
310 th_comment_clear(&tc);
313 ogg_stream_packetin(&ogg_os, &op);
314 if(ogg_stream_pageout(&ogg_os, &og) != 1)
316 snprintf(errmsg,
ERRMSGSIZ,
"Internal Ogg library error.");
320 fwrite(og.header, 1, og.header_len, ogg_fp);
321 fwrite(og.body, 1, og.body_len, ogg_fp);
327 ret = th_encode_flushheader(td, &tc, &op);
330 snprintf(errmsg,
ERRMSGSIZ,
"Internal Theora library error.");
336 ogg_stream_packetin(&ogg_os, &op);
345 int result = ogg_stream_flush(&ogg_os, &og);
349 snprintf(errmsg,
ERRMSGSIZ,
"Internal Ogg library error.");
354 fwrite(og.header, 1, og.header_len, ogg_fp);
355 fwrite(og.body, 1, og.body_len, ogg_fp);
366 theora_write_frame(1);
372 int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &
buffer,
sizeof(
buffer));
375 IDLog(
"Could not read two-pass summary data from encoder.");
378 if(fseek(twopass_file, 0, SEEK_SET) < 0)
380 IDLog(
"Unable to seek in two-pass data file.");
384 if(fwrite(
buffer, 1, bytes, twopass_file) <
static_cast<size_t>(bytes))
386 IDLog(
"Unable to write to two-pass data file.");
389 fflush(twopass_file);
398 if(ogg_stream_flush(&ogg_os, &og))
400 fwrite(og.header, og.header_len, 1, ogg_fp);
401 fwrite(og.body, og.body_len, 1, ogg_fp);
410 ogg_stream_clear(&ogg_os);
412 fclose(twopass_file);
425 memcpy(ycbcr[0].data, frame, ycbcr[0].stride * ycbcr[0].height);
427 memset(ycbcr[1].data, 0x80, ycbcr[1].stride * ycbcr[1].height);
428 memset(ycbcr[2].data, 0x80, ycbcr[2].stride * ycbcr[2].height);
442 theora_write_frame(0);
448 bool TheoraRecorder::writeFrameMono(uint8_t *frame)
453 int offset = ((
rawWidth * subY) + subX);
455 uint8_t *srcBuffer = frame + offset;
456 uint8_t *destBuffer = frame;
457 int imageWidth = subW;
458 int imageHeight = subH;
460 for (
int i = 0; i < imageHeight; i++)
461 memcpy(destBuffer + i * imageWidth, srcBuffer +
rawWidth * i, imageWidth);
467 bool TheoraRecorder::writeFrameColor(uint8_t *frame)
472 int offset = ((
rawWidth * subY) + subX);
474 uint8_t *srcBuffer = frame + offset * 3;
475 uint8_t *destBuffer = frame;
476 int imageWidth = subW;
477 int imageHeight = subH;
480 for (
int i = 0; i < imageHeight; i++)
481 memcpy(destBuffer + i * imageWidth * 3, srcBuffer +
rawWidth * 3 * i, imageWidth * 3);
490 int TheoraRecorder::theora_write_frame(
int last)
497 if( (rc = th_encode_ycbcr_in(td, ycbcr)) )
499 IDLog(
"error: could not encode frame %d", rc);
507 int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &
buffer,
sizeof(
buffer));
511 IDLog(
"Could not read two-pass data from encoder.");
515 if(fwrite(
buffer, 1, bytes, twopass_file) <
static_cast<size_t>(bytes))
517 IDLog(
"Unable to write to two-pass data file.");
521 fflush(twopass_file);
524 if(!th_encode_packetout(td, last, &op))
526 IDLog(
"error: could not read packets");
532 ogg_stream_packetin(&ogg_os, &op);
533 while(ogg_stream_pageout(&ogg_os, &og))
535 fwrite(og.header, og.header_len, 1, ogg_fp);
536 fwrite(og.body, og.body_len, 1, ogg_fp);
564 bool TheoraRecorder::frac(
double fps, uint32_t &num, uint32_t &den)
575 m[0][0] = m[1][1] = 1;
576 m[0][1] = m[1][0] = 0;
579 while (m[1][0] * ( ai = (
long)x ) + m[1][1] <= maxden)
582 t = m[0][0] * ai + m[0][1];
585 t = m[1][0] * ai + m[1][1];
590 x = 1 / (x - (double) ai);
591 if(x > (
double)0x7FFFFFFF)
int BGR2YUV(int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip)
virtual ~TheoraRecorder()
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
virtual bool writeFrame(const uint8_t *frame, uint32_t nbytes, uint64_t timestamp)
virtual bool open(const char *filename, char *errmsg)
INDI_PIXEL_FORMAT m_PixelFormat
virtual bool setSize(uint16_t width, uint16_t height)
void IDLog(const char *fmt,...)
int decode_jpeg_raw(unsigned char *jpeg_data, int len, int itype, int ctype, unsigned int width, unsigned int height, unsigned char *raw0, unsigned char *raw1, unsigned char *raw2)
decode JPEG buffer
std::vector< uint8_t > buffer
Namespace to encapsulate INDI client, drivers, and mediator classes.