Originally committed as revision 1936 to svn://svn.ffmpeg.org/ffmpeg/trunk
Fabrice Bellard authored on 2003/06/08 03:34:02... | ... |
@@ -16,18 +16,22 @@ endif |
16 | 16 |
|
17 | 17 |
ifeq ($(CONFIG_WIN32),yes) |
18 | 18 |
EXE=.exe |
19 |
-PROG=ffmpeg$(EXE) |
|
20 | 19 |
else |
21 | 20 |
ifeq ($(CONFIG_OS2),yes) |
22 | 21 |
EXE=.exe |
23 |
-PROG=ffmpeg$(EXE) |
|
24 | 22 |
else |
25 | 23 |
EXE= |
26 |
-PROG=ffmpeg ffplay |
|
27 |
-ifeq ($(CONFIG_FFSERVER),yes) |
|
28 |
-PROG+=ffserver |
|
29 | 24 |
endif |
30 | 25 |
endif |
26 |
+ |
|
27 |
+PROG=ffmpeg$(EXE) |
|
28 |
+ |
|
29 |
+ifeq ($(CONFIG_FFSERVER),yes) |
|
30 |
+PROG+=ffserver$(EXE) |
|
31 |
+endif |
|
32 |
+ |
|
33 |
+ifeq ($(CONFIG_FFPLAY),yes) |
|
34 |
+PROG+=ffplay$(EXE) |
|
31 | 35 |
endif |
32 | 36 |
|
33 | 37 |
ifeq ($(CONFIG_AUDIO_BEOS),yes) |
... | ... |
@@ -67,7 +71,7 @@ else |
67 | 67 |
TEST=test |
68 | 68 |
endif |
69 | 69 |
|
70 |
-OBJS = ffmpeg.o ffserver.o |
|
70 |
+OBJS = ffmpeg.o ffserver.o cmdutils.o ffplay.o |
|
71 | 71 |
SRCS = $(OBJS:.o=.c) $(ASM_OBJS:.o=.s) |
72 | 72 |
FFLIBS = -L./libavformat -lavformat -L./libavcodec -lavcodec |
73 | 73 |
|
... | ... |
@@ -77,9 +81,8 @@ lib: $(AMRLIBS) |
77 | 77 |
$(MAKE) -C libavcodec all |
78 | 78 |
$(MAKE) -C libavformat all |
79 | 79 |
|
80 |
- |
|
81 |
-ffmpeg_g$(EXE): ffmpeg.o .libs |
|
82 |
- $(CC) $(LDFLAGS) -o $@ ffmpeg.o $(FFLIBS) $(EXTRALIBS) |
|
80 |
+ffmpeg_g$(EXE): ffmpeg.o cmdutils.o .libs |
|
81 |
+ $(CC) $(LDFLAGS) -o $@ ffmpeg.o cmdutils.o $(FFLIBS) $(EXTRALIBS) |
|
83 | 82 |
|
84 | 83 |
ffmpeg$(EXE): ffmpeg_g$(EXE) |
85 | 84 |
cp -p $< $@ |
... | ... |
@@ -88,8 +91,15 @@ ffmpeg$(EXE): ffmpeg_g$(EXE) |
88 | 88 |
ffserver$(EXE): ffserver.o .libs |
89 | 89 |
$(CC) $(LDFLAGS) $(FFSLDFLAGS) -o $@ ffserver.o $(FFLIBS) $(EXTRALIBS) |
90 | 90 |
|
91 |
-ffplay: ffmpeg$(EXE) |
|
92 |
- ln -sf $< $@ |
|
91 |
+ffplay_g$(EXE): ffplay.o cmdutils.o .libs |
|
92 |
+ $(CC) $(LDFLAGS) -o $@ ffplay.o cmdutils.o $(FFLIBS) $(EXTRALIBS) $(SDL_LIBS) |
|
93 |
+ |
|
94 |
+ffplay$(EXE): ffplay_g$(EXE) |
|
95 |
+ cp -p $< $@ |
|
96 |
+ $(STRIP) $@ |
|
97 |
+ |
|
98 |
+ffplay.o: ffplay.c |
|
99 |
+ $(CC) $(CFLAGS) $(SDL_CFLAGS) -c -o $@ $< |
|
93 | 100 |
|
94 | 101 |
%.o: %.c |
95 | 102 |
$(CC) $(CFLAGS) -c -o $@ $< |
... | ... |
@@ -101,7 +111,6 @@ install: all $(INSTALLVHOOK) |
101 | 101 |
$(MAKE) -C libavcodec install |
102 | 102 |
install -d $(prefix)/bin |
103 | 103 |
install -c -s -m 755 $(PROG) $(prefix)/bin |
104 |
- ln -sf ffmpeg $(prefix)/bin/ffplay |
|
105 | 104 |
|
106 | 105 |
install-vhook: $(prefix)/lib/vhook |
107 | 106 |
$(MAKE) -C vhook install INSTDIR=$(prefix)/lib/vhook |
... | ... |
@@ -133,7 +142,7 @@ clean: $(CLEANVHOOK) |
133 | 133 |
$(MAKE) -C libavcodec clean |
134 | 134 |
$(MAKE) -C libavformat clean |
135 | 135 |
$(MAKE) -C tests clean |
136 |
- rm -f *.o *.d *~ .libs .depend gmon.out TAGS ffmpeg_g$(EXE) $(PROG) |
|
136 |
+ rm -f *.o *.d *~ .libs .depend gmon.out TAGS ffmpeg_g$(EXE) ffplay_g$(EXE) $(PROG) |
|
137 | 137 |
|
138 | 138 |
clean-vhook: |
139 | 139 |
$(MAKE) -C vhook clean |
140 | 140 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,120 @@ |
0 |
+/* |
|
1 |
+ * Various utilities for command line tools |
|
2 |
+ * Copyright (c) 2000-2003 Fabrice Bellard |
|
3 |
+ * |
|
4 |
+ * This library is free software; you can redistribute it and/or |
|
5 |
+ * modify it under the terms of the GNU Lesser General Public |
|
6 |
+ * License as published by the Free Software Foundation; either |
|
7 |
+ * version 2 of the License, or (at your option) any later version. |
|
8 |
+ * |
|
9 |
+ * This library is distributed in the hope that it will be useful, |
|
10 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 |
+ * Lesser General Public License for more details. |
|
13 |
+ * |
|
14 |
+ * You should have received a copy of the GNU Lesser General Public |
|
15 |
+ * License along with this library; if not, write to the Free Software |
|
16 |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
17 |
+ */ |
|
18 |
+#include <stdlib.h> |
|
19 |
+#include <stdio.h> |
|
20 |
+#include <string.h> |
|
21 |
+ |
|
22 |
+#include "common.h" |
|
23 |
+#include "avformat.h" |
|
24 |
+ |
|
25 |
+#include "cmdutils.h" |
|
26 |
+ |
|
27 |
+void show_help_options(const OptionDef *options) |
|
28 |
+{ |
|
29 |
+ const OptionDef *po; |
|
30 |
+ int i, expert, first; |
|
31 |
+ |
|
32 |
+ printf("Main options are:\n"); |
|
33 |
+ for(i=0;i<2;i++) { |
|
34 |
+ first = 1; |
|
35 |
+ for(po = options; po->name != NULL; po++) { |
|
36 |
+ char buf[64]; |
|
37 |
+ expert = (po->flags & OPT_EXPERT) != 0; |
|
38 |
+ if (expert == i) { |
|
39 |
+ if (expert && first) { |
|
40 |
+ printf("\nAdvanced options are:\n"); |
|
41 |
+ first = 0; |
|
42 |
+ } |
|
43 |
+ strcpy(buf, po->name); |
|
44 |
+ if (po->flags & HAS_ARG) { |
|
45 |
+ strcat(buf, " "); |
|
46 |
+ strcat(buf, po->argname); |
|
47 |
+ } |
|
48 |
+ printf("-%-17s %s\n", buf, po->help); |
|
49 |
+ } |
|
50 |
+ } |
|
51 |
+ } |
|
52 |
+} |
|
53 |
+ |
|
54 |
+void parse_options(int argc, char **argv, const OptionDef *options) |
|
55 |
+{ |
|
56 |
+ const char *opt, *arg; |
|
57 |
+ int optindex; |
|
58 |
+ const OptionDef *po; |
|
59 |
+ |
|
60 |
+ /* parse options */ |
|
61 |
+ optindex = 1; |
|
62 |
+ while (optindex < argc) { |
|
63 |
+ opt = argv[optindex++]; |
|
64 |
+ |
|
65 |
+ if (opt[0] == '-' && opt[1] != '\0') { |
|
66 |
+ po = options; |
|
67 |
+ while (po->name != NULL) { |
|
68 |
+ if (!strcmp(opt + 1, po->name)) |
|
69 |
+ break; |
|
70 |
+ po++; |
|
71 |
+ } |
|
72 |
+ if (!po->name) { |
|
73 |
+ fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], opt); |
|
74 |
+ exit(1); |
|
75 |
+ } |
|
76 |
+ arg = NULL; |
|
77 |
+ if (po->flags & HAS_ARG) { |
|
78 |
+ arg = argv[optindex++]; |
|
79 |
+ if (!arg) { |
|
80 |
+ fprintf(stderr, "%s: missing argument for option '%s'\n", argv[0], opt); |
|
81 |
+ exit(1); |
|
82 |
+ } |
|
83 |
+ } |
|
84 |
+ if (po->flags & OPT_STRING) { |
|
85 |
+ char *str; |
|
86 |
+ str = strdup(arg); |
|
87 |
+ *po->u.str_arg = str; |
|
88 |
+ } else if (po->flags & OPT_BOOL) { |
|
89 |
+ *po->u.int_arg = 1; |
|
90 |
+ } else { |
|
91 |
+ po->u.func_arg(arg); |
|
92 |
+ } |
|
93 |
+ } else { |
|
94 |
+ parse_arg_file(opt); |
|
95 |
+ } |
|
96 |
+ } |
|
97 |
+} |
|
98 |
+ |
|
99 |
+void print_error(const char *filename, int err) |
|
100 |
+{ |
|
101 |
+ switch(err) { |
|
102 |
+ case AVERROR_NUMEXPECTED: |
|
103 |
+ fprintf(stderr, "%s: Incorrect image filename syntax.\n" |
|
104 |
+ "Use '%%d' to specify the image number:\n" |
|
105 |
+ " for img1.jpg, img2.jpg, ..., use 'img%%d.jpg';\n" |
|
106 |
+ " for img001.jpg, img002.jpg, ..., use 'img%%03d.jpg'.\n", |
|
107 |
+ filename); |
|
108 |
+ break; |
|
109 |
+ case AVERROR_INVALIDDATA: |
|
110 |
+ fprintf(stderr, "%s: Error while parsing header\n", filename); |
|
111 |
+ break; |
|
112 |
+ case AVERROR_NOFMT: |
|
113 |
+ fprintf(stderr, "%s: Unknown format\n", filename); |
|
114 |
+ break; |
|
115 |
+ default: |
|
116 |
+ fprintf(stderr, "%s: Error while opening file\n", filename); |
|
117 |
+ break; |
|
118 |
+ } |
|
119 |
+} |
0 | 120 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,25 @@ |
0 |
+#ifndef _CMD_UTILS_H |
|
1 |
+#define _CMD_UTILS_H |
|
2 |
+ |
|
3 |
+typedef struct { |
|
4 |
+ const char *name; |
|
5 |
+ int flags; |
|
6 |
+#define HAS_ARG 0x0001 |
|
7 |
+#define OPT_BOOL 0x0002 |
|
8 |
+#define OPT_EXPERT 0x0004 |
|
9 |
+#define OPT_STRING 0x0008 |
|
10 |
+ union { |
|
11 |
+ void (*func_arg)(const char *); |
|
12 |
+ int *int_arg; |
|
13 |
+ char **str_arg; |
|
14 |
+ } u; |
|
15 |
+ const char *help; |
|
16 |
+ const char *argname; |
|
17 |
+} OptionDef; |
|
18 |
+ |
|
19 |
+void show_help_options(const OptionDef *options); |
|
20 |
+void parse_options(int argc, char **argv, const OptionDef *options); |
|
21 |
+void parse_arg_file(const char *filename); |
|
22 |
+void print_error(const char *filename, int err); |
|
23 |
+ |
|
24 |
+#endif /* _CMD_UTILS_H */ |
... | ... |
@@ -1,6 +1,6 @@ |
1 | 1 |
/* |
2 | 2 |
* FFmpeg main |
3 |
- * Copyright (c) 2000, 2001, 2002 Fabrice Bellard |
|
3 |
+ * Copyright (c) 2000-2003 Fabrice Bellard |
|
4 | 4 |
* |
5 | 5 |
* This library is free software; you can redistribute it and/or |
6 | 6 |
* modify it under the terms of the GNU Lesser General Public |
... | ... |
@@ -40,28 +40,14 @@ |
40 | 40 |
#include <time.h> |
41 | 41 |
#include <ctype.h> |
42 | 42 |
|
43 |
+#include "cmdutils.h" |
|
44 |
+ |
|
43 | 45 |
#if !defined(INFINITY) && defined(HUGE_VAL) |
44 | 46 |
#define INFINITY HUGE_VAL |
45 | 47 |
#endif |
46 | 48 |
|
47 | 49 |
#define MAXINT64 int64_t_C(0x7fffffffffffffff) |
48 | 50 |
|
49 |
-typedef struct { |
|
50 |
- const char *name; |
|
51 |
- int flags; |
|
52 |
-#define HAS_ARG 0x0001 |
|
53 |
-#define OPT_BOOL 0x0002 |
|
54 |
-#define OPT_EXPERT 0x0004 |
|
55 |
-#define OPT_STRING 0x0008 |
|
56 |
- union { |
|
57 |
- void (*func_arg)(const char *); |
|
58 |
- int *int_arg; |
|
59 |
- char **str_arg; |
|
60 |
- } u; |
|
61 |
- const char *help; |
|
62 |
- const char *argname; |
|
63 |
-} OptionDef; |
|
64 |
- |
|
65 | 51 |
/* select an input stream for an output stream */ |
66 | 52 |
typedef struct AVStreamMap { |
67 | 53 |
int file_index; |
... | ... |
@@ -133,7 +119,6 @@ static int use_4mv = 0; |
133 | 133 |
static int use_aic = 0; |
134 | 134 |
static int use_umv = 0; |
135 | 135 |
/* /Fx */ |
136 |
-static int use_h263p_extra = 0; |
|
137 | 136 |
static int do_deinterlace = 0; |
138 | 137 |
static int workaround_bugs = FF_BUG_AUTODETECT; |
139 | 138 |
static int error_resilience = 2; |
... | ... |
@@ -161,7 +146,6 @@ static char *str_copyright = NULL; |
161 | 161 |
static char *str_comment = NULL; |
162 | 162 |
static int do_benchmark = 0; |
163 | 163 |
static int do_hex_dump = 0; |
164 |
-static int do_play = 0; |
|
165 | 164 |
static int do_psnr = 0; |
166 | 165 |
static int do_vstats = 0; |
167 | 166 |
static int do_pass = 0; |
... | ... |
@@ -1134,11 +1118,7 @@ static int av_encode(AVFormatContext **output_files, |
1134 | 1134 |
} |
1135 | 1135 |
|
1136 | 1136 |
#ifndef CONFIG_WIN32 |
1137 |
- if (!do_play) { |
|
1138 |
- fprintf(stderr, "Press [q] to stop encoding\n"); |
|
1139 |
- } else { |
|
1140 |
- fprintf(stderr, "Press [q] to stop playing\n"); |
|
1141 |
- } |
|
1137 |
+ fprintf(stderr, "Press [q] to stop encoding\n"); |
|
1142 | 1138 |
#endif |
1143 | 1139 |
term_init(); |
1144 | 1140 |
|
... | ... |
@@ -1998,28 +1978,6 @@ static void opt_recording_time(const char *arg) |
1998 | 1998 |
recording_time = parse_date(arg, 1); |
1999 | 1999 |
} |
2000 | 2000 |
|
2001 |
-static void print_error(const char *filename, int err) |
|
2002 |
-{ |
|
2003 |
- switch(err) { |
|
2004 |
- case AVERROR_NUMEXPECTED: |
|
2005 |
- fprintf(stderr, "%s: Incorrect image filename syntax.\n" |
|
2006 |
- "Use '%%d' to specify the image number:\n" |
|
2007 |
- " for img1.jpg, img2.jpg, ..., use 'img%%d.jpg';\n" |
|
2008 |
- " for img001.jpg, img002.jpg, ..., use 'img%%03d.jpg'.\n", |
|
2009 |
- filename); |
|
2010 |
- break; |
|
2011 |
- case AVERROR_INVALIDDATA: |
|
2012 |
- fprintf(stderr, "%s: Error while parsing header\n", filename); |
|
2013 |
- break; |
|
2014 |
- case AVERROR_NOFMT: |
|
2015 |
- fprintf(stderr, "%s: Unknown format\n", filename); |
|
2016 |
- break; |
|
2017 |
- default: |
|
2018 |
- fprintf(stderr, "%s: Error while opening file\n", filename); |
|
2019 |
- break; |
|
2020 |
- } |
|
2021 |
-} |
|
2022 |
- |
|
2023 | 2001 |
static void opt_input_file(const char *filename) |
2024 | 2002 |
{ |
2025 | 2003 |
AVFormatContext *ic; |
... | ... |
@@ -2524,40 +2482,6 @@ static void prepare_grab(void) |
2524 | 2524 |
} |
2525 | 2525 |
} |
2526 | 2526 |
|
2527 |
-/* open the necessary output devices for playing */ |
|
2528 |
-static void prepare_play(void) |
|
2529 |
-{ |
|
2530 |
- int has_video, has_audio; |
|
2531 |
- |
|
2532 |
- check_audio_video_inputs(&has_video, &has_audio); |
|
2533 |
- |
|
2534 |
- /* manual disable */ |
|
2535 |
- if (audio_disable) { |
|
2536 |
- has_audio = 0; |
|
2537 |
- } |
|
2538 |
- if (video_disable) { |
|
2539 |
- has_video = 0; |
|
2540 |
- } |
|
2541 |
- |
|
2542 |
- if (has_audio) { |
|
2543 |
- file_oformat = guess_format("audio_device", NULL, NULL); |
|
2544 |
- if (!file_oformat) { |
|
2545 |
- fprintf(stderr, "Could not find audio device\n"); |
|
2546 |
- exit(1); |
|
2547 |
- } |
|
2548 |
- opt_output_file(audio_device?audio_device:"/dev/dsp"); |
|
2549 |
- } |
|
2550 |
- |
|
2551 |
- if (has_video) { |
|
2552 |
- file_oformat = guess_format("framebuffer_device", NULL, NULL); |
|
2553 |
- if (!file_oformat) { |
|
2554 |
- fprintf(stderr, "Could not find framebuffer device\n"); |
|
2555 |
- exit(1); |
|
2556 |
- } |
|
2557 |
- opt_output_file(""); |
|
2558 |
- } |
|
2559 |
-} |
|
2560 |
- |
|
2561 | 2527 |
/* same option as mencoder */ |
2562 | 2528 |
static void opt_pass(const char *pass_str) |
2563 | 2529 |
{ |
... | ... |
@@ -2668,47 +2592,6 @@ static void show_formats(void) |
2668 | 2668 |
exit(1); |
2669 | 2669 |
} |
2670 | 2670 |
|
2671 |
-void show_help(void) |
|
2672 |
-{ |
|
2673 |
- const char *prog; |
|
2674 |
- const OptionDef *po; |
|
2675 |
- int i, expert; |
|
2676 |
- |
|
2677 |
- prog = do_play ? "ffplay" : "ffmpeg"; |
|
2678 |
- |
|
2679 |
- printf("%s version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n", |
|
2680 |
- prog); |
|
2681 |
- |
|
2682 |
- if (!do_play) { |
|
2683 |
- printf("usage: ffmpeg [[options] -i input_file]... {[options] outfile}...\n" |
|
2684 |
- "Hyper fast MPEG1/MPEG4/H263/RV and AC3/MPEG audio encoder\n"); |
|
2685 |
- } else { |
|
2686 |
- printf("usage: ffplay [options] input_file...\n" |
|
2687 |
- "Simple audio player\n"); |
|
2688 |
- } |
|
2689 |
- |
|
2690 |
- printf("\n" |
|
2691 |
- "Main options are:\n"); |
|
2692 |
- for(i=0;i<2;i++) { |
|
2693 |
- if (i == 1) |
|
2694 |
- printf("\nAdvanced options are:\n"); |
|
2695 |
- for(po = options; po->name != NULL; po++) { |
|
2696 |
- char buf[64]; |
|
2697 |
- expert = (po->flags & OPT_EXPERT) != 0; |
|
2698 |
- if (expert == i) { |
|
2699 |
- strcpy(buf, po->name); |
|
2700 |
- if (po->flags & HAS_ARG) { |
|
2701 |
- strcat(buf, " "); |
|
2702 |
- strcat(buf, po->argname); |
|
2703 |
- } |
|
2704 |
- printf("-%-17s %s\n", buf, po->help); |
|
2705 |
- } |
|
2706 |
- } |
|
2707 |
- } |
|
2708 |
- |
|
2709 |
- exit(1); |
|
2710 |
-} |
|
2711 |
- |
|
2712 | 2671 |
const OptionDef options[] = { |
2713 | 2672 |
{ "L", 0, {(void*)show_licence}, "show license" }, |
2714 | 2673 |
{ "h", 0, {(void*)show_help}, "show help" }, |
... | ... |
@@ -2803,83 +2686,42 @@ const OptionDef options[] = { |
2803 | 2803 |
{ NULL, }, |
2804 | 2804 |
}; |
2805 | 2805 |
|
2806 |
+void show_help(void) |
|
2807 |
+{ |
|
2808 |
+ printf("ffmpeg version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"); |
|
2809 |
+ printf("usage: ffmpeg [[options] -i input_file]... {[options] outfile}...\n" |
|
2810 |
+ "Hyper fast Audio and Video encoder\n"); |
|
2811 |
+ printf("\n"); |
|
2812 |
+ show_help_options(options); |
|
2813 |
+ exit(1); |
|
2814 |
+} |
|
2815 |
+ |
|
2816 |
+void parse_arg_file(const char *filename) |
|
2817 |
+{ |
|
2818 |
+ opt_output_file(filename); |
|
2819 |
+} |
|
2820 |
+ |
|
2806 | 2821 |
int main(int argc, char **argv) |
2807 | 2822 |
{ |
2808 |
- int optindex, i; |
|
2809 |
- const char *opt, *arg; |
|
2810 |
- const OptionDef *po; |
|
2823 |
+ int i; |
|
2811 | 2824 |
int64_t ti; |
2812 | 2825 |
|
2813 | 2826 |
av_register_all(); |
2814 | 2827 |
|
2815 |
- /* detect if invoked as player */ |
|
2816 |
- i = strlen(argv[0]); |
|
2817 |
- if (i >= 6 && !strcmp(argv[0] + i - 6, "ffplay")) |
|
2818 |
- do_play = 1; |
|
2819 |
- |
|
2820 | 2828 |
if (argc <= 1) |
2821 | 2829 |
show_help(); |
2822 | 2830 |
|
2823 | 2831 |
/* parse options */ |
2824 |
- optindex = 1; |
|
2825 |
- while (optindex < argc) { |
|
2826 |
- opt = argv[optindex++]; |
|
2827 |
- |
|
2828 |
- if (opt[0] == '-' && opt[1] != '\0') { |
|
2829 |
- po = options; |
|
2830 |
- while (po->name != NULL) { |
|
2831 |
- if (!strcmp(opt + 1, po->name)) |
|
2832 |
- break; |
|
2833 |
- po++; |
|
2834 |
- } |
|
2835 |
- if (!po->name) { |
|
2836 |
- fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], opt); |
|
2837 |
- exit(1); |
|
2838 |
- } |
|
2839 |
- arg = NULL; |
|
2840 |
- if (po->flags & HAS_ARG) { |
|
2841 |
- arg = argv[optindex++]; |
|
2842 |
- if (!arg) { |
|
2843 |
- fprintf(stderr, "%s: missing argument for option '%s'\n", argv[0], opt); |
|
2844 |
- exit(1); |
|
2845 |
- } |
|
2846 |
- } |
|
2847 |
- if (po->flags & OPT_STRING) { |
|
2848 |
- char *str; |
|
2849 |
- str = av_strdup(arg); |
|
2850 |
- *po->u.str_arg = str; |
|
2851 |
- } else if (po->flags & OPT_BOOL) { |
|
2852 |
- *po->u.int_arg = 1; |
|
2853 |
- } else { |
|
2854 |
- po->u.func_arg(arg); |
|
2855 |
- } |
|
2856 |
- } else { |
|
2857 |
- if (!do_play) { |
|
2858 |
- opt_output_file(opt); |
|
2859 |
- } else { |
|
2860 |
- opt_input_file(opt); |
|
2861 |
- } |
|
2862 |
- } |
|
2863 |
- } |
|
2832 |
+ parse_options(argc, argv, options); |
|
2864 | 2833 |
|
2865 |
- |
|
2866 |
- if (!do_play) { |
|
2867 |
- /* file converter / grab */ |
|
2868 |
- if (nb_output_files <= 0) { |
|
2869 |
- fprintf(stderr, "Must supply at least one output file\n"); |
|
2870 |
- exit(1); |
|
2871 |
- } |
|
2872 |
- |
|
2873 |
- if (nb_input_files == 0) { |
|
2874 |
- prepare_grab(); |
|
2875 |
- } |
|
2876 |
- } else { |
|
2877 |
- /* player */ |
|
2878 |
- if (nb_input_files <= 0) { |
|
2879 |
- fprintf(stderr, "Must supply at least one input file\n"); |
|
2880 |
- exit(1); |
|
2881 |
- } |
|
2882 |
- prepare_play(); |
|
2834 |
+ /* file converter / grab */ |
|
2835 |
+ if (nb_output_files <= 0) { |
|
2836 |
+ fprintf(stderr, "Must supply at least one output file\n"); |
|
2837 |
+ exit(1); |
|
2838 |
+ } |
|
2839 |
+ |
|
2840 |
+ if (nb_input_files == 0) { |
|
2841 |
+ prepare_grab(); |
|
2883 | 2842 |
} |
2884 | 2843 |
|
2885 | 2844 |
ti = getutime(); |
2886 | 2845 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,1397 @@ |
0 |
+/* |
|
1 |
+ * FFplay : Simple Media Player based on the ffmpeg libraries |
|
2 |
+ * Copyright (c) 2003 Fabrice Bellard |
|
3 |
+ * |
|
4 |
+ * This library is free software; you can redistribute it and/or |
|
5 |
+ * modify it under the terms of the GNU Lesser General Public |
|
6 |
+ * License as published by the Free Software Foundation; either |
|
7 |
+ * version 2 of the License, or (at your option) any later version. |
|
8 |
+ * |
|
9 |
+ * This library is distributed in the hope that it will be useful, |
|
10 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 |
+ * Lesser General Public License for more details. |
|
13 |
+ * |
|
14 |
+ * You should have received a copy of the GNU Lesser General Public |
|
15 |
+ * License along with this library; if not, write to the Free Software |
|
16 |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
17 |
+ */ |
|
18 |
+#define HAVE_AV_CONFIG_H |
|
19 |
+#include "common.h" |
|
20 |
+#include "avformat.h" |
|
21 |
+ |
|
22 |
+#include "cmdutils.h" |
|
23 |
+ |
|
24 |
+#include <SDL.h> |
|
25 |
+#include <SDL_thread.h> |
|
26 |
+ |
|
27 |
+#if defined(__linux__) |
|
28 |
+#define HAVE_X11 |
|
29 |
+#endif |
|
30 |
+ |
|
31 |
+#ifdef HAVE_X11 |
|
32 |
+#include <X11/Xlib.h> |
|
33 |
+#endif |
|
34 |
+ |
|
35 |
+#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) |
|
36 |
+#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) |
|
37 |
+ |
|
38 |
+/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */ |
|
39 |
+#define SAMPLE_ARRAY_SIZE (2*65536) |
|
40 |
+ |
|
41 |
+typedef struct PacketQueue { |
|
42 |
+ AVPacketList *first_pkt, *last_pkt; |
|
43 |
+ int nb_packets; |
|
44 |
+ int size; |
|
45 |
+ int abort_request; |
|
46 |
+ SDL_mutex *mutex; |
|
47 |
+ SDL_cond *cond; |
|
48 |
+} PacketQueue; |
|
49 |
+ |
|
50 |
+#define VIDEO_PICTURE_QUEUE_SIZE 1 |
|
51 |
+ |
|
52 |
+typedef struct VideoPicture { |
|
53 |
+ int delay; /* delay before showing the next picture */ |
|
54 |
+ SDL_Overlay *bmp; |
|
55 |
+ int width, height; /* source height & width */ |
|
56 |
+ int allocated; |
|
57 |
+} VideoPicture; |
|
58 |
+ |
|
59 |
+enum { |
|
60 |
+ AV_SYNC_AUDIO_MASTER, /* default choice */ |
|
61 |
+ AV_SYNC_VIDEO_MASTER, |
|
62 |
+ AV_SYNC_EXTERNAL_CLOCK, /* if external clock, then you must update external_clock yourself */ |
|
63 |
+}; |
|
64 |
+ |
|
65 |
+typedef struct VideoState { |
|
66 |
+ SDL_Thread *parse_tid; |
|
67 |
+ SDL_Thread *video_tid; |
|
68 |
+ int no_background; |
|
69 |
+ int abort_request; |
|
70 |
+ int paused; |
|
71 |
+ AVFormatContext *ic; |
|
72 |
+ int dtg_active_format; |
|
73 |
+ |
|
74 |
+ int audio_stream; |
|
75 |
+ |
|
76 |
+ int av_sync_type; |
|
77 |
+ double external_clock; /* external clock */ |
|
78 |
+ |
|
79 |
+ double audio_clock; /* current audio clock value */ |
|
80 |
+ AVStream *audio_st; |
|
81 |
+ PacketQueue audioq; |
|
82 |
+ int audio_hw_buf_size; |
|
83 |
+ /* samples output by the codec. we reserve more space for avsync |
|
84 |
+ compensation */ |
|
85 |
+ uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; |
|
86 |
+ int audio_buf_size; /* in bytes */ |
|
87 |
+ int audio_buf_index; /* in bytes */ |
|
88 |
+ AVPacket audio_pkt; |
|
89 |
+ uint8_t *audio_pkt_data; |
|
90 |
+ int audio_pkt_size; |
|
91 |
+ int64_t audio_pkt_ipts; |
|
92 |
+ |
|
93 |
+ int show_audio; /* if true, display audio samples */ |
|
94 |
+ int16_t sample_array[SAMPLE_ARRAY_SIZE]; |
|
95 |
+ int sample_array_index; |
|
96 |
+ |
|
97 |
+ double video_clock; /* current video clock value */ |
|
98 |
+ int video_stream; |
|
99 |
+ AVStream *video_st; |
|
100 |
+ PacketQueue videoq; |
|
101 |
+ |
|
102 |
+ VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; |
|
103 |
+ int pictq_size, pictq_rindex, pictq_windex; |
|
104 |
+ SDL_mutex *pictq_mutex; |
|
105 |
+ SDL_cond *pictq_cond; |
|
106 |
+ |
|
107 |
+ // QETimer *video_timer; |
|
108 |
+ char filename[1024]; |
|
109 |
+ int width, height, xleft, ytop; |
|
110 |
+} VideoState; |
|
111 |
+ |
|
112 |
+void show_help(void); |
|
113 |
+int audio_write_get_buf_size(VideoState *is); |
|
114 |
+ |
|
115 |
+/* options specified by the user */ |
|
116 |
+static AVInputFormat *file_iformat; |
|
117 |
+static const char *input_filename; |
|
118 |
+static int fs_screen_width; |
|
119 |
+static int fs_screen_height; |
|
120 |
+static int screen_width = 640; |
|
121 |
+static int screen_height = 480; |
|
122 |
+static int audio_disable; |
|
123 |
+static int video_disable; |
|
124 |
+static int display_disable; |
|
125 |
+static int show_status; |
|
126 |
+ |
|
127 |
+/* current context */ |
|
128 |
+static int is_full_screen; |
|
129 |
+static VideoState *cur_stream; |
|
130 |
+static int16_t audio_callback_time; |
|
131 |
+ |
|
132 |
+#define FF_ALLOC_EVENT (SDL_USEREVENT) |
|
133 |
+#define FF_REFRESH_EVENT (SDL_USEREVENT + 1) |
|
134 |
+ |
|
135 |
+SDL_Surface *screen; |
|
136 |
+ |
|
137 |
+/* packet queue handling */ |
|
138 |
+static void packet_queue_init(PacketQueue *q) |
|
139 |
+{ |
|
140 |
+ memset(q, 0, sizeof(PacketQueue)); |
|
141 |
+ q->mutex = SDL_CreateMutex(); |
|
142 |
+ q->cond = SDL_CreateCond(); |
|
143 |
+} |
|
144 |
+ |
|
145 |
+static void packet_queue_end(PacketQueue *q) |
|
146 |
+{ |
|
147 |
+ AVPacketList *pkt, *pkt1; |
|
148 |
+ |
|
149 |
+ for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { |
|
150 |
+ pkt1 = pkt->next; |
|
151 |
+ av_free_packet(&pkt->pkt); |
|
152 |
+ } |
|
153 |
+ SDL_DestroyMutex(q->mutex); |
|
154 |
+ SDL_DestroyCond(q->cond); |
|
155 |
+} |
|
156 |
+ |
|
157 |
+static int packet_queue_put(PacketQueue *q, AVPacket *pkt) |
|
158 |
+{ |
|
159 |
+ AVPacketList *pkt1; |
|
160 |
+ |
|
161 |
+ pkt1 = av_malloc(sizeof(AVPacketList)); |
|
162 |
+ if (!pkt1) |
|
163 |
+ return -1; |
|
164 |
+ pkt1->pkt = *pkt; |
|
165 |
+ pkt1->next = NULL; |
|
166 |
+ |
|
167 |
+ SDL_LockMutex(q->mutex); |
|
168 |
+ |
|
169 |
+ if (!q->last_pkt) |
|
170 |
+ |
|
171 |
+ q->first_pkt = pkt1; |
|
172 |
+ else |
|
173 |
+ q->last_pkt->next = pkt1; |
|
174 |
+ q->last_pkt = pkt1; |
|
175 |
+ q->nb_packets++; |
|
176 |
+ q->size += pkt1->pkt.size; |
|
177 |
+ /* XXX: should duplicate packet data in DV case */ |
|
178 |
+ SDL_CondSignal(q->cond); |
|
179 |
+ |
|
180 |
+ SDL_UnlockMutex(q->mutex); |
|
181 |
+ return 0; |
|
182 |
+} |
|
183 |
+ |
|
184 |
+static void packet_queue_abort(PacketQueue *q) |
|
185 |
+{ |
|
186 |
+ SDL_LockMutex(q->mutex); |
|
187 |
+ |
|
188 |
+ q->abort_request = 1; |
|
189 |
+ |
|
190 |
+ SDL_CondSignal(q->cond); |
|
191 |
+ |
|
192 |
+ SDL_UnlockMutex(q->mutex); |
|
193 |
+} |
|
194 |
+ |
|
195 |
+/* return < 0 if aborted, 0 if no packet and > 0 if packet. */ |
|
196 |
+static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) |
|
197 |
+{ |
|
198 |
+ AVPacketList *pkt1; |
|
199 |
+ int ret; |
|
200 |
+ |
|
201 |
+ SDL_LockMutex(q->mutex); |
|
202 |
+ |
|
203 |
+ for(;;) { |
|
204 |
+ if (q->abort_request) { |
|
205 |
+ ret = -1; |
|
206 |
+ break; |
|
207 |
+ } |
|
208 |
+ |
|
209 |
+ pkt1 = q->first_pkt; |
|
210 |
+ if (pkt1) { |
|
211 |
+ q->first_pkt = pkt1->next; |
|
212 |
+ if (!q->first_pkt) |
|
213 |
+ q->last_pkt = NULL; |
|
214 |
+ q->nb_packets--; |
|
215 |
+ q->size -= pkt1->pkt.size; |
|
216 |
+ *pkt = pkt1->pkt; |
|
217 |
+ av_free(pkt1); |
|
218 |
+ ret = 1; |
|
219 |
+ break; |
|
220 |
+ } else if (!block) { |
|
221 |
+ ret = 0; |
|
222 |
+ break; |
|
223 |
+ } else { |
|
224 |
+ SDL_CondWait(q->cond, q->mutex); |
|
225 |
+ } |
|
226 |
+ } |
|
227 |
+ SDL_UnlockMutex(q->mutex); |
|
228 |
+ return ret; |
|
229 |
+} |
|
230 |
+ |
|
231 |
+static inline void fill_rectangle(SDL_Surface *screen, |
|
232 |
+ int x, int y, int w, int h, int color) |
|
233 |
+{ |
|
234 |
+ SDL_Rect rect; |
|
235 |
+ rect.x = x; |
|
236 |
+ rect.y = y; |
|
237 |
+ rect.w = w; |
|
238 |
+ rect.h = h; |
|
239 |
+ SDL_FillRect(screen, &rect, color); |
|
240 |
+} |
|
241 |
+ |
|
242 |
+#if 0 |
|
243 |
+/* draw only the border of a rectangle */ |
|
244 |
+void fill_border(VideoState *s, int x, int y, int w, int h, int color) |
|
245 |
+{ |
|
246 |
+ int w1, w2, h1, h2; |
|
247 |
+ |
|
248 |
+ /* fill the background */ |
|
249 |
+ w1 = x; |
|
250 |
+ if (w1 < 0) |
|
251 |
+ w1 = 0; |
|
252 |
+ w2 = s->width - (x + w); |
|
253 |
+ if (w2 < 0) |
|
254 |
+ w2 = 0; |
|
255 |
+ h1 = y; |
|
256 |
+ if (h1 < 0) |
|
257 |
+ h1 = 0; |
|
258 |
+ h2 = s->height - (y + h); |
|
259 |
+ if (h2 < 0) |
|
260 |
+ h2 = 0; |
|
261 |
+ fill_rectangle(screen, |
|
262 |
+ s->xleft, s->ytop, |
|
263 |
+ w1, s->height, |
|
264 |
+ color); |
|
265 |
+ fill_rectangle(screen, |
|
266 |
+ s->xleft + s->width - w2, s->ytop, |
|
267 |
+ w2, s->height, |
|
268 |
+ color); |
|
269 |
+ fill_rectangle(screen, |
|
270 |
+ s->xleft + w1, s->ytop, |
|
271 |
+ s->width - w1 - w2, h1, |
|
272 |
+ color); |
|
273 |
+ fill_rectangle(screen, |
|
274 |
+ s->xleft + w1, s->ytop + s->height - h2, |
|
275 |
+ s->width - w1 - w2, h2, |
|
276 |
+ color); |
|
277 |
+} |
|
278 |
+#endif |
|
279 |
+ |
|
280 |
+static void video_image_display(VideoState *is) |
|
281 |
+{ |
|
282 |
+ VideoPicture *vp; |
|
283 |
+ float aspect_ratio; |
|
284 |
+ int width, height, x, y; |
|
285 |
+ SDL_Rect rect; |
|
286 |
+ |
|
287 |
+ vp = &is->pictq[is->pictq_rindex]; |
|
288 |
+ if (vp->bmp) { |
|
289 |
+ /* XXX: use variable in the frame */ |
|
290 |
+ aspect_ratio = is->video_st->codec.aspect_ratio; |
|
291 |
+ if (aspect_ratio <= 0.0) |
|
292 |
+ aspect_ratio = (float)is->video_st->codec.width / |
|
293 |
+ (float)is->video_st->codec.height; |
|
294 |
+ /* if an active format is indicated, then it overrides the |
|
295 |
+ mpeg format */ |
|
296 |
+#if 0 |
|
297 |
+ if (is->video_st->codec.dtg_active_format != is->dtg_active_format) { |
|
298 |
+ is->dtg_active_format = is->video_st->codec.dtg_active_format; |
|
299 |
+ printf("dtg_active_format=%d\n", is->dtg_active_format); |
|
300 |
+ } |
|
301 |
+#endif |
|
302 |
+#if 0 |
|
303 |
+ switch(is->video_st->codec.dtg_active_format) { |
|
304 |
+ case FF_DTG_AFD_SAME: |
|
305 |
+ default: |
|
306 |
+ /* nothing to do */ |
|
307 |
+ break; |
|
308 |
+ case FF_DTG_AFD_4_3: |
|
309 |
+ aspect_ratio = 4.0 / 3.0; |
|
310 |
+ break; |
|
311 |
+ case FF_DTG_AFD_16_9: |
|
312 |
+ aspect_ratio = 16.0 / 9.0; |
|
313 |
+ break; |
|
314 |
+ case FF_DTG_AFD_14_9: |
|
315 |
+ aspect_ratio = 14.0 / 9.0; |
|
316 |
+ break; |
|
317 |
+ case FF_DTG_AFD_4_3_SP_14_9: |
|
318 |
+ aspect_ratio = 14.0 / 9.0; |
|
319 |
+ break; |
|
320 |
+ case FF_DTG_AFD_16_9_SP_14_9: |
|
321 |
+ aspect_ratio = 14.0 / 9.0; |
|
322 |
+ break; |
|
323 |
+ case FF_DTG_AFD_SP_4_3: |
|
324 |
+ aspect_ratio = 4.0 / 3.0; |
|
325 |
+ break; |
|
326 |
+ } |
|
327 |
+#endif |
|
328 |
+ |
|
329 |
+ /* XXX: we suppose the screen has a 1.0 pixel ratio */ |
|
330 |
+ height = is->height; |
|
331 |
+ width = ((int)rint(height * aspect_ratio)) & -3; |
|
332 |
+ if (width > is->width) { |
|
333 |
+ width = is->width; |
|
334 |
+ height = ((int)rint(width / aspect_ratio)) & -3; |
|
335 |
+ } |
|
336 |
+ x = (is->width - width) / 2; |
|
337 |
+ y = (is->height - height) / 2; |
|
338 |
+ if (!is->no_background) { |
|
339 |
+ /* fill the background */ |
|
340 |
+ // fill_border(is, x, y, width, height, QERGB(0x00, 0x00, 0x00)); |
|
341 |
+ } else { |
|
342 |
+ is->no_background = 0; |
|
343 |
+ } |
|
344 |
+ rect.x = is->xleft + x; |
|
345 |
+ rect.y = is->xleft + y; |
|
346 |
+ rect.w = width; |
|
347 |
+ rect.h = height; |
|
348 |
+ SDL_DisplayYUVOverlay(vp->bmp, &rect); |
|
349 |
+ } else { |
|
350 |
+#if 0 |
|
351 |
+ fill_rectangle(screen, |
|
352 |
+ is->xleft, is->ytop, is->width, is->height, |
|
353 |
+ QERGB(0x00, 0x00, 0x00)); |
|
354 |
+#endif |
|
355 |
+ } |
|
356 |
+} |
|
357 |
+ |
|
358 |
+static inline int compute_mod(int a, int b) |
|
359 |
+{ |
|
360 |
+ a = a % b; |
|
361 |
+ if (a >= 0) |
|
362 |
+ return a; |
|
363 |
+ else |
|
364 |
+ return a + b; |
|
365 |
+} |
|
366 |
+ |
|
367 |
+static void video_audio_display(VideoState *s) |
|
368 |
+{ |
|
369 |
+ int i, i_start, x, y1, y, ys, delay, n, nb_display_channels; |
|
370 |
+ int ch, channels, h, h2, bgcolor, fgcolor; |
|
371 |
+ int16_t time_diff; |
|
372 |
+ |
|
373 |
+ /* compute display index : center on currently output samples */ |
|
374 |
+ channels = s->audio_st->codec.channels; |
|
375 |
+ nb_display_channels = channels; |
|
376 |
+ n = 2 * channels; |
|
377 |
+ delay = audio_write_get_buf_size(s); |
|
378 |
+ delay /= n; |
|
379 |
+ |
|
380 |
+ /* to be more precise, we take into account the time spent since |
|
381 |
+ the last buffer computation */ |
|
382 |
+ if (audio_callback_time) { |
|
383 |
+ time_diff = av_gettime() - audio_callback_time; |
|
384 |
+ delay += (time_diff * s->audio_st->codec.sample_rate) / 1000000; |
|
385 |
+ } |
|
386 |
+ |
|
387 |
+ delay -= s->width / 2; |
|
388 |
+ if (delay < s->width) |
|
389 |
+ delay = s->width; |
|
390 |
+ i_start = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE); |
|
391 |
+ |
|
392 |
+ bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00); |
|
393 |
+ fill_rectangle(screen, |
|
394 |
+ s->xleft, s->ytop, s->width, s->height, |
|
395 |
+ bgcolor); |
|
396 |
+ |
|
397 |
+ fgcolor = SDL_MapRGB(screen->format, 0xff, 0xff, 0xff); |
|
398 |
+ |
|
399 |
+ /* total height for one channel */ |
|
400 |
+ h = s->height / nb_display_channels; |
|
401 |
+ /* graph height / 2 */ |
|
402 |
+ h2 = (h * 9) / 20; |
|
403 |
+ for(ch = 0;ch < nb_display_channels; ch++) { |
|
404 |
+ i = i_start + ch; |
|
405 |
+ y1 = s->ytop + ch * h + (h / 2); /* position of center line */ |
|
406 |
+ for(x = 0; x < s->width; x++) { |
|
407 |
+ y = (s->sample_array[i] * h2) >> 15; |
|
408 |
+ if (y < 0) { |
|
409 |
+ y = -y; |
|
410 |
+ ys = y1 - y; |
|
411 |
+ } else { |
|
412 |
+ ys = y1; |
|
413 |
+ } |
|
414 |
+ fill_rectangle(screen, |
|
415 |
+ s->xleft + x, ys, 1, y, |
|
416 |
+ fgcolor); |
|
417 |
+ i += channels; |
|
418 |
+ if (i >= SAMPLE_ARRAY_SIZE) |
|
419 |
+ i -= SAMPLE_ARRAY_SIZE; |
|
420 |
+ } |
|
421 |
+ } |
|
422 |
+ |
|
423 |
+ fgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0xff); |
|
424 |
+ |
|
425 |
+ for(ch = 1;ch < nb_display_channels; ch++) { |
|
426 |
+ y = s->ytop + ch * h; |
|
427 |
+ fill_rectangle(screen, |
|
428 |
+ s->xleft, y, s->width, 1, |
|
429 |
+ fgcolor); |
|
430 |
+ } |
|
431 |
+ SDL_UpdateRect(screen, s->xleft, s->ytop, s->width, s->height); |
|
432 |
+} |
|
433 |
+ |
|
434 |
+/* display the current picture, if any */ |
|
435 |
+static void video_display(VideoState *is) |
|
436 |
+{ |
|
437 |
+ if (is->audio_st && is->show_audio) |
|
438 |
+ video_audio_display(is); |
|
439 |
+ else if (is->video_st) |
|
440 |
+ video_image_display(is); |
|
441 |
+} |
|
442 |
+ |
|
443 |
+static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) |
|
444 |
+{ |
|
445 |
+ SDL_Event event; |
|
446 |
+ event.type = FF_REFRESH_EVENT; |
|
447 |
+ event.user.data1 = opaque; |
|
448 |
+ SDL_PushEvent(&event); |
|
449 |
+ return 0; /* 0 means stop timer */ |
|
450 |
+} |
|
451 |
+ |
|
452 |
+/* schedule a video refresh in 'delay' ms */ |
|
453 |
+static void schedule_refresh(VideoState *is, int delay) |
|
454 |
+{ |
|
455 |
+ SDL_AddTimer(delay, sdl_refresh_timer_cb, is); |
|
456 |
+} |
|
457 |
+ |
|
458 |
+/* called to display each frame */ |
|
459 |
+static void video_refresh_timer(void *opaque) |
|
460 |
+{ |
|
461 |
+ VideoState *is = opaque; |
|
462 |
+ VideoPicture *vp; |
|
463 |
+ |
|
464 |
+ if (is->video_st) { |
|
465 |
+ if (is->pictq_size == 0) { |
|
466 |
+ /* if no picture, need to wait */ |
|
467 |
+ schedule_refresh(is, 40); |
|
468 |
+ } else { |
|
469 |
+ vp = &is->pictq[is->pictq_rindex]; |
|
470 |
+ |
|
471 |
+ /* launch timer for next picture */ |
|
472 |
+ schedule_refresh(is, vp->delay); |
|
473 |
+ |
|
474 |
+ /* display picture */ |
|
475 |
+ video_display(is); |
|
476 |
+ |
|
477 |
+ /* update queue size and signal for next picture */ |
|
478 |
+ if (++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) |
|
479 |
+ is->pictq_rindex = 0; |
|
480 |
+ |
|
481 |
+ SDL_LockMutex(is->pictq_mutex); |
|
482 |
+ is->pictq_size--; |
|
483 |
+ SDL_CondSignal(is->pictq_cond); |
|
484 |
+ SDL_UnlockMutex(is->pictq_mutex); |
|
485 |
+ } |
|
486 |
+ } else if (is->audio_st) { |
|
487 |
+ /* draw the next audio frame */ |
|
488 |
+ |
|
489 |
+ schedule_refresh(is, 40); |
|
490 |
+ |
|
491 |
+ /* if only audio stream, then display the audio bars (better |
|
492 |
+ than nothing, just to test the implementation */ |
|
493 |
+ |
|
494 |
+ /* display picture */ |
|
495 |
+ video_display(is); |
|
496 |
+ } else { |
|
497 |
+ schedule_refresh(is, 100); |
|
498 |
+ } |
|
499 |
+ if (show_status) { |
|
500 |
+ static int64_t last_time; |
|
501 |
+ int64_t cur_time; |
|
502 |
+ int aqsize, vqsize; |
|
503 |
+ |
|
504 |
+ cur_time = av_gettime(); |
|
505 |
+ if (!last_time || (cur_time - last_time) >= 500 * 1000) { |
|
506 |
+ aqsize = 0; |
|
507 |
+ vqsize = 0; |
|
508 |
+ if (is->audio_st) |
|
509 |
+ aqsize = is->audioq.size; |
|
510 |
+ if (is->video_st) |
|
511 |
+ vqsize = is->videoq.size; |
|
512 |
+ printf("A:%7.2f V:%7.2f aq=%5dKB vq=%5dKB \r", |
|
513 |
+ is->audio_clock, is->video_clock, aqsize / 1024, vqsize / 1024); |
|
514 |
+ fflush(stdout); |
|
515 |
+ last_time = cur_time; |
|
516 |
+ } |
|
517 |
+ } |
|
518 |
+} |
|
519 |
+ |
|
520 |
+/* allocate a picture (needs to do that in main thread to avoid |
|
521 |
+ potential locking problems */ |
|
522 |
+static void alloc_picture(void *opaque) |
|
523 |
+{ |
|
524 |
+ VideoState *is = opaque; |
|
525 |
+ VideoPicture *vp; |
|
526 |
+ int is_yuv; |
|
527 |
+ |
|
528 |
+ vp = &is->pictq[is->pictq_windex]; |
|
529 |
+ |
|
530 |
+ if (vp->bmp) |
|
531 |
+ SDL_FreeYUVOverlay(vp->bmp); |
|
532 |
+ |
|
533 |
+ /* XXX: use generic function */ |
|
534 |
+ switch(is->video_st->codec.pix_fmt) { |
|
535 |
+ case PIX_FMT_YUV420P: |
|
536 |
+ case PIX_FMT_YUV422P: |
|
537 |
+ case PIX_FMT_YUV444P: |
|
538 |
+ case PIX_FMT_YUV422: |
|
539 |
+ case PIX_FMT_YUV410P: |
|
540 |
+ case PIX_FMT_YUV411P: |
|
541 |
+ is_yuv = 1; |
|
542 |
+ break; |
|
543 |
+ default: |
|
544 |
+ is_yuv = 0; |
|
545 |
+ break; |
|
546 |
+ } |
|
547 |
+ |
|
548 |
+ if (is_yuv) { |
|
549 |
+ vp->bmp = SDL_CreateYUVOverlay(is->video_st->codec.width, |
|
550 |
+ is->video_st->codec.height, |
|
551 |
+ SDL_YV12_OVERLAY, |
|
552 |
+ screen); |
|
553 |
+ } else { |
|
554 |
+#if 0 |
|
555 |
+ vp->bmp = bmp_alloc(screen, |
|
556 |
+ is->video_st->codec.width, |
|
557 |
+ is->video_st->codec.height, |
|
558 |
+ screen->bitmap_format, |
|
559 |
+ 0); |
|
560 |
+#endif |
|
561 |
+ vp->bmp = NULL; |
|
562 |
+ } |
|
563 |
+ vp->width = is->video_st->codec.width; |
|
564 |
+ vp->height = is->video_st->codec.height; |
|
565 |
+ |
|
566 |
+ SDL_LockMutex(is->pictq_mutex); |
|
567 |
+ vp->allocated = 1; |
|
568 |
+ SDL_CondSignal(is->pictq_cond); |
|
569 |
+ SDL_UnlockMutex(is->pictq_mutex); |
|
570 |
+} |
|
571 |
+ |
|
572 |
+#define VIDEO_CORRECTION_THRESHOLD 0.2 |
|
573 |
+ |
|
574 |
+static int output_picture(VideoState *is, AVPicture *src_pict, double pts) |
|
575 |
+{ |
|
576 |
+ VideoPicture *vp; |
|
577 |
+ int dst_pix_fmt; |
|
578 |
+ AVPicture pict; |
|
579 |
+ double delay, ref_clock, diff; |
|
580 |
+ |
|
581 |
+ /* wait until we have space to put a new picture */ |
|
582 |
+ SDL_LockMutex(is->pictq_mutex); |
|
583 |
+ while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && |
|
584 |
+ !is->videoq.abort_request) { |
|
585 |
+ SDL_CondWait(is->pictq_cond, is->pictq_mutex); |
|
586 |
+ } |
|
587 |
+ SDL_UnlockMutex(is->pictq_mutex); |
|
588 |
+ |
|
589 |
+ if (is->videoq.abort_request) |
|
590 |
+ return -1; |
|
591 |
+ |
|
592 |
+ vp = &is->pictq[is->pictq_windex]; |
|
593 |
+ |
|
594 |
+ /* alloc or resize hardware picture buffer */ |
|
595 |
+ if (!vp->bmp || |
|
596 |
+ vp->width != is->video_st->codec.width || |
|
597 |
+ vp->height != is->video_st->codec.height) { |
|
598 |
+ SDL_Event event; |
|
599 |
+ |
|
600 |
+ vp->allocated = 0; |
|
601 |
+ |
|
602 |
+ /* the allocation must be done in the main thread to avoid |
|
603 |
+ locking problems */ |
|
604 |
+ event.type = FF_ALLOC_EVENT; |
|
605 |
+ event.user.data1 = is; |
|
606 |
+ SDL_PushEvent(&event); |
|
607 |
+ |
|
608 |
+ /* wait until the picture is allocated */ |
|
609 |
+ SDL_LockMutex(is->pictq_mutex); |
|
610 |
+ while (!vp->allocated && !is->videoq.abort_request) { |
|
611 |
+ SDL_CondWait(is->pictq_cond, is->pictq_mutex); |
|
612 |
+ } |
|
613 |
+ SDL_UnlockMutex(is->pictq_mutex); |
|
614 |
+ |
|
615 |
+ if (is->videoq.abort_request) |
|
616 |
+ return -1; |
|
617 |
+ } |
|
618 |
+ |
|
619 |
+ if (vp->bmp) { |
|
620 |
+ /* get a pointer on the bitmap */ |
|
621 |
+ SDL_LockYUVOverlay (vp->bmp); |
|
622 |
+ |
|
623 |
+ dst_pix_fmt = PIX_FMT_YUV420P; |
|
624 |
+ pict.data[0] = vp->bmp->pixels[0]; |
|
625 |
+ pict.data[1] = vp->bmp->pixels[2]; |
|
626 |
+ pict.data[2] = vp->bmp->pixels[1]; |
|
627 |
+ |
|
628 |
+ pict.linesize[0] = vp->bmp->pitches[0]; |
|
629 |
+ pict.linesize[1] = vp->bmp->pitches[2]; |
|
630 |
+ pict.linesize[2] = vp->bmp->pitches[1]; |
|
631 |
+ |
|
632 |
+ img_convert(&pict, dst_pix_fmt, |
|
633 |
+ src_pict, is->video_st->codec.pix_fmt, |
|
634 |
+ is->video_st->codec.width, is->video_st->codec.height); |
|
635 |
+ /* update the bitmap content */ |
|
636 |
+ SDL_UnlockYUVOverlay(vp->bmp); |
|
637 |
+ |
|
638 |
+ /* compute delay for the next frame and take into account the |
|
639 |
+ pts if needed to make a correction. Since we do not support |
|
640 |
+ correct MPEG B frame PTS, we put a high threshold */ |
|
641 |
+ |
|
642 |
+ if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) { |
|
643 |
+ ref_clock = is->video_clock; |
|
644 |
+ } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) { |
|
645 |
+ /* cannot use audio master if no audio, so fall back to no sync */ |
|
646 |
+ if (!is->audio_st) |
|
647 |
+ ref_clock = is->video_clock; |
|
648 |
+ else |
|
649 |
+ ref_clock = is->audio_clock; |
|
650 |
+ } else { |
|
651 |
+ ref_clock = is->external_clock; |
|
652 |
+ } |
|
653 |
+ diff = is->video_clock - ref_clock; |
|
654 |
+ delay = (double)is->video_st->codec.frame_rate_base / |
|
655 |
+ (double)is->video_st->codec.frame_rate; |
|
656 |
+ if (fabs(diff) > VIDEO_CORRECTION_THRESHOLD) { |
|
657 |
+ /* if too big difference, then we adjust */ |
|
658 |
+ delay += diff; |
|
659 |
+ /* compute the difference */ |
|
660 |
+ if (delay < 0.01) |
|
661 |
+ delay = 0.01; |
|
662 |
+ else if (delay > 1.0) |
|
663 |
+ delay = 1.0; |
|
664 |
+ } |
|
665 |
+ vp->delay = (int)(delay * 1000 + 0.5); |
|
666 |
+ |
|
667 |
+ /* now we can update the picture count */ |
|
668 |
+ if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) |
|
669 |
+ is->pictq_windex = 0; |
|
670 |
+ SDL_LockMutex(is->pictq_mutex); |
|
671 |
+ is->pictq_size++; |
|
672 |
+ SDL_UnlockMutex(is->pictq_mutex); |
|
673 |
+ } |
|
674 |
+ |
|
675 |
+ /* update video clock */ |
|
676 |
+ if (pts != 0) { |
|
677 |
+ is->video_clock = pts; |
|
678 |
+ } else { |
|
679 |
+ is->video_clock += (double)is->video_st->codec.frame_rate_base / |
|
680 |
+ (double)is->video_st->codec.frame_rate; |
|
681 |
+ } |
|
682 |
+ return 0; |
|
683 |
+} |
|
684 |
+ |
|
685 |
+static int video_thread(void *arg) |
|
686 |
+{ |
|
687 |
+ VideoState *is = arg; |
|
688 |
+ AVPacket pkt1, *pkt = &pkt1; |
|
689 |
+ unsigned char *ptr; |
|
690 |
+ int len, len1, got_picture, i; |
|
691 |
+ AVFrame frame; |
|
692 |
+ AVPicture pict; |
|
693 |
+ int64_t ipts; |
|
694 |
+ double pts; |
|
695 |
+ |
|
696 |
+ for(;;) { |
|
697 |
+ while (is->paused && !is->videoq.abort_request) { |
|
698 |
+ SDL_Delay(10); |
|
699 |
+ } |
|
700 |
+ if (packet_queue_get(&is->videoq, pkt, 1) < 0) |
|
701 |
+ break; |
|
702 |
+ ipts = pkt->pts; |
|
703 |
+ ptr = pkt->data; |
|
704 |
+ if (is->video_st->codec.codec_id == CODEC_ID_RAWVIDEO) { |
|
705 |
+ avpicture_fill(&pict, ptr, |
|
706 |
+ is->video_st->codec.pix_fmt, |
|
707 |
+ is->video_st->codec.width, |
|
708 |
+ is->video_st->codec.height); |
|
709 |
+ pts = 0; |
|
710 |
+ if (ipts != AV_NOPTS_VALUE) |
|
711 |
+ pts = (double)ipts * is->ic->pts_num / is->ic->pts_den; |
|
712 |
+ if (output_picture(is, &pict, pts) < 0) |
|
713 |
+ goto the_end; |
|
714 |
+ } else { |
|
715 |
+ len = pkt->size; |
|
716 |
+ while (len > 0) { |
|
717 |
+ len1 = avcodec_decode_video(&is->video_st->codec, |
|
718 |
+ &frame, &got_picture, ptr, len); |
|
719 |
+ if (len1 < 0) |
|
720 |
+ break; |
|
721 |
+ if (got_picture) { |
|
722 |
+ for(i=0;i<4;i++) { |
|
723 |
+ pict.data[i] = frame.data[i]; |
|
724 |
+ pict.linesize[i] = frame.linesize[i]; |
|
725 |
+ } |
|
726 |
+ pts = 0; |
|
727 |
+ if (ipts != AV_NOPTS_VALUE) |
|
728 |
+ pts = (double)ipts * is->ic->pts_num / is->ic->pts_den; |
|
729 |
+ ipts = AV_NOPTS_VALUE; |
|
730 |
+ if (output_picture(is, &pict, pts) < 0) |
|
731 |
+ goto the_end; |
|
732 |
+ } |
|
733 |
+ ptr += len1; |
|
734 |
+ len -= len1; |
|
735 |
+ } |
|
736 |
+ } |
|
737 |
+ av_free_packet(pkt); |
|
738 |
+ } |
|
739 |
+ the_end: |
|
740 |
+ return 0; |
|
741 |
+} |
|
742 |
+ |
|
743 |
+/* copy samples for viewing in editor window */ |
|
744 |
+static void update_sample_display(VideoState *is, short *samples, int samples_size) |
|
745 |
+{ |
|
746 |
+ int size, len, channels; |
|
747 |
+ |
|
748 |
+ channels = is->audio_st->codec.channels; |
|
749 |
+ |
|
750 |
+ size = samples_size / sizeof(short); |
|
751 |
+ while (size > 0) { |
|
752 |
+ len = SAMPLE_ARRAY_SIZE - is->sample_array_index; |
|
753 |
+ if (len > size) |
|
754 |
+ len = size; |
|
755 |
+ memcpy(is->sample_array + is->sample_array_index, samples, len * sizeof(short)); |
|
756 |
+ samples += len; |
|
757 |
+ is->sample_array_index += len; |
|
758 |
+ if (is->sample_array_index >= SAMPLE_ARRAY_SIZE) |
|
759 |
+ is->sample_array_index = 0; |
|
760 |
+ size -= len; |
|
761 |
+ } |
|
762 |
+} |
|
763 |
+ |
|
764 |
+/* maximum audio speed change to get correct sync */ |
|
765 |
+#define SAMPLE_CORRECTION_PERCENT_MAX 2 |
|
766 |
+ |
|
767 |
+/* return the new audio buffer size (samples can be added or deleted |
|
768 |
+ to get better sync if video or external master clock) */ |
|
769 |
+static int synchronize_audio(VideoState *is, short *samples, |
|
770 |
+ int samples_size, double pts) |
|
771 |
+{ |
|
772 |
+ int n, delay; |
|
773 |
+ double ref_clock; |
|
774 |
+ |
|
775 |
+ n = 2 * is->audio_st->codec.channels; |
|
776 |
+ |
|
777 |
+ if (is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK) |
|
778 |
+ ref_clock = is->external_clock; |
|
779 |
+ else if (is->av_sync_type == AV_SYNC_VIDEO_MASTER && is->video_st) |
|
780 |
+ ref_clock = is->video_clock; |
|
781 |
+ else |
|
782 |
+ ref_clock = is->audio_clock; |
|
783 |
+ |
|
784 |
+ /* if not master, then we try to remove or add samples to correct the clock */ |
|
785 |
+ |
|
786 |
+ if (((is->av_sync_type == AV_SYNC_VIDEO_MASTER && is->video_st) || |
|
787 |
+ is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK) && pts != 0) { |
|
788 |
+ double diff; |
|
789 |
+ int wanted_size, min_size, max_size, nb_samples; |
|
790 |
+ delay = audio_write_get_buf_size(is); |
|
791 |
+ diff = pts - (double)delay / (double)(n * is->audio_st->codec.sample_rate) - ref_clock; |
|
792 |
+ wanted_size = (int)(diff * is->audio_st->codec.sample_rate) * n; |
|
793 |
+ nb_samples = samples_size / n; |
|
794 |
+ |
|
795 |
+ min_size = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX)) / 100) * n; |
|
796 |
+ max_size = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX)) / 100) * n; |
|
797 |
+ if (wanted_size < min_size) |
|
798 |
+ wanted_size = min_size; |
|
799 |
+ else if (wanted_size > max_size) |
|
800 |
+ wanted_size = max_size; |
|
801 |
+ |
|
802 |
+ /* do the correct */ |
|
803 |
+ /* XXX: do it better with sample interpolation */ |
|
804 |
+ if (wanted_size < samples_size) { |
|
805 |
+ /* remove samples */ |
|
806 |
+ samples_size = wanted_size; |
|
807 |
+ } else if (wanted_size > samples_size) { |
|
808 |
+ uint8_t *samples_end, *q; |
|
809 |
+ int nb; |
|
810 |
+ |
|
811 |
+ /* add samples */ |
|
812 |
+ nb = (samples_size - wanted_size); |
|
813 |
+ samples_end = (uint8_t *)samples + samples_size - n; |
|
814 |
+ q = samples_end + n; |
|
815 |
+ while (nb > 0) { |
|
816 |
+ memcpy(q, samples_end, n); |
|
817 |
+ q += n; |
|
818 |
+ nb -= n; |
|
819 |
+ } |
|
820 |
+ samples_size = wanted_size; |
|
821 |
+ } |
|
822 |
+ } |
|
823 |
+ |
|
824 |
+ /* update audio clock */ |
|
825 |
+ if (is->av_sync_type == AV_SYNC_AUDIO_MASTER && pts != 0) { |
|
826 |
+ /* a pts is given: we update the audio clock precisely */ |
|
827 |
+ delay = audio_write_get_buf_size(is); |
|
828 |
+ is->audio_clock = pts - (double)delay / (double)(n * is->audio_st->codec.sample_rate); |
|
829 |
+ } else { |
|
830 |
+ is->audio_clock += (double)samples_size / (double)(n * is->audio_st->codec.sample_rate); |
|
831 |
+ } |
|
832 |
+ return samples_size; |
|
833 |
+} |
|
834 |
+ |
|
835 |
+/* decode one audio frame and returns its uncompressed size */ |
|
836 |
+static int audio_decode_frame(VideoState *is, uint8_t *audio_buf, double *pts_ptr) |
|
837 |
+{ |
|
838 |
+ AVPacket *pkt = &is->audio_pkt; |
|
839 |
+ int len1, data_size; |
|
840 |
+ double pts; |
|
841 |
+ |
|
842 |
+ for(;;) { |
|
843 |
+ if (is->paused || is->audioq.abort_request) { |
|
844 |
+ return -1; |
|
845 |
+ } |
|
846 |
+ while (is->audio_pkt_size > 0) { |
|
847 |
+ len1 = avcodec_decode_audio(&is->audio_st->codec, |
|
848 |
+ (int16_t *)audio_buf, &data_size, |
|
849 |
+ is->audio_pkt_data, is->audio_pkt_size); |
|
850 |
+ if (len1 < 0) |
|
851 |
+ break; |
|
852 |
+ is->audio_pkt_data += len1; |
|
853 |
+ is->audio_pkt_size -= len1; |
|
854 |
+ if (data_size > 0) { |
|
855 |
+ pts = 0; |
|
856 |
+ if (is->audio_pkt_ipts != AV_NOPTS_VALUE) |
|
857 |
+ pts = (double)is->audio_pkt_ipts * is->ic->pts_num / is->ic->pts_den; |
|
858 |
+ *pts_ptr = pts; |
|
859 |
+ is->audio_pkt_ipts = AV_NOPTS_VALUE; |
|
860 |
+ /* we got samples : we can exit now */ |
|
861 |
+ return data_size; |
|
862 |
+ } |
|
863 |
+ } |
|
864 |
+ |
|
865 |
+ /* free previous packet if any */ |
|
866 |
+ if (pkt->destruct) |
|
867 |
+ av_free_packet(pkt); |
|
868 |
+ |
|
869 |
+ /* read next packet */ |
|
870 |
+ if (packet_queue_get(&is->audioq, pkt, 1) < 0) |
|
871 |
+ return -1; |
|
872 |
+ is->audio_pkt_data = pkt->data; |
|
873 |
+ is->audio_pkt_size = pkt->size; |
|
874 |
+ is->audio_pkt_ipts = pkt->pts; |
|
875 |
+ } |
|
876 |
+} |
|
877 |
+ |
|
878 |
+int audio_write_get_buf_size(VideoState *is) |
|
879 |
+{ |
|
880 |
+ int delay; |
|
881 |
+ delay = is->audio_hw_buf_size; |
|
882 |
+#if 0 |
|
883 |
+ /* just a test to check if the estimated delay is OK */ |
|
884 |
+ { |
|
885 |
+ int val; |
|
886 |
+ if (ioctl(sdl_audio_fd, SNDCTL_DSP_GETODELAY, &val) < 0) |
|
887 |
+ perror("SNDCTL_DSP_GETODELAY"); |
|
888 |
+ printf("real_delay=%d delay=%d\n", val, delay); |
|
889 |
+ } |
|
890 |
+#endif |
|
891 |
+ return delay; |
|
892 |
+} |
|
893 |
+ |
|
894 |
+ |
|
895 |
+/* prepare a new audio buffer */ |
|
896 |
+void sdl_audio_callback(void *opaque, Uint8 *stream, int len) |
|
897 |
+{ |
|
898 |
+ VideoState *is = opaque; |
|
899 |
+ int audio_size, len1; |
|
900 |
+ double pts; |
|
901 |
+ |
|
902 |
+ audio_callback_time = av_gettime(); |
|
903 |
+ |
|
904 |
+ while (len > 0) { |
|
905 |
+ if (is->audio_buf_index >= is->audio_buf_size) { |
|
906 |
+ audio_size = audio_decode_frame(is, is->audio_buf, &pts); |
|
907 |
+ if (audio_size < 0) { |
|
908 |
+ /* if error, just output silence */ |
|
909 |
+ is->audio_buf_size = 1024; |
|
910 |
+ memset(is->audio_buf, 0, is->audio_buf_size); |
|
911 |
+ } else { |
|
912 |
+ if (is->show_audio) |
|
913 |
+ update_sample_display(is, (int16_t *)is->audio_buf, audio_size); |
|
914 |
+ audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, audio_size, |
|
915 |
+ pts); |
|
916 |
+ is->audio_buf_size = audio_size; |
|
917 |
+ } |
|
918 |
+ is->audio_buf_index = 0; |
|
919 |
+ } |
|
920 |
+ len1 = is->audio_buf_size - is->audio_buf_index; |
|
921 |
+ if (len1 > len) |
|
922 |
+ len1 = len; |
|
923 |
+ memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1); |
|
924 |
+ len -= len1; |
|
925 |
+ stream += len1; |
|
926 |
+ is->audio_buf_index += len1; |
|
927 |
+ } |
|
928 |
+} |
|
929 |
+ |
|
930 |
+ |
|
931 |
+/* open a given stream. Return 0 if OK */ |
|
932 |
+static int stream_component_open(VideoState *is, int stream_index) |
|
933 |
+{ |
|
934 |
+ AVFormatContext *ic = is->ic; |
|
935 |
+ AVCodecContext *enc; |
|
936 |
+ AVCodec *codec; |
|
937 |
+ SDL_AudioSpec wanted_spec, spec; |
|
938 |
+ |
|
939 |
+ if (stream_index < 0 || stream_index >= ic->nb_streams) |
|
940 |
+ return -1; |
|
941 |
+ enc = &ic->streams[stream_index]->codec; |
|
942 |
+ |
|
943 |
+ |
|
944 |
+ /* prepare audio output */ |
|
945 |
+ if (enc->codec_type == CODEC_TYPE_AUDIO) { |
|
946 |
+ wanted_spec.freq = enc->sample_rate; |
|
947 |
+ wanted_spec.format = AUDIO_S16SYS; |
|
948 |
+ wanted_spec.channels = enc->channels; |
|
949 |
+ wanted_spec.silence = 0; |
|
950 |
+ wanted_spec.samples = 8192; |
|
951 |
+ wanted_spec.callback = sdl_audio_callback; |
|
952 |
+ wanted_spec.userdata = is; |
|
953 |
+ if (SDL_OpenAudio(&wanted_spec, &spec) < 0) |
|
954 |
+ return -1; |
|
955 |
+ is->audio_hw_buf_size = spec.size; |
|
956 |
+ } |
|
957 |
+ |
|
958 |
+ codec = avcodec_find_decoder(enc->codec_id); |
|
959 |
+ if (!codec || |
|
960 |
+ avcodec_open(enc, codec) < 0) |
|
961 |
+ return -1; |
|
962 |
+ switch(enc->codec_type) { |
|
963 |
+ case CODEC_TYPE_AUDIO: |
|
964 |
+ is->audio_stream = stream_index; |
|
965 |
+ is->audio_st = ic->streams[stream_index]; |
|
966 |
+ is->audio_buf_size = 0; |
|
967 |
+ is->audio_buf_index = 0; |
|
968 |
+ is->audio_pkt_size = 0; |
|
969 |
+ memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); |
|
970 |
+ packet_queue_init(&is->audioq); |
|
971 |
+ SDL_PauseAudio(0); |
|
972 |
+ break; |
|
973 |
+ case CODEC_TYPE_VIDEO: |
|
974 |
+ is->video_stream = stream_index; |
|
975 |
+ is->video_st = ic->streams[stream_index]; |
|
976 |
+ |
|
977 |
+ packet_queue_init(&is->videoq); |
|
978 |
+ is->video_tid = SDL_CreateThread(video_thread, is); |
|
979 |
+ break; |
|
980 |
+ default: |
|
981 |
+ break; |
|
982 |
+ } |
|
983 |
+ return 0; |
|
984 |
+} |
|
985 |
+ |
|
986 |
+static void stream_component_close(VideoState *is, int stream_index) |
|
987 |
+{ |
|
988 |
+ AVFormatContext *ic = is->ic; |
|
989 |
+ AVCodecContext *enc; |
|
990 |
+ |
|
991 |
+ enc = &ic->streams[stream_index]->codec; |
|
992 |
+ |
|
993 |
+ switch(enc->codec_type) { |
|
994 |
+ case CODEC_TYPE_AUDIO: |
|
995 |
+ packet_queue_abort(&is->audioq); |
|
996 |
+ |
|
997 |
+ SDL_CloseAudio(); |
|
998 |
+ |
|
999 |
+ packet_queue_end(&is->audioq); |
|
1000 |
+ break; |
|
1001 |
+ case CODEC_TYPE_VIDEO: |
|
1002 |
+ packet_queue_abort(&is->videoq); |
|
1003 |
+ |
|
1004 |
+ /* note: we also signal this mutex to make sure we deblock the |
|
1005 |
+ video thread in all cases */ |
|
1006 |
+ SDL_LockMutex(is->pictq_mutex); |
|
1007 |
+ SDL_CondSignal(is->pictq_cond); |
|
1008 |
+ SDL_UnlockMutex(is->pictq_mutex); |
|
1009 |
+ |
|
1010 |
+ SDL_WaitThread(is->video_tid, NULL); |
|
1011 |
+ |
|
1012 |
+ packet_queue_end(&is->videoq); |
|
1013 |
+ break; |
|
1014 |
+ default: |
|
1015 |
+ break; |
|
1016 |
+ } |
|
1017 |
+ |
|
1018 |
+ avcodec_close(enc); |
|
1019 |
+ switch(enc->codec_type) { |
|
1020 |
+ case CODEC_TYPE_AUDIO: |
|
1021 |
+ is->audio_st = NULL; |
|
1022 |
+ is->audio_stream = -1; |
|
1023 |
+ break; |
|
1024 |
+ case CODEC_TYPE_VIDEO: |
|
1025 |
+ is->video_st = NULL; |
|
1026 |
+ is->video_stream = -1; |
|
1027 |
+ break; |
|
1028 |
+ default: |
|
1029 |
+ break; |
|
1030 |
+ } |
|
1031 |
+} |
|
1032 |
+ |
|
1033 |
+ |
|
1034 |
+/* this thread gets the stream from the disk or the network */ |
|
1035 |
+static int decode_thread(void *arg) |
|
1036 |
+{ |
|
1037 |
+ VideoState *is = arg; |
|
1038 |
+ AVFormatContext *ic; |
|
1039 |
+ int err, i, ret, video_index, audio_index; |
|
1040 |
+ AVPacket pkt1, *pkt = &pkt1; |
|
1041 |
+ |
|
1042 |
+ video_index = -1; |
|
1043 |
+ audio_index = -1; |
|
1044 |
+ is->video_stream = -1; |
|
1045 |
+ is->audio_stream = -1; |
|
1046 |
+ |
|
1047 |
+ err = av_open_input_file(&ic, is->filename, NULL, 0, NULL); |
|
1048 |
+ if (err < 0) |
|
1049 |
+ return 0; |
|
1050 |
+ is->ic = ic; |
|
1051 |
+ err = av_find_stream_info(ic); |
|
1052 |
+ if (err < 0) |
|
1053 |
+ goto fail; |
|
1054 |
+ |
|
1055 |
+ for(i = 0; i < ic->nb_streams; i++) { |
|
1056 |
+ AVCodecContext *enc = &ic->streams[i]->codec; |
|
1057 |
+ switch(enc->codec_type) { |
|
1058 |
+ case CODEC_TYPE_AUDIO: |
|
1059 |
+ if (audio_index < 0 && !audio_disable) |
|
1060 |
+ audio_index = i; |
|
1061 |
+ break; |
|
1062 |
+ case CODEC_TYPE_VIDEO: |
|
1063 |
+ if (video_index < 0 && !video_disable) |
|
1064 |
+ video_index = i; |
|
1065 |
+ break; |
|
1066 |
+ default: |
|
1067 |
+ break; |
|
1068 |
+ } |
|
1069 |
+ } |
|
1070 |
+ if (show_status) { |
|
1071 |
+ dump_format(ic, 0, is->filename, 0); |
|
1072 |
+ } |
|
1073 |
+ |
|
1074 |
+ /* open the streams */ |
|
1075 |
+ if (audio_index >= 0) { |
|
1076 |
+ stream_component_open(is, audio_index); |
|
1077 |
+ } |
|
1078 |
+ |
|
1079 |
+ if (video_index >= 0) { |
|
1080 |
+ stream_component_open(is, video_index); |
|
1081 |
+ } else { |
|
1082 |
+ if (!display_disable) |
|
1083 |
+ is->show_audio = 1; |
|
1084 |
+ } |
|
1085 |
+ |
|
1086 |
+ if (is->video_stream < 0 && is->audio_stream < 0) { |
|
1087 |
+ goto fail; |
|
1088 |
+ } |
|
1089 |
+ |
|
1090 |
+ for(;;) { |
|
1091 |
+ if (is->abort_request) |
|
1092 |
+ break; |
|
1093 |
+ /* if the queue are full, no need to read more */ |
|
1094 |
+ if (is->audioq.size > MAX_AUDIOQ_SIZE || |
|
1095 |
+ is->videoq.size > MAX_VIDEOQ_SIZE) { |
|
1096 |
+ /* wait 10 ms */ |
|
1097 |
+ SDL_Delay(10); |
|
1098 |
+ continue; |
|
1099 |
+ } |
|
1100 |
+ ret = av_read_packet(ic, pkt); |
|
1101 |
+ if (ret < 0) { |
|
1102 |
+ break; |
|
1103 |
+ } |
|
1104 |
+ if (pkt->stream_index == is->audio_stream) { |
|
1105 |
+ packet_queue_put(&is->audioq, pkt); |
|
1106 |
+ } else if (pkt->stream_index == is->video_stream) { |
|
1107 |
+ packet_queue_put(&is->videoq, pkt); |
|
1108 |
+ } else { |
|
1109 |
+ av_free_packet(pkt); |
|
1110 |
+ } |
|
1111 |
+ } |
|
1112 |
+ /* wait until the end */ |
|
1113 |
+ while (!is->abort_request) { |
|
1114 |
+ SDL_Delay(100); |
|
1115 |
+ } |
|
1116 |
+ |
|
1117 |
+ fail: |
|
1118 |
+ /* close each stream */ |
|
1119 |
+ if (is->audio_stream >= 0) |
|
1120 |
+ stream_component_close(is, is->audio_stream); |
|
1121 |
+ if (is->video_stream >= 0) |
|
1122 |
+ stream_component_close(is, is->video_stream); |
|
1123 |
+ |
|
1124 |
+ av_close_input_file(is->ic); |
|
1125 |
+ is->ic = NULL; /* safety */ |
|
1126 |
+ return 0; |
|
1127 |
+} |
|
1128 |
+ |
|
1129 |
+/* pause or resume the video */ |
|
1130 |
+static void stream_pause(VideoState *is) |
|
1131 |
+{ |
|
1132 |
+ is->paused = !is->paused; |
|
1133 |
+} |
|
1134 |
+ |
|
1135 |
+static VideoState *stream_open(const char *filename) |
|
1136 |
+{ |
|
1137 |
+ VideoState *is; |
|
1138 |
+ |
|
1139 |
+ is = av_mallocz(sizeof(VideoState)); |
|
1140 |
+ if (!is) |
|
1141 |
+ return NULL; |
|
1142 |
+ pstrcpy(is->filename, sizeof(is->filename), filename); |
|
1143 |
+ if (screen) { |
|
1144 |
+ is->width = screen->w; |
|
1145 |
+ is->height = screen->h; |
|
1146 |
+ } |
|
1147 |
+ is->ytop = 0; |
|
1148 |
+ is->xleft = 0; |
|
1149 |
+ |
|
1150 |
+ /* start video display */ |
|
1151 |
+ is->pictq_mutex = SDL_CreateMutex(); |
|
1152 |
+ is->pictq_cond = SDL_CreateCond(); |
|
1153 |
+ |
|
1154 |
+ /* add the refresh timer to draw the picture */ |
|
1155 |
+ schedule_refresh(is, 40); |
|
1156 |
+ |
|
1157 |
+ is->av_sync_type = AV_SYNC_AUDIO_MASTER; |
|
1158 |
+ |
|
1159 |
+ is->parse_tid = SDL_CreateThread(decode_thread, is); |
|
1160 |
+ if (!is->parse_tid) { |
|
1161 |
+ av_free(is); |
|
1162 |
+ return NULL; |
|
1163 |
+ } |
|
1164 |
+ return is; |
|
1165 |
+} |
|
1166 |
+ |
|
1167 |
+static void stream_close(VideoState *is) |
|
1168 |
+{ |
|
1169 |
+ VideoPicture *vp; |
|
1170 |
+ int i; |
|
1171 |
+ /* XXX: use a special url_shutdown call to abort parse cleanly */ |
|
1172 |
+ is->abort_request = 1; |
|
1173 |
+ SDL_WaitThread(is->parse_tid, NULL); |
|
1174 |
+ |
|
1175 |
+ /* free all pictures */ |
|
1176 |
+ for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE; i++) { |
|
1177 |
+ vp = &is->pictq[i]; |
|
1178 |
+ if (vp->bmp) { |
|
1179 |
+ SDL_FreeYUVOverlay(vp->bmp); |
|
1180 |
+ vp->bmp = NULL; |
|
1181 |
+ } |
|
1182 |
+ } |
|
1183 |
+ SDL_DestroyMutex(is->pictq_mutex); |
|
1184 |
+ SDL_DestroyCond(is->pictq_cond); |
|
1185 |
+} |
|
1186 |
+ |
|
1187 |
+void toggle_full_screen(void) |
|
1188 |
+{ |
|
1189 |
+ int w, h, flags; |
|
1190 |
+ is_full_screen = !is_full_screen; |
|
1191 |
+ if (!fs_screen_width) { |
|
1192 |
+ /* use default SDL method */ |
|
1193 |
+ SDL_WM_ToggleFullScreen(screen); |
|
1194 |
+ } else { |
|
1195 |
+ /* use the recorded resolution */ |
|
1196 |
+ flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL; |
|
1197 |
+ if (is_full_screen) { |
|
1198 |
+ w = fs_screen_width; |
|
1199 |
+ h = fs_screen_height; |
|
1200 |
+ flags |= SDL_FULLSCREEN; |
|
1201 |
+ } else { |
|
1202 |
+ w = screen_width; |
|
1203 |
+ h = screen_height; |
|
1204 |
+ flags |= SDL_RESIZABLE; |
|
1205 |
+ } |
|
1206 |
+ screen = SDL_SetVideoMode(w, h, 0, flags); |
|
1207 |
+ cur_stream->width = w; |
|
1208 |
+ cur_stream->height = h; |
|
1209 |
+ } |
|
1210 |
+} |
|
1211 |
+ |
|
1212 |
+void toggle_pause(void) |
|
1213 |
+{ |
|
1214 |
+ if (cur_stream) |
|
1215 |
+ stream_pause(cur_stream); |
|
1216 |
+} |
|
1217 |
+ |
|
1218 |
+void do_exit(void) |
|
1219 |
+{ |
|
1220 |
+ if (cur_stream) { |
|
1221 |
+ stream_close(cur_stream); |
|
1222 |
+ cur_stream = NULL; |
|
1223 |
+ } |
|
1224 |
+ if (show_status) |
|
1225 |
+ printf("\n"); |
|
1226 |
+ SDL_Quit(); |
|
1227 |
+ exit(0); |
|
1228 |
+} |
|
1229 |
+ |
|
1230 |
+void toggle_audio_display(void) |
|
1231 |
+{ |
|
1232 |
+ if (cur_stream) { |
|
1233 |
+ cur_stream->show_audio = !cur_stream->show_audio; |
|
1234 |
+ } |
|
1235 |
+} |
|
1236 |
+ |
|
1237 |
+/* handle an event sent by the GUI */ |
|
1238 |
+void event_loop(void) |
|
1239 |
+{ |
|
1240 |
+ SDL_Event event; |
|
1241 |
+ |
|
1242 |
+ for(;;) { |
|
1243 |
+ SDL_WaitEvent(&event); |
|
1244 |
+ switch(event.type) { |
|
1245 |
+ case SDL_KEYDOWN: |
|
1246 |
+ switch(event.key.keysym.sym) { |
|
1247 |
+ case SDLK_ESCAPE: |
|
1248 |
+ case SDLK_q: |
|
1249 |
+ do_exit(); |
|
1250 |
+ break; |
|
1251 |
+ case SDLK_f: |
|
1252 |
+ toggle_full_screen(); |
|
1253 |
+ break; |
|
1254 |
+ case SDLK_p: |
|
1255 |
+ case SDLK_SPACE: |
|
1256 |
+ toggle_pause(); |
|
1257 |
+ break; |
|
1258 |
+ case SDLK_a: |
|
1259 |
+ toggle_audio_display(); |
|
1260 |
+ break; |
|
1261 |
+ default: |
|
1262 |
+ break; |
|
1263 |
+ } |
|
1264 |
+ break; |
|
1265 |
+ case SDL_VIDEORESIZE: |
|
1266 |
+ if (cur_stream) { |
|
1267 |
+ screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 0, |
|
1268 |
+ SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL); |
|
1269 |
+ cur_stream->width = event.resize.w; |
|
1270 |
+ cur_stream->height = event.resize.h; |
|
1271 |
+ } |
|
1272 |
+ break; |
|
1273 |
+ case SDL_QUIT: |
|
1274 |
+ do_exit(); |
|
1275 |
+ break; |
|
1276 |
+ case FF_ALLOC_EVENT: |
|
1277 |
+ alloc_picture(event.user.data1); |
|
1278 |
+ break; |
|
1279 |
+ case FF_REFRESH_EVENT: |
|
1280 |
+ video_refresh_timer(event.user.data1); |
|
1281 |
+ break; |
|
1282 |
+ default: |
|
1283 |
+ break; |
|
1284 |
+ } |
|
1285 |
+ } |
|
1286 |
+} |
|
1287 |
+ |
|
1288 |
+void opt_width(const char *arg) |
|
1289 |
+{ |
|
1290 |
+ screen_width = atoi(arg); |
|
1291 |
+} |
|
1292 |
+ |
|
1293 |
+void opt_height(const char *arg) |
|
1294 |
+{ |
|
1295 |
+ screen_height = atoi(arg); |
|
1296 |
+} |
|
1297 |
+ |
|
1298 |
+static void opt_format(const char *arg) |
|
1299 |
+{ |
|
1300 |
+ file_iformat = av_find_input_format(arg); |
|
1301 |
+ if (!file_iformat) { |
|
1302 |
+ fprintf(stderr, "Unknown input format: %s\n", arg); |
|
1303 |
+ exit(1); |
|
1304 |
+ } |
|
1305 |
+} |
|
1306 |
+ |
|
1307 |
+const OptionDef options[] = { |
|
1308 |
+ { "h", 0, {(void*)show_help}, "show help" }, |
|
1309 |
+ { "x", HAS_ARG, {(void*)opt_width}, "force displayed width", "width" }, |
|
1310 |
+ { "y", HAS_ARG, {(void*)opt_height}, "force displayed height", "height" }, |
|
1311 |
+ { "an", OPT_BOOL, {(void*)&audio_disable}, "disable audio" }, |
|
1312 |
+ { "vn", OPT_BOOL, {(void*)&video_disable}, "disable video" }, |
|
1313 |
+ { "nodisp", OPT_BOOL, {(void*)&display_disable}, "disable graphical display" }, |
|
1314 |
+ { "f", HAS_ARG, {(void*)opt_format}, "force format", "fmt" }, |
|
1315 |
+ { "stats", OPT_BOOL | OPT_EXPERT, {(void*)&show_status}, "show status", "" }, |
|
1316 |
+ { NULL, }, |
|
1317 |
+}; |
|
1318 |
+ |
|
1319 |
+void show_help(void) |
|
1320 |
+{ |
|
1321 |
+ printf("usage: ffplay [options] input_file\n" |
|
1322 |
+ "Simple media player\n"); |
|
1323 |
+ printf("\n"); |
|
1324 |
+ show_help_options(options); |
|
1325 |
+ printf("\nWhile playing:\n" |
|
1326 |
+ "q, ESC quit\n" |
|
1327 |
+ "f toggle full screen\n" |
|
1328 |
+ "p, SPC pause\n" |
|
1329 |
+ "a show audio waves\n" |
|
1330 |
+ ); |
|
1331 |
+ exit(1); |
|
1332 |
+} |
|
1333 |
+ |
|
1334 |
+void parse_arg_file(const char *filename) |
|
1335 |
+{ |
|
1336 |
+ input_filename = filename; |
|
1337 |
+} |
|
1338 |
+ |
|
1339 |
+/* Called from the main */ |
|
1340 |
+int main(int argc, char **argv) |
|
1341 |
+{ |
|
1342 |
+ int flags; |
|
1343 |
+ |
|
1344 |
+ /* register all codecs, demux and protocols */ |
|
1345 |
+ av_register_all(); |
|
1346 |
+ |
|
1347 |
+ parse_options(argc, argv, options); |
|
1348 |
+ |
|
1349 |
+ if (!input_filename) |
|
1350 |
+ show_help(); |
|
1351 |
+ |
|
1352 |
+ if (display_disable) { |
|
1353 |
+ video_disable = 1; |
|
1354 |
+ } |
|
1355 |
+ flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_EVENTTHREAD; |
|
1356 |
+ if (SDL_Init (flags)) { |
|
1357 |
+ fprintf(stderr, "Could not initialize SDL - exiting\n"); |
|
1358 |
+ exit(1); |
|
1359 |
+ } |
|
1360 |
+ |
|
1361 |
+ if (!display_disable) { |
|
1362 |
+ screen = SDL_SetVideoMode(screen_width, screen_height, 0, |
|
1363 |
+ SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL); |
|
1364 |
+ if (!screen) { |
|
1365 |
+ fprintf(stderr, "SDL: could not set video mode - exiting\n"); |
|
1366 |
+ exit(1); |
|
1367 |
+ } |
|
1368 |
+ SDL_WM_SetCaption("FFplay", "FFplay"); |
|
1369 |
+#ifdef HAVE_X11 |
|
1370 |
+ /* save the screen resolution... SDL should allow full screen |
|
1371 |
+ by resizing the window */ |
|
1372 |
+ { |
|
1373 |
+ Display *dpy; |
|
1374 |
+ dpy = XOpenDisplay(NULL); |
|
1375 |
+ if (dpy) { |
|
1376 |
+ fs_screen_width = DisplayWidth(dpy, DefaultScreen(dpy)); |
|
1377 |
+ fs_screen_height = DisplayHeight(dpy, DefaultScreen(dpy)); |
|
1378 |
+ XCloseDisplay(dpy); |
|
1379 |
+ } |
|
1380 |
+ } |
|
1381 |
+#endif |
|
1382 |
+ } |
|
1383 |
+ |
|
1384 |
+ SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE); |
|
1385 |
+ SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); |
|
1386 |
+ SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE); |
|
1387 |
+ SDL_EventState(SDL_USEREVENT, SDL_IGNORE); |
|
1388 |
+ |
|
1389 |
+ cur_stream = stream_open(input_filename); |
|
1390 |
+ |
|
1391 |
+ event_loop(); |
|
1392 |
+ |
|
1393 |
+ /* never returns */ |
|
1394 |
+ |
|
1395 |
+ return 0; |
|
1396 |
+} |