$search
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 //info.target_bitrate = 800000; 00040 info.target_bitrate = 0; 00041 info.quality = 50; 00042 info.keyframe_granule_shift = 6; // max keyframe interval = 1 << 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 // finish the headers, to begin a new page 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); // aperture priority exposure mode 00121 cam.set_control(0x9a0903, 1); // auto exposure 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 // see if it's time to start a new logfile 00150 ros::Time t(ros::Time::now()); 00151 ros::Duration log_duration(t - log_start); 00152 if (log_duration.toSec() > 60*60) // new log every hour 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 // separate out the luma/chroma planes, as required by theora 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 // skip every other chrominance row, to go from V4L2 YUYV to YUV420 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++; // skip cr 00189 *py++ = *pframe++; 00190 pframe++; // skip cb 00191 } 00192 } 00193 cam.release(buf_idx); // give the image back to the kernel 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