00001 #include <cstdlib>
00002 #include <cstring>
00003 #include <unistd.h>
00004 #include <cstdio>
00005 #include <csignal>
00006 #include <ros/time.h>
00007 #include "uvc_cam/uvc_cam.h"
00008 #include <theora/codec.h>
00009 #include <theora/theoraenc.h>
00010 #include <ogg/ogg.h>
00011 #include <vector>
00012 #include <string>
00013
00014 const unsigned WIDTH = 640, HEIGHT = 480, FPS = 30;
00015 static bool done = false;
00016
00017 void sigint_handler(int)
00018 {
00019 done = true;
00020 }
00021
00022 void logfilename(char *buf, uint32_t buf_len)
00023 {
00024 time_t t;
00025 time(&t);
00026 struct tm *lt = localtime(&t);
00027 strftime(buf, buf_len, "videos/%Y%m%d-%H%M%S.ogg", lt);
00028 }
00029
00030 void start_encoder(th_enc_ctx **context, ogg_stream_state *oggss, FILE **f)
00031 {
00032 th_info info;
00033 th_info_init(&info);
00034 info.frame_width = info.pic_width = WIDTH;
00035 info.frame_height = info.pic_height = HEIGHT;
00036 info.pic_x = info.pic_y = 0;
00037 info.colorspace = TH_CS_UNSPECIFIED;
00038 info.pixel_fmt = TH_PF_420;
00039
00040 info.target_bitrate = 0;
00041 info.quality = 50;
00042 info.keyframe_granule_shift = 6;
00043 info.fps_numerator = FPS;
00044 info.fps_denominator = 1;
00045 info.aspect_numerator = 1;
00046 info.aspect_denominator = 1;
00047
00048 *context = th_encode_alloc(&info);
00049 if (!(*context))
00050 {
00051 fprintf(stderr, "couldn't initialize the encoder\n");
00052 exit(1);
00053 }
00054 else
00055 printf("encoder ok\n");
00056
00057 th_comment comment;
00058 th_comment_init(&comment);
00059 th_comment_add(&comment, (char *)"created by the uvc_cam ROS package");
00060 comment.vendor = (char *)"uvc_cam ROS package";
00061
00062 ogg_stream_init(oggss, rand());
00063
00064 char fn[100];
00065 logfilename(fn, sizeof(fn));
00066 *f = fopen(fn, "w");
00067 if (!(*f))
00068 {
00069 fprintf(stderr, "couldn't open output file\n");
00070 exit(1);
00071 }
00072
00073 ogg_packet oggpacket;
00074 ogg_page oggpage;
00075 while (th_encode_flushheader(*context, &comment, &oggpacket) > 0)
00076 {
00077 ogg_stream_packetin(oggss, &oggpacket);
00078 while (ogg_stream_pageout(oggss, &oggpage))
00079 {
00080 fwrite(oggpage.header, oggpage.header_len, 1, *f);
00081 fwrite(oggpage.body , oggpage.body_len , 1, *f);
00082 }
00083 }
00084
00085
00086 while (ogg_stream_flush(oggss, &oggpage) > 0)
00087 {
00088 fwrite(oggpage.header, oggpage.header_len, 1, *f);
00089 fwrite(oggpage.body , oggpage.body_len , 1, *f);
00090 }
00091 }
00092
00093 void shutdown_encoder(th_enc_ctx **context, ogg_stream_state *oggss, FILE **f)
00094 {
00095 printf("flushing previous log...\n");
00096 ogg_page oggpage;
00097 while (ogg_stream_flush(oggss, &oggpage) > 0)
00098 {
00099 fwrite(oggpage.header, oggpage.header_len, 1, *f);
00100 fwrite(oggpage.body , oggpage.body_len , 1, *f);
00101 }
00102 fclose(*f);
00103 th_encode_free(*context);
00104 ogg_stream_clear(oggss);
00105 *f = NULL;
00106 *context = NULL;
00107 }
00108
00109 int main(int argc, char **argv)
00110 {
00111 if (argc != 2)
00112 {
00113 fprintf(stderr, "usage: record_theora DEVICE\n");
00114 return 1;
00115 }
00116 signal(SIGINT, sigint_handler);
00117 srand(time(NULL));
00118
00119 uvc_cam::Cam cam(argv[1], uvc_cam::Cam::MODE_YUYV, WIDTH, HEIGHT, FPS);
00120 cam.set_control(0x9a0901, 3);
00121 cam.set_control(0x9a0903, 1);
00122 ros::Time t_prev(ros::Time::now());
00123 int count = 0;
00124 ogg_stream_state oggss;
00125 ogg_page oggpage;
00126 ogg_packet oggpacket;
00127
00128 th_enc_ctx *context;
00129 FILE *ogg_file;
00130 start_encoder(&context, &oggss, &ogg_file);
00131
00132 th_img_plane planes[3];
00133
00134 planes[0].width = WIDTH;
00135 planes[0].stride = WIDTH;
00136 planes[0].height = HEIGHT;
00137
00138 planes[1].width = planes[2].width = WIDTH/2;
00139 planes[1].stride = planes[2].stride = WIDTH/2;
00140 planes[1].height = planes[2].height = HEIGHT/2;
00141
00142 planes[0].data = new uint8_t[WIDTH*HEIGHT];
00143 planes[1].data = new uint8_t[WIDTH*HEIGHT/4];
00144 planes[2].data = new uint8_t[WIDTH*HEIGHT/4];
00145
00146 ros::Time log_start(ros::Time::now());
00147 while (!done)
00148 {
00149
00150 ros::Time t(ros::Time::now());
00151 ros::Duration log_duration(t - log_start);
00152 if (log_duration.toSec() > 60*60)
00153 {
00154 shutdown_encoder(&context, &oggss, &ogg_file);
00155 log_start = t;
00156 start_encoder(&context, &oggss, &ogg_file);
00157 }
00158
00159 uint8_t *frame = NULL;
00160 uint32_t bytes_used;
00161 int buf_idx = cam.grab(&frame, bytes_used);
00162 if (count++ % 30 == 0)
00163 {
00164 ros::Duration d(t - t_prev);
00165 printf("%.1f fps\n", 30.0 / d.toSec());
00166 t_prev = t;
00167 }
00168 if (frame)
00169 {
00170
00171 uint8_t *pframe = frame;
00172 uint8_t *py = planes[0].data;
00173 uint8_t *pcr = planes[1].data;
00174 uint8_t *pcb = planes[2].data;
00175 for (uint32_t row = 0; row < HEIGHT; row+=2)
00176 {
00177
00178 for (uint32_t col = 0; col < WIDTH; col+=2)
00179 {
00180 *py++ = *pframe++;
00181 *pcr++ = *pframe++;
00182 *py++ = *pframe++;
00183 *pcb++ = *pframe++;
00184 }
00185 for (uint32_t col = 0; col < WIDTH; col+=2)
00186 {
00187 *py++ = *pframe++;
00188 pframe++;
00189 *py++ = *pframe++;
00190 pframe++;
00191 }
00192 }
00193 cam.release(buf_idx);
00194 int ret = th_encode_ycbcr_in(context, planes);
00195 if (ret == TH_EFAULT)
00196 printf("EFAULT while encoding\n");
00197 else if (ret == TH_EINVAL)
00198 printf("EINVAL while encoding\n");
00199
00200 while (th_encode_packetout(context, 0, &oggpacket) > 0)
00201 {
00202 ogg_stream_packetin(&oggss, &oggpacket);
00203 while (ogg_stream_pageout(&oggss, &oggpage))
00204 {
00205 fwrite(oggpage.header, oggpage.header_len, 1, ogg_file);
00206 fwrite(oggpage.body , oggpage.body_len , 1, ogg_file);
00207 }
00208 }
00209 }
00210 }
00211 shutdown_encoder(&context, &oggss, &ogg_file);
00212
00213 return 0;
00214 }
00215