4
* Copyright (C) Charles 'Buck' Krasic - April 2000
5
* Copyright (C) Erik Walthinsen - April 2000
7
* This file is part of libdv, a free DV (IEC 61834/SMPTE 314M)
10
* libdv is free software; you can redistribute it and/or modify it
11
* under the terms of the GNU Lesser Public License as published by
12
* the Free Software Foundation; either version 2.1, or (at your
13
* option) any later version.
15
* libdv is distributed in the hope that it will be useful, but
16
* WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* Lesser Public License for more details.
20
* You should have received a copy of the GNU Lesser Public License
21
* along with libdv; see the file COPYING. If not, write to
22
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24
* The libdv homepage is http://libdv.sourceforge.net/.
31
#define _FILE_OFFSET_BITS 64
37
#include <sys/types.h>
48
# define timersub(a, b, result) \
50
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
51
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
52
if ((result)->tv_usec < 0) { \
54
(result)->tv_usec += 1000000; \
59
#define DV_PLAYER_OPT_VERSION 0
60
#define DV_PLAYER_OPT_DISABLE_AUDIO 1
61
#define DV_PLAYER_OPT_DISABLE_VIDEO 2
62
#define DV_PLAYER_OPT_NUM_FRAMES 3
63
#define DV_PLAYER_OPT_OSS_INCLUDE 4
64
#define DV_PLAYER_OPT_DISPLAY_INCLUDE 5
65
#define DV_PLAYER_OPT_DECODER_INCLUDE 6
66
#define DV_PLAYER_OPT_AUTOHELP 7
67
#define DV_PLAYER_OPT_DUMP_FRAMES 8
68
#define DV_PLAYER_OPT_NO_MMAP 9
69
#define DV_PLAYER_OPT_LOOP_COUNT 10
70
#define DV_PLAYER_NUM_OPTS 11
72
/* Book-keeping for mmap */
73
typedef struct dv_mmap_region_s {
74
void *map_start; /* Start of mapped region (page aligned) */
75
size_t map_length; /* Size of mapped region */
76
guint8 *data_start; /* Data we asked for */
80
dv_decoder_t *decoder;
81
dv_display_t *display;
83
dv_mmap_region_t mmap_region;
86
gint arg_disable_audio;
87
gint arg_disable_video;
90
char* arg_dump_frames;
93
struct poptOption option_table[DV_PLAYER_NUM_OPTS+1];
94
#endif /* HAVE_LIBPOPT */
102
if(!(result = (dv_player_t *)calloc(1,sizeof(dv_player_t)))) goto no_mem;
103
if(!(result->display = dv_display_new())) goto no_display;
104
if(!(result->oss = dv_oss_new())) goto no_oss;
105
if(!(result->decoder = dv_decoder_new(TRUE, FALSE, FALSE))) goto no_decoder;
106
result->arg_loop_count = 1;
109
result->option_table[DV_PLAYER_OPT_VERSION] = (struct poptOption) {
113
descrip: "show playdv version number",
116
result->option_table[DV_PLAYER_OPT_DISABLE_AUDIO] = (struct poptOption) {
117
longName: "disable-audio",
118
arg: &result->arg_disable_audio,
119
descrip: "skip audio decoding",
120
}; /* disable audio */
122
result->option_table[DV_PLAYER_OPT_DISABLE_VIDEO] = (struct poptOption) {
123
longName: "disable-video",
124
descrip: "skip video decoding",
125
arg: &result->arg_disable_video,
126
}; /* disable video */
128
result->option_table[DV_PLAYER_OPT_NUM_FRAMES] = (struct poptOption) {
129
longName: "num-frames",
131
argInfo: POPT_ARG_INT,
132
arg: &result->arg_num_frames,
134
descrip: "stop after <count> frames",
135
}; /* number of frames */
137
result->option_table[DV_PLAYER_OPT_DUMP_FRAMES] = (struct poptOption) {
138
longName: "dump-frames",
139
argInfo: POPT_ARG_STRING,
140
arg: &result->arg_dump_frames,
141
argDescrip: "pattern",
142
descrip: "dump all frames to file pattern like capture%05d.ppm "
144
}; /* dump all frames */
146
result->option_table[DV_PLAYER_OPT_NO_MMAP] = (struct poptOption) {
148
arg: &result->no_mmap,
149
descrip: "don't use mmap for reading. (usefull for pipes)"
152
result->option_table[DV_PLAYER_OPT_LOOP_COUNT] = (struct poptOption) {
153
longName: "loop-count",
155
argInfo: POPT_ARG_INT,
156
arg: &result->arg_loop_count,
158
descrip: "loop playback <count> times, 0 for infinite",
161
result->option_table[DV_PLAYER_OPT_OSS_INCLUDE] = (struct poptOption) {
162
argInfo: POPT_ARG_INCLUDE_TABLE,
163
arg: result->oss->option_table,
164
descrip: "Audio output options",
167
result->option_table[DV_PLAYER_OPT_DISPLAY_INCLUDE] = (struct poptOption) {
168
argInfo: POPT_ARG_INCLUDE_TABLE,
169
arg: result->display->option_table,
170
descrip: "Video output options",
173
result->option_table[DV_PLAYER_OPT_DECODER_INCLUDE] = (struct poptOption) {
174
argInfo: POPT_ARG_INCLUDE_TABLE,
175
arg: result->decoder->option_table,
176
descrip: "Decoder options",
179
result->option_table[DV_PLAYER_OPT_AUTOHELP] = (struct poptOption) {
180
argInfo: POPT_ARG_INCLUDE_TABLE,
181
arg: poptHelpOptions,
182
descrip: "Help options",
185
#endif /* HAVE_LIBPOPT */
192
free(result->display);
198
} /* dv_player_new */
201
/* I decided to try to use mmap for reading the input. I got a slight
202
* (about %5-10) performance improvement */
203
void mmap_unaligned(int fd, int no_mmap, off_t offset, size_t length,
204
dv_mmap_region_t *mmap_region)
209
size_t start_padding;
212
static off_t last_offset = 0;
213
static size_t last_length = 0;
214
static size_t overrun_size = 0;
215
static size_t overrun_offset = 0;
218
size_t to_read = length;
220
if (!mmap_region->map_start) {
221
mmap_region->map_start = malloc(144000);
224
if (last_offset == offset) {
225
if (last_length < length) {
226
to_read -= last_length;
227
} else if (last_length > length) {
228
overrun_size = last_length - length;
229
overrun_offset = length;
230
last_length = length;
236
last_offset = offset;
237
last_length = length;
239
memmove(mmap_region->map_start,
240
mmap_region->map_start + overrun_offset,
242
if (to_read > overrun_size) {
243
to_read -= overrun_size;
246
overrun_size -= to_read;
247
overrun_offset += to_read;
251
while (to_read > 0) {
252
int rval = read(fd, mmap_region->map_start
253
+ length - to_read, to_read);
255
if (to_read != length) {
256
fprintf(stderr, "Short read!\n");
267
mmap_region->map_length = length;
268
mmap_region->data_start = mmap_region->map_start;
270
page_size = getpagesize();
271
start_padding = offset % page_size;
272
real_offset = (offset / page_size) * page_size;
273
real_length = real_offset + start_padding + length;
274
real_start = mmap(0, real_length, PROT_READ, MAP_SHARED,
277
mmap_region->map_start = real_start;
278
mmap_region->map_length = real_length;
279
mmap_region->data_start = real_start + start_padding;
281
} /* mmap_unaligned */
283
int munmap_unaligned(dv_mmap_region_t *mmap_region, int no_mmap)
288
return(munmap(mmap_region->map_start,mmap_region->map_length));
290
} /* munmap_unaligned */
293
main(int argc,char *argv[])
295
dv_player_t *dv_player = NULL;
296
const char *filename; /* name of input file */
298
off_t offset = 0, eof = 0;
299
guint frame_count = 0;
301
gdouble seconds = 0.0;
302
gint16 *audio_buffers[4];
304
int rc; /* return code from popt */
305
poptContext optCon; /* context for parsing command-line options */
306
#endif /* HAVE_LIBPOPT */
308
if(!(dv_player = dv_player_new())) goto no_mem;
311
/* Parse options using popt */
312
optCon = poptGetContext(NULL, argc, (const char **)argv, dv_player->option_table, 0);
313
poptSetOtherOptionHelp(optCon, "[raw dv file or -- for stdin]");
315
while ((rc = poptGetNextOpt(optCon)) > 0) {
318
goto display_version;
325
filename = poptGetArg(optCon);
326
if (rc < -1) goto bad_arg;
327
if((filename == NULL) || !(poptPeekArg(optCon) == NULL))
329
poptFreeContext(optCon);
331
/* No popt, no usage and no options! HINT: get popt if you don't
332
* have it yet, it's at: ftp://ftp.redhat.com/pub/redhat/code/popt
335
#endif /* HAVE_LIBOPT */
337
if (strcmp(filename, "-") == 0) {
338
dv_player->no_mmap = 1;
341
/* Open the input file, do fstat to get it's total size */
342
if(-1 == (fd = open(filename,O_RDONLY))) goto openfail;
344
if (!dv_player->no_mmap) {
345
if(fstat(fd, &dv_player->statbuf)) goto fstatfail;
346
eof = dv_player->statbuf.st_size;
349
dv_player->decoder->quality = dv_player->decoder->video->quality;
351
/* Read in header of first frame to see how big frames are */
352
mmap_unaligned(fd,dv_player->no_mmap,0,120000,&dv_player->mmap_region);
353
if(MAP_FAILED == dv_player->mmap_region.map_start) goto map_failed;
355
if(dv_parse_header(dv_player->decoder, dv_player->mmap_region.data_start)< 0)
356
goto header_parse_error;
358
if (dv_format_wide (dv_player->decoder))
359
fprintf (stderr, "format 16:9\n");
360
if (dv_format_normal (dv_player->decoder))
361
fprintf (stderr, "format 4:3\n");
363
fprintf(stderr, "Audio is %.1f kHz, %d bits quantization, "
364
"%d channels, emphasis %s\n",
365
(float)dv_player->decoder->audio->frequency / 1000.0,
366
dv_player->decoder->audio->quantization,
367
dv_player->decoder->audio->num_channels,
368
(dv_player->decoder->audio->emphasis ? "on" : "off"));
370
munmap_unaligned(&dv_player->mmap_region,dv_player->no_mmap);
372
eof -= dv_player->decoder->frame_size; /* makes loop condition simpler */
374
if(!dv_player->arg_disable_video) {
375
if(!dv_display_init (dv_player->display, &argc, &argv,
376
dv_player->decoder->width, dv_player->decoder->height,
377
dv_player->decoder->sampling, "playdv", "playdv")) goto no_display;
380
dv_player->arg_disable_audio =
381
dv_player->arg_disable_audio || (!dv_oss_init(dv_player->decoder, dv_player->oss));
383
for(i=0; i < 4; i++) {
384
if(!(audio_buffers[i] = malloc(DV_AUDIO_MAX_SAMPLES*sizeof(gint16)))) goto no_mem;
387
gettimeofday(dv_player->tv+0,NULL);
391
dv_player->decoder->prev_frame_decoded = 0;
393
offset <= eof || dv_player->no_mmap;
394
offset += dv_player->decoder->frame_size) {
397
* Map the frame's data into memory
399
mmap_unaligned (fd,dv_player->no_mmap,
400
offset, dv_player->decoder->frame_size,
401
&dv_player->mmap_region);
402
if (MAP_FAILED == dv_player->mmap_region.map_start) goto map_failed;
403
if (dv_parse_header (dv_player->decoder,
404
dv_player->mmap_region.data_start) > 0) {
406
* video norm has changed so remap region for current frame
408
munmap_unaligned (&dv_player->mmap_region,dv_player->no_mmap);
409
mmap_unaligned (fd, dv_player->no_mmap, offset,
410
dv_player->decoder->frame_size, &dv_player->mmap_region);
411
if (MAP_FAILED == dv_player->mmap_region.map_start) goto map_failed;
412
dv_display_set_norm (dv_player->display, dv_player->decoder->system);
415
/* -----------------------------------------------------------------------
416
* now frame is complete, so we may parse all the rest of info packs
418
dv_parse_packs (dv_player -> decoder, dv_player -> mmap_region. data_start);
420
/* -----------------------------------------------------------------------
421
* keep track of any possible format changes of dv source material
423
dv_display_check_format (dv_player->display,
424
dv_format_wide (dv_player->decoder));
426
/* Parse and unshuffle audio */
427
if(!dv_player->arg_disable_audio) {
428
if(dv_decode_full_audio(dv_player->decoder, dv_player->mmap_region.data_start, audio_buffers)) {
429
dv_oss_play(dv_player->decoder, dv_player->oss, audio_buffers);
433
if(!dv_player->arg_disable_video) {
435
if (dv_format_wide (dv_player->decoder))
436
fprintf (stderr, "format 16:9\n");
437
if (dv_format_normal (dv_player->decoder))
438
fprintf (stderr, "format 4:3\n");
441
if (!dv_player->decoder->prev_frame_decoded ||
442
dv_frame_changed (dv_player->decoder)) {
444
dv_report_video_error (dv_player -> decoder,
445
dv_player -> mmap_region. data_start);
447
/* Parse and decode video */
448
dv_decode_full_frame(dv_player->decoder, dv_player->mmap_region.data_start,
449
dv_player->display->color_space,
450
dv_player->display->pixels,
451
dv_player->display->pitches);
452
dv_player->decoder->prev_frame_decoded = 1;
454
fprintf (stderr, "same_frame\n");
457
/* ----------------------------------------------------------------------
458
* save all frames. even it was not nessessary to decode
460
if(dv_player->arg_dump_frames) {
464
if (strcmp(dv_player->arg_dump_frames, "-") == 0) {
467
snprintf(fname, 4096, dv_player->arg_dump_frames,
469
fp = fopen(fname, "w");
471
fprintf(fp, "P6\n# CREATOR: playdv\n%d %d\n255\n",
472
dv_player->display->width, dv_player->display->height);
473
fwrite(dv_player->display->pixels[0],
474
3, dv_player->display->width
475
* dv_player->display->height, fp);
481
dv_display_show(dv_player->display);
485
if((dv_player->arg_num_frames > 0) && (frame_count >= dv_player->arg_num_frames)) {
489
{int dummy;read(0,&dummy,1);}
492
/* Release the frame's data */
493
munmap_unaligned(&dv_player->mmap_region, dv_player->no_mmap);
500
if (--dv_player->arg_loop_count != 0) {
501
lseek( fd, 0, SEEK_SET);
505
gettimeofday(dv_player->tv+1,NULL);
506
timersub(dv_player->tv+1,dv_player->tv+0,dv_player->tv+2);
507
seconds = (double)dv_player->tv[2].tv_usec / 1000000.0;
508
seconds += dv_player->tv[2].tv_sec;
509
fprintf(stderr,"Processed %d frames in %05.2f seconds (%05.2f fps)\n",
510
frame_count, seconds, (double)frame_count/seconds);
511
dv_decoder_free(dv_player->decoder);
515
/* Error handling section */
518
fprintf(stderr,"playdv: version %s, http://libdv.sourceforge.net/\n",
523
/* an error occurred during option processing */
524
fprintf(stderr, "%s: %s\n",
525
poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
541
fprintf(stderr,"Parser error reading first header\n");
544
fprintf(stderr,"Out of memory\n");