Top secrets sources NedoPC pentevo

Rev

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

#include "std.h"
#include "emul.h"
#include "vars.h"
#include "util.h"

#include "savevid.h"


//#define DEBUG 1
//handles
static HANDLE hPipe=INVALID_HANDLE_VALUE;
static STARTUPINFO si;
static PROCESS_INFORMATION pi;

TSVSet SVSet;           //settings
int videosaver_state;   //0-not saving, 1-saving


//AVI global hdr + video hdrs
static char AVIRIFF[]=
"\x52\x49\x46\x46\x00\x00\x00\x00\x41\x56\x49\x20\x4C\x49\x53\x54" //RIFF size=0 (inf)
"\x3c\x01\x00\x00\x68\x64\x72\x6C\x61\x76\x69\x68\x38\x00\x00\x00"
"\x20\x4E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x01\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00"
"\x80\x02\x00\x00\xE0\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x4C\x49\x53\x54\x80\x00\x00\x00"
"\x73\x74\x72\x6C\x73\x74\x72\x68\x38\x00\x00\x00\x76\x69\x64\x73"
"\x44\x49\x42\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x01\x00\x00\x00\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x02\xE0\x01\x73\x74\x72\x66\x28\x00\x00\x00\x28\x00\x00\x00"
"\x80\x02\x00\x00\xE0\x01\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00"
"\x00\x10\x0E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x4A\x55\x4E\x4B\x04\x00\x00\x00\x00\x00\x00\x00"
;

//AVI audio hdrs
static char AVIRIFF2[]=
"\x4C\x49\x53\x54\x68\x00\x00\x00\x73\x74\x72\x6C\x73\x74\x72\x68"
"\x38\x00\x00\x00\x61\x75\x64\x73\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x10\xB1\x02\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF"
"\x04\x00\x00\x00\x6D\x00\x70\x00\x67\x00\x3B\x00\x73\x74\x72\x66"
"\x10\x00\x00\x00\x01\x00\x02\x00\x44\xAC\x00\x00\x10\xB1\x02\x00"
"\x04\x00\x10\x00\x4A\x55\x4E\x4B\x04\x00\x00\x00\x00\x00\x00\x00"
;

//AVI start streams
static char AVIRIFF3[]=
"\x4C\x49\x53\x54\x00\x00\x00\x00\x6D\x6F\x76\x69"; //LIST movi size=0

//AVI stream data headers
static char avi_frameh_vid[] = "00db    "; //DIB
static char avi_frameh_aud[] = "01wb    "; //wave


//proto
static int pipewrite(HANDLE hPipe, char *buf, unsigned len);
static void savevideo_finish();


//init:
//ffmpeg_exec  - path/name of ffmpeg executable (ex: "ffmpeg.exe")
//ffmpeg_param - params for ffmpeg output video (ex: "-c:a libmp3lame -b:a 320k")
//out_fname    - output video name (ex: "video0.avi")
//newcons      - create new console for ffmpeg (0/1)
//w,h          - width, height (ex: 460, 480)
//fps          - frames per second (ex: 50)
//sndfq        - sound sample rate (ex: 44100)
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)
{
    //create named pipe for stream video to ffmpeg
    hPipe=CreateNamedPipe(
        PIPENAME,                 // pipe name
        PIPE_ACCESS_OUTBOUND,     // write access
        PIPE_TYPE_BYTE |          // byte type pipe
        PIPE_READMODE_BYTE |      // byte-read mode
        PIPE_NOWAIT,              // non blocking mode (for async connect)
        1,                        // max. instances
        PIPESIZE,                 // output buffer size
        1024,                     // input buffer size
        0,                        // client time-out
        nullptr);                    // default security attribute

    if(hPipe==INVALID_HANDLE_VALUE)
    {
        color(CONSCLR_ERROR); printf("error: CreateNamedPipe failed.\n");
        return -1;
    }
#ifdef DEBUG
color(CONSCLR_INFO); printf("debug: named pipe '%s' created.\n",PIPENAME);
#endif

    //start ffmpeg process
    char args[VS_MAX_FFPATH+VS_MAX_FFPARM+VS_MAX_FFVOUT+100];
    _snprintf(args, sizeof(args), "\"%s\" -i %s %s -y %s", ffmpeg_exec, PIPENAME, ffmpeg_param, out_fname);
#ifdef DEBUG
color(CONSCLR_INFO); printf("debug: %s\n", args);
#endif

    memset(&si, 0, sizeof(si));
    si.cb=sizeof(si);
    //si.wShowWindow=SW_SHOW;
    si.wShowWindow=SW_MINIMIZE;
    //si.wShowWindow=SW_HIDE;
    si.dwFlags=STARTF_USESHOWWINDOW;
    memset(&pi, 0, sizeof(pi));

    if(!CreateProcess(nullptr, // no app name
        args,               // cmd line
        nullptr,               // proc attr
        nullptr,               // thread attr
        FALSE,              // Inherit Handles
        (newcons)?CREATE_NEW_CONSOLE:0, // Creation Flags
        nullptr,               // Environment
        nullptr,               // Current Directory
        &si,                // Startup Info
        &pi))               // Process Information
    {
        color(CONSCLR_ERROR); printf("error: can not start ffmpeg.\n");
        CloseHandle(hPipe);
        return -1;
    }
#ifdef DEBUG
color(CONSCLR_INFO); printf("debug: ffmpeg started.\n");
#endif

    //wait for connection from ffmpeg, 5 sec timeout
    unsigned long t=GetTickCount();
    int conn=0;
    while(t+5000ul>GetTickCount())
    {
        conn=ConnectNamedPipe(hPipe, nullptr) ? 1 : (GetLastError()==ERROR_PIPE_CONNECTED);
        if(conn) break;
        Sleep(10);
    }

    //connected?
    if (!conn)
    {
        color(CONSCLR_ERROR); printf("error: no connection from ffmpeg.\n");
        CloseHandle(hPipe);
        return -1;
    }
    DWORD dwMode=PIPE_READMODE_BYTE | PIPE_WAIT;
    SetNamedPipeHandleState(hPipe, &dwMode, nullptr,nullptr); //set blocking mode
#ifdef DEBUG
color(CONSCLR_INFO); printf("debug: got connection from pipe.\n");
#endif

    //patch and send video header
    //with negative height we can use linear bitmap data! :)
    //this also fix ffmpeg's bug-o-feature with BottomUp property when '-c:v copy' is used
    *(unsigned int*)(AVIRIFF+0x20)=u32(1000000/fps);
    *(unsigned int*)(AVIRIFF+0x40)=u32(w);
    *(unsigned int*)(AVIRIFF+0x44)=u32(-h);

    *(unsigned int*)(AVIRIFF+0x84)=unsigned(fps);
    *(unsigned short*)(AVIRIFF+0xa0)=u16(w);
    *(unsigned short*)(AVIRIFF+0xa2)=u16(-h);
    *(unsigned int*)(AVIRIFF+0xb0)=u32(w);
    *(unsigned int*)(AVIRIFF+0xb4)=u32(-h);

    *(unsigned int*)(AVIRIFF2+0x2c)=u32(sndfq*4);
    *(unsigned int*)(AVIRIFF2+0x58)=u32(sndfq);

    int res=pipewrite(hPipe,AVIRIFF,sizeof(AVIRIFF)-1);
    res+=pipewrite(hPipe,AVIRIFF2,sizeof(AVIRIFF2)-1);
    res+=pipewrite(hPipe,AVIRIFF3,sizeof(AVIRIFF3)-1);
    if(res<0)
    {
        //pipe error, finish
        color(CONSCLR_ERROR); printf("error: ffmpeg aborted connection at the beginning.\n");
        savevideo_finish();
        return -1;
    }
#ifdef DEBUG
color(CONSCLR_INFO); printf("debug: video header sent.\n");
#endif

    return 0;
}


//send video frame to stream:
//buf  - picture buffer (raw bmp RGB24 format)
//size - size of picture (W*H*3), bytes
static int savevideo_put_vframe(char *buf, unsigned size)
{
    //send frame header
    *(unsigned int*)(avi_frameh_vid+4)=size;
    int res=pipewrite(hPipe,avi_frameh_vid,8);
    if(res<0) return -1;

    //send frame data
    res=pipewrite(hPipe,buf,size);
    if(res<0) return -1;
    return 0;
}

//send audio frame to stream:
//buf  - sound buffer (raw 16 bit le stereo)
//size - length of sound data, bytes
static int savevideo_put_aframe(char *buf, unsigned int size)
{
    //send frame header
    *(unsigned int*)(avi_frameh_aud+4)=size;
    int res=pipewrite(hPipe,avi_frameh_aud,8);
    if(res<0) return -1;

    //send frame data
    res=pipewrite(hPipe,buf,size);
    if(res<0) return -1;
    return 0;
}

//finish saving: close handles
static void savevideo_finish()
{
    //send video trailer (none)
    //close pipe
    CloseHandle(hPipe);

    //wait for ffmpeg done
#ifdef DEBUG
color(CONSCLR_INFO); printf("debug: waiting for ffmpeg finish.\n");
#endif
    WaitForSingleObject(pi.hProcess, INFINITE);
#ifdef DEBUG
color(CONSCLR_INFO); printf("debug: saving video done.\n");
#endif

    //close handles
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}


//send data block to pipe
static int pipewrite(HANDLE hPipe, char *buf, unsigned len)
{
    DWORD cbWritten = 0;
    int res = 0;

    unsigned p=0;
    while(p<len) //is all data sent?
    {
        res = WriteFile(
            hPipe,      // handle to pipe
            &buf[p],    // buffer to write from
            len-p,      // number of bytes to write
            &cbWritten, // number of bytes written
            nullptr);      // not overlapped I/O
        if(res)
        {
            p+=cbWritten;
        }
        else
        {
#ifdef DEBUG
color(CONSCLR_INFO); printf("debug: pipe disconnected.\n");
#endif
            return -1;
        }
    }
    return 0;
}



/*****************************************************************************
 * Public functions                                                          *
 *****************************************************************************/


//main function of video saver
void main_savevideo()
{
    static int vidn=0;
    char video_name[VS_MAX_FFVOUT],tmp[VS_MAX_FFVOUT]={0};
    int res;

    if(videosaver_state==0) //start saver
    {
        //construct name of output file
        strncpy(tmp,conf.ffmpeg.vout,sizeof(tmp)-1);
        char *p=strchr(tmp,'#');
        if(p)
        {   //'#' found, split name into two parts, insert a number
            *p=0;
            _snprintf(video_name,sizeof(video_name),"%s%d%s",tmp,vidn,p+1);
        }
        else
            _snprintf(video_name,sizeof(video_name),"%s",tmp);

        //init ffmpeg
        res=savevideo_init(conf.ffmpeg.exec, conf.ffmpeg.parm, video_name,
            conf.ffmpeg.newcons, int(temp.ox), int(temp.oy), int(conf.intfq), int(conf.sound.fq));
        if(res) //init ok?
        {
            color(CONSCLR_ERROR); printf("error: init video saver failed.\n");
            return;
        }
        sprintf(statusline, "start saving video");


        //store screen and audio settings
        SVSet.xsz=temp.ox;
        SVSet.ysz=temp.oy;
        SVSet.fps=conf.intfq;
        SVSet.sndfq=conf.sound.fq;
        SVSet.snden=conf.sound.enabled;

        //allocate buffers for pictures
        SVSet.dx = temp.ox * temp.obpp / 8;
        SVSet.scrbuf_unaligned = (unsigned char*)malloc(SVSet.dx * temp.oy + CACHE_LINE);
        SVSet.scrbuf = (unsigned char*)align_by(SVSet.scrbuf_unaligned, CACHE_LINE);
        SVSet.dsll = ((temp.ox * 3 + 3) & ~3U);
        SVSet.ds = (u8*)malloc(SVSet.dsll * temp.oy);

        vidn++;
        videosaver_state=1;
    }
    else //saving done
    {
        //stop ffmpeg
        savevideo_finish();
        sprintf(statusline, "stop saving video");

        //free buffers
        free(SVSet.ds);
        free(SVSet.scrbuf_unaligned);

        videosaver_state=0;
    }

    statcnt = 25;  //show status during 25 frames
}


//save graphics handler
void savevideo_gfx()
{
    //is format changed?
    if(temp.ox!=SVSet.xsz || temp.oy!=SVSet.ysz ||
       conf.intfq!=SVSet.fps || conf.sound.fq!=SVSet.sndfq ||
       conf.sound.enabled!=SVSet.snden)
    {
        main_savevideo(); //stop saving!
        return;
    }

    //render screen to scrbuf buffer
    renders[conf.render].func(SVSet.scrbuf, SVSet.dx); // render to memory buffer (PAL8, YUY2, RGB15, RGB16, RGB32)
    //convert colors to RGB24
    ConvBgr24(SVSet.ds, SVSet.scrbuf, int(SVSet.dx));
    //send frame to encoder
    if(savevideo_put_vframe((char*)SVSet.ds, SVSet.dsll*SVSet.ysz))
    {
        //stop saving if error occured
        color(CONSCLR_ERROR); printf("error: ffmpeg aborted connection.\n");
        main_savevideo();
        return;
    }
}

//save sound handler
void savevideo_snd()
{
    //is format changed?
    if(temp.ox!=SVSet.xsz || temp.oy!=SVSet.ysz ||
       conf.intfq!=SVSet.fps || conf.sound.fq!=SVSet.sndfq ||
       conf.sound.enabled!=SVSet.snden)
    {
        main_savevideo(); //stop saving!
        return;
    }

    //send frame to encoder
    if(savevideo_put_aframe((char*)sndplaybuf,spbsize))
    {
        //stop saving if error occured
        color(CONSCLR_ERROR); printf("error: ffmpeg aborted connection.\n");
        main_savevideo();
        return;
    }
}