Subversion Repositories pentevo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
716 lvd 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
HANDLE hPipe=INVALID_HANDLE_VALUE;
12
STARTUPINFO si;
13
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
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
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
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
char avi_frameh_vid[] = "00db    "; //DIB
54
char avi_frameh_aud[] = "01wb    "; //wave
55
 
56
 
57
//proto
58
static int pipewrite(HANDLE hPipe, char *buf, int 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
        NULL);                    // 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(NULL, // no app name
110
        args,               // cmd line
111
        NULL,               // proc attr
112
        NULL,               // thread attr
113
        FALSE,              // Inherit Handles
114
        (newcons)?CREATE_NEW_CONSOLE:0, // Creation Flags
115
        NULL,               // Environment
116
        NULL,               // 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, NULL) ? 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, NULL,NULL); //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)=1000000/fps;
155
    *(unsigned int*)(AVIRIFF+0x40)=w;
156
    *(unsigned int*)(AVIRIFF+0x44)=-h;
157
 
158
    *(unsigned int*)(AVIRIFF+0x84)=fps;
159
    *(unsigned short*)(AVIRIFF+0xa0)=w;
160
    *(unsigned short*)(AVIRIFF+0xa2)=-h;
161
    *(unsigned int*)(AVIRIFF+0xb0)=w;
162
    *(unsigned int*)(AVIRIFF+0xb4)=-h;
163
 
164
    *(unsigned int*)(AVIRIFF2+0x2c)=sndfq*4;
165
    *(unsigned int*)(AVIRIFF2+0x58)=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, int len)
241
{
242
    DWORD cbWritten = 0;
243
    int res = 0;
244
 
245
    int 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
            NULL);      // 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%u%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, temp.ox, temp.oy, conf.intfq, 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) & ~3);
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, 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
}