Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/post/src/plugins/savempg.c
3203 views
1
//=============================================================================
2
// savempg.c
3
//
4
// Module for compressing and saving ElmerPost-pictures in MPEG formats.
5
//
6
// Compile e.g. as follows:
7
//
8
// Linux:
9
//
10
// gcc -shared -O -I/usr/include/ffmpeg -I/usr/include/tcl8.4
11
// savempg.c -o savempg.o -lavcodec -lavutil -lswscale
12
//
13
// MinGW:
14
// gcc -shared -O -I$FFMPEG/include -L$FFMPEG/lib -o savempg.dll
15
// savempg.c -lopengl32 -ltcl84 -lavcodec -lavutil -lswscale -lz
16
//
17
// ( Note that the libraries required depend on the libavcodec build. )
18
//
19
// Copy the shared library into $ELMER_POST_HOME/modules and run ElmerPost
20
//
21
// Usage in Elmer-Post:
22
//
23
// savempg codec name [ default: mpg1 ( mpg2, mpg2, yuv4 ) ]
24
// savempg bitrate value [ default: 1000000 bps ]
25
// savempg gopsize value [ default: 20 frames ]
26
// savempg bframes value [ default: 2 frames ]
27
// savempg start file.es [ initializes video compressor ]
28
// savempg append [ adds current frame to video sequence ]
29
// savempg stop [ finalizes video compressor ]
30
//
31
// Parsed together from screensave.c && the ffmpeg samples
32
//
33
// Written by: ML, 30. Sept. 2007
34
//=============================================================================
35
#ifdef HAVE_AV_CONFIG_H
36
#undef HAVE_AV_CONFIG_H
37
#endif
38
39
#if defined(WIN32) || defined(win32)
40
#include <windows.h>
41
#endif
42
43
#include <stdlib.h>
44
#include <stdio.h>
45
#include <string.h>
46
#include <inttypes.h>
47
#include <math.h>
48
#include <GL/gl.h>
49
#include <tcl.h>
50
#include "avcodec.h"
51
#include "swscale.h"
52
53
#define INBUF_SIZE 4096
54
#define STATE_READY 0
55
#define STATE_STARTED 1
56
#define DEFAULT_B_FRAMES 2
57
#define DEFAULT_GOP_SIZE 20
58
#define DEFAULT_BITRATE 1000000
59
#define DEFAULT_MPG_BUFSIZE 500000
60
61
#undef MPEG4
62
63
#if !defined(INFINITY)
64
#define INFINITY 999999999
65
#endif
66
67
typedef struct buffer_s {
68
uint8_t *MPG;
69
uint8_t *YUV;
70
uint8_t *RGB;
71
uint8_t *ROW;
72
} buffer_t;
73
74
void free_buffers( buffer_t *buff ) {
75
free( buff->MPG );
76
free( buff->YUV );
77
free( buff->RGB );
78
free( buff->ROW );
79
}
80
81
void SetMessage( Tcl_Interp *interp, char *message ) {
82
sprintf( interp->result, "savempg: %s\n", message );
83
}
84
85
static double psnr( double d ) {
86
if( d == 0 )
87
return INFINITY;
88
return -10.0 * log( d ) / log( 10.0 );
89
}
90
91
void print_info( int count_frames, AVCodecContext *context, int bytes ) {
92
double tmp = context->width * context->height * 255.0 * 255.0;
93
double Ypsnr = psnr( context->coded_frame->error[0] / tmp );
94
double quality = context->coded_frame->quality/(double)FF_QP2LAMBDA;
95
char pict_type = av_get_pict_type_char(context->coded_frame->pict_type);
96
fprintf( stdout, "savempg: frame %4d: type=%c, ", count_frames, pict_type );
97
fprintf( stdout, "size=%6d bytes, PSNR(Y)=%5.2f dB, ", bytes, Ypsnr );
98
fprintf( stdout, "q=%2.1f\n", (float)quality );
99
fflush( stdout );
100
}
101
102
static int SaveMPG( ClientData cl,Tcl_Interp *interp,int argc,char **argv ) {
103
static AVCodec *codec = NULL;
104
static AVCodecContext *context = NULL;
105
static AVFrame *YUVpicture = NULL;
106
static AVFrame *RGBpicture = NULL;
107
static int bytes, PIXsize, stride;
108
static int y, nx, ny, ox, oy, viewp[4];
109
static int i_state = STATE_READY;
110
static int initialized = 0;
111
static int count_frames = 0;
112
static int bitrate = DEFAULT_BITRATE;
113
static int gopsize = DEFAULT_GOP_SIZE;
114
static int bframes = DEFAULT_B_FRAMES;
115
static int MPGbufsize = DEFAULT_MPG_BUFSIZE;
116
static int codec_id = CODEC_ID_MPEG1VIDEO;
117
static FILE *MPGfile;
118
static char *state, fname[256];
119
static buffer_t buff;
120
static struct SwsContext *img_convert_ctx;
121
122
if( argc < 2 ) {
123
SetMessage( interp, "too few arguments" );
124
return TCL_ERROR;
125
}
126
127
state = argv[1];
128
//===========================================================================
129
// SELECT CODEC
130
//===========================================================================
131
if( !strncmp( state, "codec", 5 ) ) {
132
133
// Can't change while running:
134
//----------------------------
135
if( i_state != STATE_READY ) {
136
SetMessage( interp, "can't change codec when running" );
137
return TCL_ERROR;
138
}
139
140
// Default is MPEG1:
141
//------------------
142
codec_id = CODEC_ID_MPEG1VIDEO;
143
144
if( argc >= 3 ) {
145
char *c = argv[2];
146
147
if( !strncmp( c, "mpg1", 4 ) ) {
148
codec_id = CODEC_ID_MPEG1VIDEO;
149
fprintf( stdout, "savempg: codec: mpg1\n" );
150
}
151
152
if( !strncmp( c, "mpg2", 4 ) ) {
153
codec_id = CODEC_ID_MPEG2VIDEO;
154
fprintf( stdout, "savempg: codec: mpg2\n" );
155
}
156
157
if( !strncmp( c, "mpg4", 4 ) ) {
158
codec_id = CODEC_ID_MPEG4;
159
fprintf( stdout, "savempg: codec: mpg4\n" );
160
}
161
162
if( !strncmp( c, "yuv4", 4 ) ) {
163
codec_id = CODEC_ID_RAWVIDEO;
164
fprintf( stdout, "savempg: codec: yuv4\n" );
165
}
166
}
167
168
fflush( stdout );
169
170
return TCL_OK;
171
}
172
173
//===========================================================================
174
// SET BITRATE
175
//===========================================================================
176
if( !strncmp( state, "bitrate", 7 ) ) {
177
178
// Can't change while running:
179
//----------------------------
180
if( i_state != STATE_READY ) {
181
SetMessage( interp, "can't change bitrate when running" );
182
return TCL_ERROR;
183
}
184
185
bitrate = DEFAULT_BITRATE;
186
187
if( argc >= 3 )
188
bitrate = atoi( argv[2] );
189
190
if( bitrate < 100000 )
191
bitrate = 100000;
192
193
fprintf( stdout, "savempg: bitrate: %d bps\n", bitrate );
194
fflush( stdout );
195
196
return TCL_OK;
197
}
198
199
//===========================================================================
200
// SET GOPSIZE
201
//===========================================================================
202
if( !strncmp( state, "gopsize", 7 ) ) {
203
204
// Can't change while running:
205
//----------------------------
206
if( i_state != STATE_READY ) {
207
SetMessage( interp, "can't change gopsize when running" );
208
return TCL_ERROR;
209
}
210
211
gopsize = DEFAULT_GOP_SIZE;
212
213
if( argc >= 3 )
214
gopsize = atoi( argv[2] );
215
216
if( gopsize < 1 )
217
gopsize = 1;
218
219
fprintf( stdout, "savempg: gop: %d frames\n", gopsize );
220
fflush( stdout );
221
222
return TCL_OK;
223
}
224
225
//===========================================================================
226
// SET B-FRAMES
227
//===========================================================================
228
if( !strncmp( state, "bframes", 7 ) ) {
229
230
// Can't change while running:
231
//----------------------------
232
if( i_state != STATE_READY ) {
233
SetMessage( interp, "can't change bframes when running" );
234
return TCL_ERROR;
235
}
236
237
bframes = DEFAULT_B_FRAMES;
238
239
if( argc >= 3 )
240
bframes = atoi( argv[2] );
241
242
if( bframes < 0 )
243
bframes = 0;
244
245
fprintf( stdout, "savempg: bframes: %d\n", bframes );
246
fflush( stdout );
247
248
return TCL_OK;
249
}
250
251
//===========================================================================
252
// START COMPRESSION
253
//===========================================================================
254
else if( !strncmp( state, "start", 5 ) ) {
255
256
// Can't start if already running:
257
//----------------------------------
258
if( i_state != STATE_READY ) {
259
SetMessage( interp, "already started" );
260
return TCL_ERROR;
261
}
262
263
// Determine output file name:
264
//---------------------------
265
if( argc < 3 ) {
266
strcpy( fname, "elmerpost.es" );
267
} else {
268
strncpy( fname, argv[2], 256 );
269
}
270
271
// Open output file:
272
//------------------
273
if( (MPGfile = fopen(fname, "wb")) == NULL ) {
274
SetMessage( interp, "can't open file" );
275
return TCL_ERROR;
276
}
277
278
fprintf( stdout, "savempg: file: %s\n", fname );
279
fprintf( stdout, "savempg: libavcodec: %s\n",
280
AV_STRINGIFY(LIBAVCODEC_VERSION) );
281
fprintf( stdout, "savempg: libavutil: %s\n",
282
AV_STRINGIFY(LIBAVUTIL_VERSION) );
283
fprintf( stdout, "savempg: libswscale: %s\n",
284
AV_STRINGIFY(LIBSWSCALE_VERSION) );
285
fflush( stdout );
286
287
count_frames = 0;
288
289
// Determine view port size etc.:
290
//-------------------------------
291
glGetIntegerv( GL_VIEWPORT, viewp );
292
ox = viewp[0];
293
oy = viewp[1];
294
nx = viewp[2] + 1;
295
ny = viewp[3] + 1;
296
PIXsize = nx * ny;
297
stride = 3 * nx;
298
299
// Must be even:
300
//--------------
301
if( nx % 2 || ny % 2 ) {
302
fprintf( stdout, "savempg: state: stopped\n" );
303
fflush( stdout );
304
fclose( MPGfile );
305
SetMessage( interp, "view port must be even" );
306
return TCL_ERROR;
307
}
308
309
// Allocate memory:
310
//-----------------
311
if( codec_id == CODEC_ID_RAWVIDEO )
312
MPGbufsize = 3 * (PIXsize / 2);
313
314
if ( !(buff.RGB = (uint8_t*)malloc(stride*ny)) ||
315
!(buff.ROW = (uint8_t*)malloc(stride)) ||
316
!(buff.YUV = (uint8_t*)malloc(3 * (PIXsize / 2))) ||
317
!(buff.MPG = (uint8_t*)malloc(MPGbufsize)) ) {
318
fclose( MPGfile );
319
SetMessage( interp, "can't allocate memory" );
320
return TCL_ERROR;
321
}
322
323
// Initialize libavcodec:
324
//-----------------------
325
if( !initialized ) {
326
avcodec_init();
327
avcodec_register_all();
328
initialized = 1;
329
}
330
331
// Choose codec:
332
//--------------
333
codec = avcodec_find_encoder( codec_id );
334
335
if( !codec ) {
336
free_buffers( &buff );
337
fclose( MPGfile );
338
SetMessage( interp, "can't find codec" );
339
return TCL_ERROR;
340
}
341
342
// Init codec context etc.:
343
//--------------------------
344
context = avcodec_alloc_context();
345
346
context->bit_rate = bitrate;
347
context->width = nx;
348
context->height = ny;
349
context->time_base = (AVRational){ 1, 25 };
350
context->gop_size = gopsize;
351
context->max_b_frames = bframes;
352
context->pix_fmt = PIX_FMT_YUV420P;
353
context->flags |= CODEC_FLAG_PSNR;
354
355
if( avcodec_open( context, codec ) < 0 ) {
356
avcodec_close( context );
357
av_free( context );
358
free_buffers( &buff );
359
fclose( MPGfile );
360
SetMessage( interp, "can't open codec" );
361
return TCL_ERROR;
362
}
363
364
YUVpicture = avcodec_alloc_frame();
365
366
YUVpicture->data[0] = buff.YUV;
367
YUVpicture->data[1] = buff.YUV + PIXsize;
368
YUVpicture->data[2] = buff.YUV + PIXsize + PIXsize / 4;
369
YUVpicture->linesize[0] = nx;
370
YUVpicture->linesize[1] = nx / 2;
371
YUVpicture->linesize[2] = nx / 2;
372
373
RGBpicture = avcodec_alloc_frame();
374
375
RGBpicture->data[0] = buff.RGB;
376
RGBpicture->data[1] = buff.RGB;
377
RGBpicture->data[2] = buff.RGB;
378
RGBpicture->linesize[0] = stride;
379
RGBpicture->linesize[1] = stride;
380
RGBpicture->linesize[2] = stride;
381
382
// Write .ym4 header for raw video:
383
//----------------------------------
384
if( codec_id == CODEC_ID_RAWVIDEO ) {
385
fprintf( MPGfile, "YUV4MPEG2 W%d H%d F25:1 Ip A1:1", nx, ny );
386
fprintf( MPGfile, "%c", (char)0x0a );
387
}
388
389
// Set state "started":
390
//----------------------
391
i_state = STATE_STARTED;
392
fprintf( stdout, "savempg: state: started\n" );
393
fflush( stdout );
394
395
return TCL_OK;
396
}
397
398
//===========================================================================
399
// COMPRESS FRAME
400
//===========================================================================
401
else if( !strncmp( state, "append", 6) ) {
402
403
// Can't compress if status != started:
404
//-------------------------------------
405
if( i_state != STATE_STARTED ) {
406
SetMessage( interp, "not started" );
407
return TCL_ERROR;
408
}
409
410
// Read RGB data:
411
//---------------
412
glReadBuffer( GL_FRONT );
413
glReadPixels( ox, oy, nx, ny, GL_RGB, GL_UNSIGNED_BYTE, buff.RGB );
414
415
// The picture is upside down - flip it:
416
//---------------------------------------
417
for( y = 0; y < ny/2; y++ ) {
418
uint8_t *r1 = buff.RGB + stride * y;
419
uint8_t *r2 = buff.RGB + stride * (ny - 1 - y);
420
memcpy( buff.ROW, r1, stride );
421
memcpy( r1, r2, stride );
422
memcpy( r2, buff.ROW, stride );
423
}
424
425
// Convert to YUV:
426
//----------------
427
if( img_convert_ctx == NULL )
428
img_convert_ctx = sws_getContext( nx, ny, PIX_FMT_RGB24,
429
nx, ny, PIX_FMT_YUV420P,
430
SWS_BICUBIC, NULL, NULL, NULL );
431
432
if( img_convert_ctx == NULL ) {
433
SetMessage( interp, "can't initialize scaler context" );
434
return TCL_ERROR;
435
}
436
437
sws_scale( img_convert_ctx, RGBpicture->data, RGBpicture->linesize,
438
0, ny, YUVpicture->data, YUVpicture->linesize );
439
440
// Encode frame:
441
//--------------
442
bytes = avcodec_encode_video( context, buff.MPG,
443
MPGbufsize, YUVpicture );
444
445
count_frames++;
446
print_info( count_frames, context, bytes );
447
fflush( stdout );
448
449
if( codec_id == CODEC_ID_RAWVIDEO ) {
450
fprintf( MPGfile, "FRAME" );
451
fprintf( MPGfile, "%c", (char)0x0a );
452
}
453
454
fwrite( buff.MPG, 1, bytes, MPGfile );
455
456
return TCL_OK;
457
}
458
459
//===========================================================================
460
// STOP COMPRESSION
461
//===========================================================================
462
else if( !strncmp( state, "stop", 4) ) {
463
464
// Can't stop if status != started:
465
//---------------------------------
466
if( i_state != STATE_STARTED ) {
467
SetMessage( interp, "not started" );
468
return TCL_ERROR;
469
}
470
471
// Get the delayed frames, if any:
472
//--------------------------------
473
for( ; bytes; ) {
474
bytes = avcodec_encode_video( context, buff.MPG, MPGbufsize, NULL );
475
476
count_frames++;
477
print_info( count_frames, context, bytes );
478
fflush( stdout );
479
480
fwrite( buff.MPG, 1, bytes, MPGfile );
481
}
482
483
// Add sequence end code:
484
//-----------------------
485
if( codec_id == CODEC_ID_MPEG1VIDEO ) {
486
buff.MPG[0] = 0x00;
487
buff.MPG[1] = 0x00;
488
buff.MPG[2] = 0x01;
489
buff.MPG[3] = 0xb7;
490
fwrite( buff.MPG, 1, 4, MPGfile );
491
}
492
493
// Finalize:
494
//-----------
495
avcodec_close( context );
496
av_free( context );
497
av_free( YUVpicture );
498
av_free( RGBpicture );
499
free_buffers( &buff );
500
fclose( MPGfile );
501
502
i_state = STATE_READY;
503
fprintf( stdout, "savempg: state: stopped\n" );
504
fflush( stdout );
505
506
return TCL_OK;
507
}
508
509
//===========================================================================
510
// UNKNOWN STATE
511
//===========================================================================
512
else {
513
SetMessage( interp, "unknown request" );
514
return TCL_ERROR;
515
}
516
517
// We should never ever arrive at this point:
518
//-------------------------------------------
519
SetMessage( interp, "unknown error" );
520
return TCL_ERROR;
521
}
522
523
#if defined(WIN32) || defined(win32)
524
__declspec(dllexport)
525
#endif
526
527
int Savempg_Init( Tcl_Interp *interp ) {
528
Tcl_CreateCommand( interp, "savempg", (Tcl_CmdProc *)SaveMPG,
529
(ClientData)NULL, (Tcl_CmdDeleteProc *)NULL );
530
return TCL_OK;
531
}
532
533