oggtheoracapture.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:16k
源码类别:

OpenGL

开发平台:

Visual C++

  1. /*
  2.  *  Copyright (C) 2006, William K Volkman <wkvsf@users.sourceforge.net>
  3.  * 
  4.  *  This program is free software; you can redistribute it and/or
  5.  *  modify it under the terms of the GNU General Public License
  6.  *  as published by the Free Software Foundation; either version 2
  7.  *  of the License, or (at your option) any later version.
  8.  *
  9.  * http://www.fourcc.org/fccyvrgb.php
  10.  * Ey = 0.299R+0.587G+0.114B
  11.  * Ecr = 0.713(R - Ey) = 0.500R-0.419G-0.081B
  12.  * Ecb = 0.564(B - Ey) = -0.169R-0.331G+0.500B
  13.  *
  14.  * the defined range for Y is [16,235] (220 steps) and the valid ranges
  15.  * for Cr and Cb are [16,239] (235 steps) 
  16.  *
  17.  * http://www.neuro.sfc.keio.ac.jp/~aly/polygon/info/color-space-faq.html
  18.  *  RGB -> YUV                    | YUV -> RGB
  19.  *  Y =  0.299*Red+0.587*Green+0.114*Blue    | Red   = Y+0.000*U+1.140*V
  20.  *  U = -0.147*Red-0.289*Green+0.436*Blue    | Green = Y-0.396*U-0.581*V
  21.  *  V =  0.615*Red-0.515*Green-0.100*Blue    | Blue  = Y+2.029*U+0.000*V
  22.  *
  23.  *  +----------------+---------------+-----------------+----------------+
  24.  *  | Recommendation | Coef. for red | Coef. for Green | Coef. for Blue |
  25.  *  +----------------+---------------+-----------------+----------------+
  26.  *  | Rec 601-1      | 0.299         | 0.587           | 0.114          |
  27.  *  | Rec 709        | 0.2125        | 0.7154          | 0.0721         |
  28.  *  | ITU            | 0.2125        | 0.7154          | 0.0721         |
  29.  *  +----------------+---------------+-----------------+----------------+
  30.  *  RGB -> YCbCr
  31.  *  Y  = Coef. for red*Red+Coef. for green*Green+Coef. for blue*Blue
  32.  *  Cb = (Blue-Y)/(2-2*Coef. for blue)
  33.  *  Cr = (Red-Y)/(2-2*Coef. for red)
  34.  *
  35.  *  RGB -> YCbCr (with Rec 601-1 specs)        | YCbCr (with Rec 601-1 specs) -> RGB
  36.  *  Y=  0.2989*Red+0.5866*Green+0.1145*Blue    | Red=  Y+0.0000*Cb+1.4022*Cr
  37.  *  Cb=-0.1687*Red-0.3312*Green+0.5000*Blue    | Green=Y-0.3456*Cb-0.7145*Cr
  38.  *  Cr= 0.5000*Red-0.4183*Green-0.0816*Blue    | Blue= Y+1.7710*Cb+0.0000*Cr
  39.  *
  40.  * http://en.wikipedia.org/wiki/YUV/RGB_conversion_formulas
  41.  * Y := min(abs(r * 2104 + g * 4130 + b * 802 + 4096 + 131072) >> 13,235)
  42.  * U := min(abs(r * -1214 + g * -2384 + b * 3598 + 4096 + 1048576) >> 13,240)
  43.  * V := min(abs(r * 3598 + g * -3013 + b * -585 + 4096 + 1048576) >> 13,240)
  44.  */
  45. #ifndef _GNU_SOURCE
  46. # define _GNU_SOURCE
  47. #endif
  48. #define _LARGEFILE_SOURCE
  49. #define _LARGEFILE64_SOURCE
  50. #define _FILE_OFFSET_BITS 64
  51. #ifdef HAVE_CONFIG_H
  52. # include <config.h>
  53. #endif
  54. #ifndef _REENTRANT
  55. # define _REENTRANT
  56. #endif
  57. #include <cstdlib>
  58. #include <cstdio>
  59. #include <cmath>
  60. #include <celutil/debug.h>
  61. #include <celutil/util.h>
  62. #include "../celengine/gl.h"
  63. #include <string>
  64. #include <cstring>
  65. #include "theora/theora.h"
  66. using namespace std;
  67. #include "oggtheoracapture.h"
  68. //  {"video-rate-target",required_argument,NULL,'V'},
  69. //  {"video-quality",required_argument,NULL,'v'},
  70. //  {"aspect-numerator",optional_argument,NULL,'s'},
  71. //  {"aspect-denominator",optional_argument,NULL,'S'},
  72. //  {"framerate-numerator",optional_argument,NULL,'f'},
  73. //  {"framerate-denominator",optional_argument,NULL,'F'},
  74. OggTheoraCapture::OggTheoraCapture():
  75.     video_x(0),
  76.     video_y(0),
  77.     frame_x(0),
  78.     frame_y(0),
  79.     frame_x_offset(0),
  80.     frame_y_offset(0),
  81.     video_an(4),
  82.     video_ad(3),
  83.     video_hzn(12),
  84.     video_hzd(1),
  85.     video_r(-1), // 45000 <= video_r <= 2000000 (45Kbps - 2000Kbps)
  86.     video_q(63), // 0-63 aka 0-10 * 6.3 the higher the value the faster the encoding and the larger the output file
  87.     capturing(false),
  88.     video_frame_count(0),
  89.     video_bytesout(0),
  90.     rowStride(0),
  91.     pixels(NULL),
  92.     outfile(NULL)
  93. {
  94.     yuvframe[0] = NULL;
  95.     yuvframe[1] = NULL;
  96.     // Just being anal
  97.     memset(&yuv, 0, sizeof(yuv));
  98.     memset(&to, 0, sizeof(to));
  99.     memset(&videopage, 0, sizeof(videopage));
  100.     memset(&op, 0, sizeof(op));
  101.     memset(&td, 0, sizeof(td));
  102.     memset(&ti, 0, sizeof(ti));
  103.     memset(&tc, 0, sizeof(tc));
  104. }
  105. void OggTheoraCapture::setAspectRatio(int aspect_numerator, int aspect_denominator)
  106. {
  107.     int    a = aspect_numerator;
  108.     int    b = aspect_denominator;
  109.     while (a != b)
  110.     {
  111.         if (a > b)
  112.             a = a - b;
  113.         else
  114.             b = b - a;
  115.     }
  116.     if (a > 1) {
  117.         video_an = aspect_numerator / a;
  118.         video_ad = aspect_denominator / a;
  119.     } 
  120.     else
  121.     {
  122.         video_an = aspect_numerator;
  123.         video_ad = aspect_denominator;
  124.     }
  125. }
  126. void OggTheoraCapture::setQuality(float quality)
  127. {
  128.     if (quality < 0.0)
  129.         video_q = 7;
  130.     else if (quality < 1.0)
  131.         video_q = 0;
  132.     else if (quality <= 10.00)
  133.         video_q = (int)ceil(quality * 6.3);
  134.     else
  135.         video_q = (int)ceil(quality);
  136.     
  137. }
  138. bool OggTheoraCapture::start(const std::string& filename,
  139.                  int w, int h,
  140.                  float fps)
  141. {
  142.     if (capturing)
  143.         return false;
  144.     outfile = fopen(filename.c_str(), "wb");
  145.     if (!outfile)
  146.     {
  147.         DPRINTF(0, _("Error in creating ogg file %s for capture.n"), filename.c_str());
  148.         return false;
  149.     }
  150.     /* Set up Ogg output stream */
  151. #ifdef _WIN32
  152. std::srand(std::time(NULL));
  153. #else
  154. std::srand(time(NULL));
  155. #endif
  156.     ogg_stream_init(&to,std::rand());
  157.     frame_x = w;
  158.     frame_y = h;
  159.     if (fps > 0.05) {
  160.         if (fabs(fps - (30000.0/1001.0)) < 1e-5)
  161.         {
  162.             video_hzn = 30000;
  163.             video_hzd = 1001;
  164.         }
  165.         else if (fabs(fps - (24000.0/1001.0)) < 1e-5)
  166.         {
  167.             video_hzn = 24000;
  168.             video_hzd = 1001;
  169.         }
  170.         else
  171.         {
  172.             video_hzn = (int)ceil(fps*1000.0);
  173.             video_hzd = 1000;
  174.             int    a = video_hzn;
  175.             int    b = video_hzd;
  176.             while (a != b)
  177.             {
  178.                 if (a > b)
  179.                     a = a - b;
  180.                 else
  181.                     b = b - a;
  182.             }
  183.             if (a > 1)
  184.             {
  185.                 video_hzn /= a;
  186.                 video_hzd /= a;
  187.             }
  188.         }
  189.     }
  190.     /* Theora has a divisible-by-sixteen restriction for the encoded video size */
  191.     /* scale the frame size up to the nearest /16 and calculate offsets */
  192.     video_x=((frame_x + 15) >>4)<<4;
  193.     video_y=((frame_y + 15) >>4)<<4;
  194.     /* We force the offset to be even.
  195.        This ensures that the chroma samples align properly with the luma
  196.        samples. */
  197.     frame_x_offset=((video_x-frame_x)/2)&~1;
  198.     frame_y_offset=((video_y-frame_y)/2)&~1;
  199.     theora_info_init(&ti);
  200.     ti.width=video_x;
  201.     ti.height=video_y;
  202.     ti.frame_width=frame_x;
  203.     ti.frame_height=frame_y;
  204.     ti.offset_x=frame_x_offset;
  205.     ti.offset_y=frame_y_offset;
  206.     ti.fps_numerator=video_hzn;
  207.     ti.fps_denominator=video_hzd;
  208.     ti.aspect_numerator=video_an;
  209.     ti.aspect_denominator=video_ad;
  210.     if (frame_x == 720 && frame_y == 576)
  211.         ti.colorspace=OC_CS_ITU_REC_470BG; //OC_CS_UNSPECIFIED;
  212.     else
  213.         ti.colorspace=OC_CS_ITU_REC_470M; //OC_CS_UNSPECIFIED;
  214.     //ti.pixelformat=OC_PF_420;
  215.     ti.target_bitrate=video_r;
  216.     ti.quality=video_q;
  217.     ti.dropframes_p=0;
  218.     ti.quick_p=1;
  219.     ti.keyframe_auto_p=1;
  220.     ti.keyframe_frequency=64;
  221.     ti.keyframe_frequency_force=64;
  222.     ti.keyframe_data_target_bitrate=(int)(video_r*1.5);
  223.     ti.keyframe_auto_threshold=80;
  224.     ti.keyframe_mindistance=8;
  225.     ti.noise_sensitivity=1;
  226.     theora_encode_init(&td,&ti);
  227.     theora_info_clear(&ti);
  228.     /* first packet will get its own page automatically */
  229.     theora_encode_header(&td,&op);
  230.     ogg_stream_packetin(&to,&op);
  231.     if(ogg_stream_pageout(&to,&videopage) != 1){
  232.         cerr << _("Internal Ogg library error.") << endl;
  233.         return false;
  234.     }
  235.     fwrite(videopage.header, 1, videopage.header_len, outfile);
  236.     fwrite(videopage.body,   1, videopage.body_len, outfile);
  237.     /* create the remaining theora headers */
  238.     theora_comment_init(&tc);
  239.     theora_encode_comment(&tc,&op);
  240.     theora_comment_clear(&tc);
  241.     ogg_stream_packetin(&to,&op);
  242.     theora_encode_tables(&td,&op);
  243.     ogg_stream_packetin(&to,&op);
  244.     while(1)
  245.     {
  246.         int result = ogg_stream_flush(&to,&videopage);
  247.         if( result<0 )
  248.         {
  249.             /* can't get here */
  250.             cerr << _("Internal Ogg library error.")  << endl;
  251.             return false;
  252.         }
  253.         if( result==0 )
  254.             break;
  255.         fwrite(videopage.header,1,videopage.header_len,outfile);
  256.         fwrite(videopage.body,1,  videopage.body_len,outfile);
  257.     }
  258.     /* Initialize the double frame buffer.
  259.      * We allocate enough for a 4:4:4 color sampling
  260.      */
  261.     yuvframe[0]= new unsigned char[video_x*video_y*3];
  262.     yuvframe[1]= new unsigned char[video_x*video_y*3];
  263.     // Now the buffer for reading the GL RGB pixels
  264.     rowStride = (frame_x * 3 + 3) & ~0x3;
  265.     pixels = new unsigned char[rowStride*frame_y];
  266.         /* clear initial frame as it may be larger than actual video data */
  267.         /* fill Y plane with 0x10 and UV planes with 0x80, for black data */
  268.     // The UV plane must be 4:2:0
  269.     memset(yuvframe[0],0x10,video_x*video_y);
  270.     memset(yuvframe[0]+video_x*video_y,0x80,video_x*video_y*2);
  271.     memset(yuvframe[1],0x10,video_x*video_y);
  272.     memset(yuvframe[1]+video_x*video_y,0x80,video_x*video_y*2);
  273.     yuv.y_width=video_x;
  274.     yuv.y_height=video_y;
  275.     yuv.y_stride=video_x;
  276.     // Note we lie here by saying it's 4:2:0 and we will convert 4:4:4 to 4:2;0 later
  277.     yuv.uv_width=video_x/2;
  278.     yuv.uv_height=video_y/2;
  279.     yuv.uv_stride=video_x/2;
  280.     printf(_("OggTheoraCapture::start() - Theora video: %s %.2f(%d/%d) fps quality %d %dx%d offset (%dx%d)n"),
  281.            filename.c_str(),
  282.            (double)video_hzn/(double)video_hzd,
  283.            video_hzn,video_hzd,
  284.            video_q,
  285.            video_x,video_y,
  286.            frame_x_offset,frame_y_offset);
  287.     capturing = true;
  288.     return true;
  289. }
  290. bool OggTheoraCapture::captureFrame()
  291. {
  292.     if (!capturing)
  293.         return false;
  294.     while (ogg_stream_pageout(&to,&videopage)>0)
  295.     {
  296.         /* flush a video page */
  297.         video_bytesout+=fwrite(videopage.header,1,videopage.header_len,outfile);
  298.         video_bytesout+=fwrite(videopage.body,1,videopage.body_len,outfile);
  299.     }
  300.     if(ogg_stream_eos(&to)) return false;
  301.     // Get the dimensions of the current viewport
  302.     int viewport[4];
  303.     glGetIntegerv(GL_VIEWPORT, viewport);
  304.     int x = viewport[0] + (viewport[2] - frame_x) / 2;
  305.     int y = viewport[1] + (viewport[3] - frame_y) / 2;
  306.     glReadPixels(x, y, frame_x, frame_y,
  307.              GL_RGB, GL_UNSIGNED_BYTE,
  308.              pixels);
  309.     unsigned char *ybase = yuvframe[0];
  310.     unsigned char *ubase = yuvframe[0]+ video_x*video_y;
  311.     unsigned char *vbase = yuvframe[0]+ video_x*video_y*2;
  312.     // We go ahead and build 4:4:4 frames
  313.     for (int y=0; y<frame_y; y++)
  314.     {
  315.         unsigned char *yptr = ybase + (video_x*(y+frame_y_offset))+frame_x_offset;
  316.         unsigned char *uptr = ubase + (video_x*(y+frame_y_offset))+frame_x_offset;
  317.         unsigned char *vptr = vbase + (video_x*(y+frame_y_offset))+frame_x_offset;
  318.         unsigned char *rgb = pixels + ((frame_y-1-y)*rowStride); // The video is inverted
  319.         for (int x=0; x<frame_x; x++)
  320.         {
  321.             unsigned char r = *rgb++;
  322.             unsigned char g = *rgb++;
  323.             unsigned char b = *rgb++;
  324.             *yptr++ = min(abs(r * 2104 + g * 4130 + b * 802 + 4096 + 131072) >> 13,235);
  325.             *uptr++ = min(abs(r * -1214 + g * -2384 + b * 3598 + 4096 + 1048576) >> 13,240);
  326.             *vptr++ = min(abs(r * 3598 + g * -3013 + b * -585 + 4096 + 1048576) >> 13,240);
  327.         }
  328.     }
  329.     /*
  330.      * The video strategy is to capture one frame ahead so when we're at end of
  331.      * stream we can mark last video frame as such.  Have two YUV frames before
  332.      * encoding. Theora is a one-frame-in,one-frame-out system; submit a frame
  333.      * for compression and pull out the packet
  334.      */
  335.     if (video_frame_count > 0)
  336.     {
  337.         yuv.y= yuvframe[1];
  338.         yuv.u= yuvframe[1]+ video_x*video_y;
  339.         yuv.v= yuvframe[1]+ video_x*video_y*2;
  340.         // Convert to 4:2:0
  341.         unsigned char * uin0 = yuv.u;
  342.         unsigned char * uin1 = yuv.u + video_x;
  343.         unsigned char * uout = yuv.u;
  344.         unsigned char * vin0 = yuv.v;
  345.         unsigned char * vin1 = yuv.v + video_x;
  346.         unsigned char * vout = yuv.v; 
  347.         for (int y = 0; y < video_y; y += 2)
  348.         {
  349.             for (int x = 0; x < video_x; x += 2)
  350.             {
  351.                 *uout = (uin0[0] + uin0[1] + uin1[0] + uin1[1]) >> 2;
  352.                 uin0 += 2;
  353.                 uin1 += 2;
  354.                 uout++;
  355.                 *vout = (vin0[0] + vin0[1] + vin1[0] + vin1[1]) >> 2;
  356.                 vin0 += 2;
  357.                 vin1 += 2;
  358.                 vout++;
  359.             }
  360.             uin0 += video_x;
  361.             uin1 += video_x;
  362.             vin0 += video_x;
  363.             vin1 += video_x;
  364.         }
  365.         theora_encode_YUVin(&td,&yuv);
  366.         theora_encode_packetout(&td,0,&op);
  367.         ogg_stream_packetin(&to,&op);
  368.     }
  369.     video_frame_count += 1;
  370.     //if ((video_frame_count % 10) == 0)
  371.     //    printf("Writing frame %dn", video_frame_count);
  372.     unsigned char *temp = yuvframe[0];
  373.     yuvframe[0] = yuvframe[1];
  374.     yuvframe[1] = temp;
  375.     frameCaptured();
  376.     return true;
  377. }
  378. void OggTheoraCapture::cleanup()
  379. {
  380.     capturing = false;
  381.     /* clear out state */
  382.     if(outfile)
  383.     {
  384.         printf(_("OggTheoraCapture::cleanup() - wrote %d framesn"), video_frame_count);
  385.         if (video_frame_count > 0)
  386.         {
  387.             yuv.y= yuvframe[1];
  388.             yuv.u= yuvframe[1]+ video_x*video_y;
  389.             yuv.v= yuvframe[1]+ video_x*video_y*2 ;
  390.             // Convert to 4:2:0
  391.             unsigned char * uin0 = yuv.u;
  392.             unsigned char * uin1 = yuv.u + video_x;
  393.             unsigned char * uout = yuv.u;
  394.             unsigned char * vin0 = yuv.v;
  395.             unsigned char * vin1 = yuv.v + video_x;
  396.             unsigned char * vout = yuv.v;
  397.             for (int y = 0; y < video_y; y += 2)
  398.             {
  399.                 for (int x = 0; x < video_x; x += 2)
  400.                 {
  401.                     *uout = (uin0[0] + uin0[1] + uin1[0] + uin1[1]) >> 2;
  402.                     uin0 += 2;
  403.                     uin1 += 2;
  404.                     uout++;
  405.                     *vout = (vin0[0] + vin0[1] + vin1[0] + vin1[1]) >> 2;
  406.                     vin0 += 2;
  407.                     vin1 += 2;
  408.                     vout++;
  409.                 }
  410.                 uin0 += video_x;
  411.                 uin1 += video_x;
  412.                 vin0 += video_x;
  413.                 vin1 += video_x;
  414.             }
  415.             theora_encode_YUVin(&td,&yuv);
  416.             theora_encode_packetout(&td,1,&op);
  417.             ogg_stream_packetin(&to,&op);
  418.         }
  419.         while(ogg_stream_pageout(&to,&videopage)>0)
  420.         {
  421.             /* flush a video page */
  422.             video_bytesout+=fwrite(videopage.header,1,videopage.header_len,outfile);
  423.             video_bytesout+=fwrite(videopage.body,1,videopage.body_len,outfile);
  424.         }
  425.         if(ogg_stream_flush(&to,&videopage)>0)
  426.         {
  427.             /* flush a video page */
  428.             video_bytesout+=fwrite(videopage.header,1,videopage.header_len,outfile);
  429.             video_bytesout+=fwrite(videopage.body,1,videopage.body_len,outfile);
  430.         }
  431.         theora_clear(&td);
  432.         ogg_stream_clear(&to);
  433.         //ogg_stream_destroy(&to); /* Documentation says to do this however we are seeing a double free libogg 1.1.2 */
  434.         std::fclose(outfile);
  435.         outfile = NULL;
  436.         delete [] yuvframe[0];
  437.         delete [] yuvframe[1];
  438.         delete [] pixels;
  439.     }
  440. }
  441. bool OggTheoraCapture::end()
  442. {
  443.     cleanup();
  444.     return true;
  445. }
  446. int OggTheoraCapture::getWidth() const
  447. {
  448.     return frame_x;
  449. }
  450. int OggTheoraCapture::getHeight() const
  451. {
  452.     return frame_y;
  453. }
  454. int OggTheoraCapture::getFrameCount() const
  455. {
  456.     return video_frame_count;
  457. }
  458. float OggTheoraCapture::getFrameRate() const
  459. {
  460.     return float(video_hzn)/float(video_hzd);
  461. }
  462. OggTheoraCapture::~OggTheoraCapture()
  463. {
  464.     cleanup();
  465. }