record_theora.cpp
Go to the documentation of this file.
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 


corobot_camera
Author(s):
autogenerated on Sun Oct 5 2014 23:17:57