Subversion Repositories pentevo

Rev

Rev 883 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed | ?url?

  1. #include "std.h"
  2. #include "emul.h"
  3. #include "vars.h"
  4. #include "util.h"
  5.  
  6. #include "savevid.h"
  7.  
  8.  
  9. //#define DEBUG 1
  10. //handles
  11. static HANDLE hPipe=INVALID_HANDLE_VALUE;
  12. static STARTUPINFO si;
  13. static PROCESS_INFORMATION pi;
  14.  
  15. TSVSet SVSet;           //settings
  16. int videosaver_state;   //0-not saving, 1-saving
  17.  
  18.  
  19. //AVI global hdr + video hdrs
  20. static char AVIRIFF[]=
  21. "\x52\x49\x46\x46\x00\x00\x00\x00\x41\x56\x49\x20\x4C\x49\x53\x54" //RIFF size=0 (inf)
  22. "\x3c\x01\x00\x00\x68\x64\x72\x6C\x61\x76\x69\x68\x38\x00\x00\x00"
  23. "\x20\x4E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x01\x00\x00"
  24. "\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00"
  25. "\x80\x02\x00\x00\xE0\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  26. "\x00\x00\x00\x00\x00\x00\x00\x00\x4C\x49\x53\x54\x80\x00\x00\x00"
  27. "\x73\x74\x72\x6C\x73\x74\x72\x68\x38\x00\x00\x00\x76\x69\x64\x73"
  28. "\x44\x49\x42\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  29. "\x01\x00\x00\x00\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  30. "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00"
  31. "\x80\x02\xE0\x01\x73\x74\x72\x66\x28\x00\x00\x00\x28\x00\x00\x00"
  32. "\x80\x02\x00\x00\xE0\x01\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00"
  33. "\x00\x10\x0E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  34. "\x00\x00\x00\x00\x4A\x55\x4E\x4B\x04\x00\x00\x00\x00\x00\x00\x00"
  35. ;
  36.  
  37. //AVI audio hdrs
  38. static char AVIRIFF2[]=
  39. "\x4C\x49\x53\x54\x68\x00\x00\x00\x73\x74\x72\x6C\x73\x74\x72\x68"
  40. "\x38\x00\x00\x00\x61\x75\x64\x73\x00\x00\x00\x00\x00\x00\x00\x00"
  41. "\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x10\xB1\x02\x00"
  42. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF"
  43. "\x04\x00\x00\x00\x6D\x00\x70\x00\x67\x00\x3B\x00\x73\x74\x72\x66"
  44. "\x10\x00\x00\x00\x01\x00\x02\x00\x44\xAC\x00\x00\x10\xB1\x02\x00"
  45. "\x04\x00\x10\x00\x4A\x55\x4E\x4B\x04\x00\x00\x00\x00\x00\x00\x00"
  46. ;
  47.  
  48. //AVI start streams
  49. static char AVIRIFF3[]=
  50. "\x4C\x49\x53\x54\x00\x00\x00\x00\x6D\x6F\x76\x69"; //LIST movi size=0
  51.  
  52. //AVI stream data headers
  53. static char avi_frameh_vid[] = "00db    "; //DIB
  54. static char avi_frameh_aud[] = "01wb    "; //wave
  55.  
  56.  
  57. //proto
  58. static int pipewrite(HANDLE hPipe, char *buf, unsigned len);
  59. static void savevideo_finish();
  60.  
  61.  
  62. //init:
  63. //ffmpeg_exec  - path/name of ffmpeg executable (ex: "ffmpeg.exe")
  64. //ffmpeg_param - params for ffmpeg output video (ex: "-c:a libmp3lame -b:a 320k")
  65. //out_fname    - output video name (ex: "video0.avi")
  66. //newcons      - create new console for ffmpeg (0/1)
  67. //w,h          - width, height (ex: 460, 480)
  68. //fps          - frames per second (ex: 50)
  69. //sndfq        - sound sample rate (ex: 44100)
  70. static int savevideo_init(const char *ffmpeg_exec, const char *ffmpeg_param, const char *out_fname, char newcons, int w, int h, int fps, int sndfq)
  71. {
  72.     //create named pipe for stream video to ffmpeg
  73.     hPipe=CreateNamedPipe(
  74.         PIPENAME,                 // pipe name
  75.         PIPE_ACCESS_OUTBOUND,     // write access
  76.         PIPE_TYPE_BYTE |          // byte type pipe
  77.         PIPE_READMODE_BYTE |      // byte-read mode
  78.         PIPE_NOWAIT,              // non blocking mode (for async connect)
  79.         1,                        // max. instances
  80.         PIPESIZE,                 // output buffer size
  81.         1024,                     // input buffer size
  82.         0,                        // client time-out
  83.         nullptr);                    // default security attribute
  84.  
  85.     if(hPipe==INVALID_HANDLE_VALUE)
  86.     {
  87.         color(CONSCLR_ERROR); printf("error: CreateNamedPipe failed.\n");
  88.         return -1;
  89.     }
  90. #ifdef DEBUG
  91. color(CONSCLR_INFO); printf("debug: named pipe '%s' created.\n",PIPENAME);
  92. #endif
  93.  
  94.     //start ffmpeg process
  95.     char args[VS_MAX_FFPATH+VS_MAX_FFPARM+VS_MAX_FFVOUT+100];
  96.     _snprintf(args, sizeof(args), "\"%s\" -i %s %s -y %s", ffmpeg_exec, PIPENAME, ffmpeg_param, out_fname);
  97. #ifdef DEBUG
  98. color(CONSCLR_INFO); printf("debug: %s\n", args);
  99. #endif
  100.  
  101.     memset(&si, 0, sizeof(si));
  102.     si.cb=sizeof(si);
  103.     //si.wShowWindow=SW_SHOW;
  104.     si.wShowWindow=SW_MINIMIZE;
  105.     //si.wShowWindow=SW_HIDE;
  106.     si.dwFlags=STARTF_USESHOWWINDOW;
  107.     memset(&pi, 0, sizeof(pi));
  108.  
  109.     if(!CreateProcess(nullptr, // no app name
  110.         args,               // cmd line
  111.         nullptr,               // proc attr
  112.         nullptr,               // thread attr
  113.         FALSE,              // Inherit Handles
  114.         (newcons)?CREATE_NEW_CONSOLE:0, // Creation Flags
  115.         nullptr,               // Environment
  116.         nullptr,               // Current Directory
  117.         &si,                // Startup Info
  118.         &pi))               // Process Information
  119.     {
  120.         color(CONSCLR_ERROR); printf("error: can not start ffmpeg.\n");
  121.         CloseHandle(hPipe);
  122.         return -1;
  123.     }
  124. #ifdef DEBUG
  125. color(CONSCLR_INFO); printf("debug: ffmpeg started.\n");
  126. #endif
  127.  
  128.     //wait for connection from ffmpeg, 5 sec timeout
  129.     unsigned long t=GetTickCount();
  130.     int conn=0;
  131.     while(t+5000ul>GetTickCount())
  132.     {
  133.         conn=ConnectNamedPipe(hPipe, nullptr) ? 1 : (GetLastError()==ERROR_PIPE_CONNECTED);
  134.         if(conn) break;
  135.         Sleep(10);
  136.     }
  137.  
  138.     //connected?
  139.     if (!conn)
  140.     {
  141.         color(CONSCLR_ERROR); printf("error: no connection from ffmpeg.\n");
  142.         CloseHandle(hPipe);
  143.         return -1;
  144.     }
  145.     DWORD dwMode=PIPE_READMODE_BYTE | PIPE_WAIT;
  146.     SetNamedPipeHandleState(hPipe, &dwMode, nullptr,nullptr); //set blocking mode
  147. #ifdef DEBUG
  148. color(CONSCLR_INFO); printf("debug: got connection from pipe.\n");
  149. #endif
  150.  
  151.     //patch and send video header
  152.     //with negative height we can use linear bitmap data! :)
  153.     //this also fix ffmpeg's bug-o-feature with BottomUp property when '-c:v copy' is used
  154.     *(unsigned int*)(AVIRIFF+0x20)=u32(1000000/fps);
  155.     *(unsigned int*)(AVIRIFF+0x40)=u32(w);
  156.     *(unsigned int*)(AVIRIFF+0x44)=u32(-h);
  157.  
  158.     *(unsigned int*)(AVIRIFF+0x84)=unsigned(fps);
  159.     *(unsigned short*)(AVIRIFF+0xa0)=u16(w);
  160.     *(unsigned short*)(AVIRIFF+0xa2)=u16(-h);
  161.     *(unsigned int*)(AVIRIFF+0xb0)=u32(w);
  162.     *(unsigned int*)(AVIRIFF+0xb4)=u32(-h);
  163.  
  164.     *(unsigned int*)(AVIRIFF2+0x2c)=u32(sndfq*4);
  165.     *(unsigned int*)(AVIRIFF2+0x58)=u32(sndfq);
  166.  
  167.     int res=pipewrite(hPipe,AVIRIFF,sizeof(AVIRIFF)-1);
  168.     res+=pipewrite(hPipe,AVIRIFF2,sizeof(AVIRIFF2)-1);
  169.     res+=pipewrite(hPipe,AVIRIFF3,sizeof(AVIRIFF3)-1);
  170.     if(res<0)
  171.     {
  172.         //pipe error, finish
  173.         color(CONSCLR_ERROR); printf("error: ffmpeg aborted connection at the beginning.\n");
  174.         savevideo_finish();
  175.         return -1;
  176.     }
  177. #ifdef DEBUG
  178. color(CONSCLR_INFO); printf("debug: video header sent.\n");
  179. #endif
  180.  
  181.     return 0;
  182. }
  183.  
  184.  
  185. //send video frame to stream:
  186. //buf  - picture buffer (raw bmp RGB24 format)
  187. //size - size of picture (W*H*3), bytes
  188. static int savevideo_put_vframe(char *buf, unsigned size)
  189. {
  190.     //send frame header
  191.     *(unsigned int*)(avi_frameh_vid+4)=size;
  192.     int res=pipewrite(hPipe,avi_frameh_vid,8);
  193.     if(res<0) return -1;
  194.  
  195.     //send frame data
  196.     res=pipewrite(hPipe,buf,size);
  197.     if(res<0) return -1;
  198.     return 0;
  199. }
  200.  
  201. //send audio frame to stream:
  202. //buf  - sound buffer (raw 16 bit le stereo)
  203. //size - length of sound data, bytes
  204. static int savevideo_put_aframe(char *buf, unsigned int size)
  205. {
  206.     //send frame header
  207.     *(unsigned int*)(avi_frameh_aud+4)=size;
  208.     int res=pipewrite(hPipe,avi_frameh_aud,8);
  209.     if(res<0) return -1;
  210.  
  211.     //send frame data
  212.     res=pipewrite(hPipe,buf,size);
  213.     if(res<0) return -1;
  214.     return 0;
  215. }
  216.  
  217. //finish saving: close handles
  218. static void savevideo_finish()
  219. {
  220.     //send video trailer (none)
  221.     //close pipe
  222.     CloseHandle(hPipe);
  223.  
  224.     //wait for ffmpeg done
  225. #ifdef DEBUG
  226. color(CONSCLR_INFO); printf("debug: waiting for ffmpeg finish.\n");
  227. #endif
  228.     WaitForSingleObject(pi.hProcess, INFINITE);
  229. #ifdef DEBUG
  230. color(CONSCLR_INFO); printf("debug: saving video done.\n");
  231. #endif
  232.  
  233.     //close handles
  234.     CloseHandle(pi.hProcess);
  235.     CloseHandle(pi.hThread);
  236. }
  237.  
  238.  
  239. //send data block to pipe
  240. static int pipewrite(HANDLE hPipe, char *buf, unsigned len)
  241. {
  242.     DWORD cbWritten = 0;
  243.     int res = 0;
  244.  
  245.     unsigned p=0;
  246.     while(p<len) //is all data sent?
  247.     {
  248.         res = WriteFile(
  249.             hPipe,      // handle to pipe
  250.             &buf[p],    // buffer to write from
  251.             len-p,      // number of bytes to write
  252.             &cbWritten, // number of bytes written
  253.             nullptr);      // not overlapped I/O
  254.         if(res)
  255.         {
  256.             p+=cbWritten;
  257.         }
  258.         else
  259.         {
  260. #ifdef DEBUG
  261. color(CONSCLR_INFO); printf("debug: pipe disconnected.\n");
  262. #endif
  263.             return -1;
  264.         }
  265.     }
  266.     return 0;
  267. }
  268.  
  269.  
  270.  
  271. /*****************************************************************************
  272.  * Public functions                                                          *
  273.  *****************************************************************************/
  274.  
  275. //main function of video saver
  276. void main_savevideo()
  277. {
  278.     static int vidn=0;
  279.     char video_name[VS_MAX_FFVOUT],tmp[VS_MAX_FFVOUT]={0};
  280.     int res;
  281.  
  282.     if(videosaver_state==0) //start saver
  283.     {
  284.         //construct name of output file
  285.         strncpy(tmp,conf.ffmpeg.vout,sizeof(tmp)-1);
  286.         char *p=strchr(tmp,'#');
  287.         if(p)
  288.         {   //'#' found, split name into two parts, insert a number
  289.             *p=0;
  290.             _snprintf(video_name,sizeof(video_name),"%s%d%s",tmp,vidn,p+1);
  291.         }
  292.         else
  293.             _snprintf(video_name,sizeof(video_name),"%s",tmp);
  294.  
  295.         //init ffmpeg
  296.         res=savevideo_init(conf.ffmpeg.exec, conf.ffmpeg.parm, video_name,
  297.             conf.ffmpeg.newcons, int(temp.ox), int(temp.oy), int(conf.intfq), int(conf.sound.fq));
  298.         if(res) //init ok?
  299.         {
  300.             color(CONSCLR_ERROR); printf("error: init video saver failed.\n");
  301.             return;
  302.         }
  303.         sprintf(statusline, "start saving video");
  304.  
  305.  
  306.         //store screen and audio settings
  307.         SVSet.xsz=temp.ox;
  308.         SVSet.ysz=temp.oy;
  309.         SVSet.fps=conf.intfq;
  310.         SVSet.sndfq=conf.sound.fq;
  311.         SVSet.snden=conf.sound.enabled;
  312.  
  313.         //allocate buffers for pictures
  314.         SVSet.dx = temp.ox * temp.obpp / 8;
  315.         SVSet.scrbuf_unaligned = (unsigned char*)malloc(SVSet.dx * temp.oy + CACHE_LINE);
  316.         SVSet.scrbuf = (unsigned char*)align_by(SVSet.scrbuf_unaligned, CACHE_LINE);
  317.         SVSet.dsll = ((temp.ox * 3 + 3) & ~3U);
  318.         SVSet.ds = (u8*)malloc(SVSet.dsll * temp.oy);
  319.  
  320.         vidn++;
  321.         videosaver_state=1;
  322.     }
  323.     else //saving done
  324.     {
  325.         //stop ffmpeg
  326.         savevideo_finish();
  327.         sprintf(statusline, "stop saving video");
  328.  
  329.         //free buffers
  330.         free(SVSet.ds);
  331.         free(SVSet.scrbuf_unaligned);
  332.  
  333.         videosaver_state=0;
  334.     }
  335.  
  336.     statcnt = 25;  //show status during 25 frames
  337. }
  338.  
  339.  
  340. //save graphics handler
  341. void savevideo_gfx()
  342. {
  343.     //is format changed?
  344.     if(temp.ox!=SVSet.xsz || temp.oy!=SVSet.ysz ||
  345.        conf.intfq!=SVSet.fps || conf.sound.fq!=SVSet.sndfq ||
  346.        conf.sound.enabled!=SVSet.snden)
  347.     {
  348.         main_savevideo(); //stop saving!
  349.         return;
  350.     }
  351.  
  352.     //render screen to scrbuf buffer
  353.     renders[conf.render].func(SVSet.scrbuf, SVSet.dx); // render to memory buffer (PAL8, YUY2, RGB15, RGB16, RGB32)
  354.     //convert colors to RGB24
  355.     ConvBgr24(SVSet.ds, SVSet.scrbuf, int(SVSet.dx));
  356.     //send frame to encoder
  357.     if(savevideo_put_vframe((char*)SVSet.ds, SVSet.dsll*SVSet.ysz))
  358.     {
  359.         //stop saving if error occured
  360.         color(CONSCLR_ERROR); printf("error: ffmpeg aborted connection.\n");
  361.         main_savevideo();
  362.         return;
  363.     }
  364. }
  365.  
  366. //save sound handler
  367. void savevideo_snd()
  368. {
  369.     //is format changed?
  370.     if(temp.ox!=SVSet.xsz || temp.oy!=SVSet.ysz ||
  371.        conf.intfq!=SVSet.fps || conf.sound.fq!=SVSet.sndfq ||
  372.        conf.sound.enabled!=SVSet.snden)
  373.     {
  374.         main_savevideo(); //stop saving!
  375.         return;
  376.     }
  377.  
  378.     //send frame to encoder
  379.     if(savevideo_put_aframe((char*)sndplaybuf,spbsize))
  380.     {
  381.         //stop saving if error occured
  382.         color(CONSCLR_ERROR); printf("error: ffmpeg aborted connection.\n");
  383.         main_savevideo();
  384.         return;
  385.     }
  386. }
  387.