Signed-off-by: Lukasz Marek <lukasz.m.luki@gmail.com>
Signed-off-by: Stefano Sabatini <stefasab@gmail.com>
... | ... |
@@ -2125,6 +2125,7 @@ dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid" |
2125 | 2125 |
dv1394_indev_deps="dv1394" |
2126 | 2126 |
dv1394_indev_select="dv_demuxer" |
2127 | 2127 |
fbdev_indev_deps="linux_fb_h" |
2128 |
+fbdev_outdev_deps="linux_fb_h" |
|
2128 | 2129 |
iec61883_indev_deps="libiec61883" |
2129 | 2130 |
jack_indev_deps="jack_jack_h sem_timedwait" |
2130 | 2131 |
lavfi_indev_deps="avfilter" |
... | ... |
@@ -104,6 +104,35 @@ ffmpeg -i INPUT -pix_fmt rgb24 -f caca -list_dither colors - |
104 | 104 |
@end example |
105 | 105 |
@end itemize |
106 | 106 |
|
107 |
+@section fbdev |
|
108 |
+ |
|
109 |
+Linux framebuffer output device. |
|
110 |
+ |
|
111 |
+The Linux framebuffer is a graphic hardware-independent abstraction |
|
112 |
+layer to show graphics on a computer monitor, typically on the |
|
113 |
+console. It is accessed through a file device node, usually |
|
114 |
+@file{/dev/fb0}. |
|
115 |
+ |
|
116 |
+For more detailed information read the file |
|
117 |
+@file{Documentation/fb/framebuffer.txt} included in the Linux source tree. |
|
118 |
+ |
|
119 |
+@subsection Options |
|
120 |
+@table @option |
|
121 |
+ |
|
122 |
+@item xoffset |
|
123 |
+@item yoffset |
|
124 |
+Set x/y coordinate of top left corner. Default is 0. |
|
125 |
+@end table |
|
126 |
+ |
|
127 |
+@subsection Examples |
|
128 |
+Play a file on framebuffer device @file{/dev/fb0}. |
|
129 |
+Required pixel format depends on current framebuffer settings. |
|
130 |
+@example |
|
131 |
+ffmpeg -re -i INPUT -vcodec rawvideo -pix_fmt bgra -f fbdev /dev/fb0 |
|
132 |
+@end example |
|
133 |
+ |
|
134 |
+See also @url{http://linux-fbdev.sourceforge.net/}, and fbset(1). |
|
135 |
+ |
|
107 | 136 |
@section oss |
108 | 137 |
|
109 | 138 |
OSS (Open Sound System) output device. |
... | ... |
@@ -24,6 +24,7 @@ OBJS-$(CONFIG_DSHOW_INDEV) += dshow.o dshow_enummediatypes.o \ |
24 | 24 |
dshow_pin.o dshow_common.o |
25 | 25 |
OBJS-$(CONFIG_DV1394_INDEV) += dv1394.o |
26 | 26 |
OBJS-$(CONFIG_FBDEV_INDEV) += fbdev.o |
27 |
+OBJS-$(CONFIG_FBDEV_OUTDEV) += fbdev_enc.o |
|
27 | 28 |
OBJS-$(CONFIG_IEC61883_INDEV) += iec61883.o |
28 | 29 |
OBJS-$(CONFIG_JACK_INDEV) += jack_audio.o timefilter.o |
29 | 30 |
OBJS-$(CONFIG_LAVFI_INDEV) += lavfi.o |
... | ... |
@@ -51,7 +51,7 @@ void avdevice_register_all(void) |
51 | 51 |
REGISTER_OUTDEV (CACA, caca); |
52 | 52 |
REGISTER_INDEV (DSHOW, dshow); |
53 | 53 |
REGISTER_INDEV (DV1394, dv1394); |
54 |
- REGISTER_INDEV (FBDEV, fbdev); |
|
54 |
+ REGISTER_INOUTDEV(FBDEV, fbdev); |
|
55 | 55 |
REGISTER_INDEV (IEC61883, iec61883); |
56 | 56 |
REGISTER_INDEV (JACK, jack); |
57 | 57 |
REGISTER_INDEV (LAVFI, lavfi); |
58 | 58 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,253 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2013 Lukasz Marek |
|
2 |
+ * |
|
3 |
+ * This file is part of FFmpeg. |
|
4 |
+ * |
|
5 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
6 |
+ * modify it under the terms of the GNU Lesser General Public |
|
7 |
+ * License as published by the Free Software Foundation; either |
|
8 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
9 |
+ * |
|
10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
+ * Lesser General Public License for more details. |
|
14 |
+ * |
|
15 |
+ * You should have received a copy of the GNU Lesser General Public |
|
16 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
17 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
+ */ |
|
19 |
+ |
|
20 |
+#include <unistd.h> |
|
21 |
+#include <fcntl.h> |
|
22 |
+#include <sys/ioctl.h> |
|
23 |
+#include <sys/mman.h> |
|
24 |
+#include <linux/fb.h> |
|
25 |
+#include "libavutil/pixdesc.h" |
|
26 |
+#include "libavutil/log.h" |
|
27 |
+#include "libavutil/mem.h" |
|
28 |
+#include "libavutil/opt.h" |
|
29 |
+#include "libavformat/avformat.h" |
|
30 |
+ |
|
31 |
+typedef struct { |
|
32 |
+ AVClass *class; ///< class for private options |
|
33 |
+ int xoffset; ///< x coordinate of top left corner |
|
34 |
+ int yoffset; ///< y coordinate of top left corner |
|
35 |
+ struct fb_var_screeninfo varinfo; ///< framebuffer variable info |
|
36 |
+ struct fb_fix_screeninfo fixinfo; ///< framebuffer fixed info |
|
37 |
+ int fd; ///< framebuffer device file descriptor |
|
38 |
+ int index; ///< index of a video stream |
|
39 |
+ uint8_t *data; ///< framebuffer data |
|
40 |
+} FBDevContext; |
|
41 |
+ |
|
42 |
+struct rgb_pixfmt_map_entry { |
|
43 |
+ int bits_per_pixel; |
|
44 |
+ int red_offset, green_offset, blue_offset, alpha_offset; |
|
45 |
+ enum AVPixelFormat pixfmt; |
|
46 |
+}; |
|
47 |
+ |
|
48 |
+static const struct rgb_pixfmt_map_entry rgb_pixfmt_map[] = { |
|
49 |
+ // bpp, red_offset, green_offset, blue_offset, alpha_offset, pixfmt |
|
50 |
+ { 32, 0, 8, 16, 24, AV_PIX_FMT_RGBA }, |
|
51 |
+ { 32, 16, 8, 0, 24, AV_PIX_FMT_BGRA }, |
|
52 |
+ { 32, 8, 16, 24, 0, AV_PIX_FMT_ARGB }, |
|
53 |
+ { 32, 3, 2, 8, 0, AV_PIX_FMT_ABGR }, |
|
54 |
+ { 24, 0, 8, 16, 0, AV_PIX_FMT_RGB24 }, |
|
55 |
+ { 24, 16, 8, 0, 0, AV_PIX_FMT_BGR24 }, |
|
56 |
+ { 16, 11, 5, 0, 16, AV_PIX_FMT_RGB565 }, |
|
57 |
+}; |
|
58 |
+ |
|
59 |
+static enum AVPixelFormat get_pixfmt_from_fb_varinfo(struct fb_var_screeninfo *varinfo) |
|
60 |
+{ |
|
61 |
+ int i; |
|
62 |
+ |
|
63 |
+ for (i = 0; i < FF_ARRAY_ELEMS(rgb_pixfmt_map); i++) { |
|
64 |
+ const struct rgb_pixfmt_map_entry *entry = &rgb_pixfmt_map[i]; |
|
65 |
+ if (entry->bits_per_pixel == varinfo->bits_per_pixel && |
|
66 |
+ entry->red_offset == varinfo->red.offset && |
|
67 |
+ entry->green_offset == varinfo->green.offset && |
|
68 |
+ entry->blue_offset == varinfo->blue.offset) |
|
69 |
+ return entry->pixfmt; |
|
70 |
+ } |
|
71 |
+ |
|
72 |
+ return AV_PIX_FMT_NONE; |
|
73 |
+} |
|
74 |
+ |
|
75 |
+static av_cold int fbdev_write_header(AVFormatContext *h) |
|
76 |
+{ |
|
77 |
+ FBDevContext *fbdev = h->priv_data; |
|
78 |
+ enum AVPixelFormat pix_fmt; |
|
79 |
+ AVStream *st = NULL; |
|
80 |
+ int ret, flags = O_RDWR; |
|
81 |
+ |
|
82 |
+ for (int i = 0; i < h->nb_streams; i++) { |
|
83 |
+ if (h->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { |
|
84 |
+ if (!st) { |
|
85 |
+ fbdev->index = i; |
|
86 |
+ st = h->streams[i]; |
|
87 |
+ } else { |
|
88 |
+ av_log(h, AV_LOG_WARNING, "More than one video stream found. First one is used.\n"); |
|
89 |
+ break; |
|
90 |
+ } |
|
91 |
+ } |
|
92 |
+ } |
|
93 |
+ if (!st) { |
|
94 |
+ av_log(h, AV_LOG_ERROR, "No video stream found.\n"); |
|
95 |
+ return AVERROR(EINVAL); |
|
96 |
+ } |
|
97 |
+ |
|
98 |
+ if ((fbdev->fd = avpriv_open(h->filename, flags)) == -1) { |
|
99 |
+ ret = AVERROR(errno); |
|
100 |
+ av_log(h, AV_LOG_ERROR, |
|
101 |
+ "Could not open framebuffer device '%s': %s\n", |
|
102 |
+ h->filename, av_err2str(ret)); |
|
103 |
+ return ret; |
|
104 |
+ } |
|
105 |
+ |
|
106 |
+ if (ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->varinfo) < 0) { |
|
107 |
+ ret = AVERROR(errno); |
|
108 |
+ av_log(h, AV_LOG_ERROR, "FBIOGET_VSCREENINFO: %s\n", av_err2str(ret)); |
|
109 |
+ goto fail; |
|
110 |
+ } |
|
111 |
+ |
|
112 |
+ if (ioctl(fbdev->fd, FBIOGET_FSCREENINFO, &fbdev->fixinfo) < 0) { |
|
113 |
+ ret = AVERROR(errno); |
|
114 |
+ av_log(h, AV_LOG_ERROR, "FBIOGET_FSCREENINFO: %s\n", av_err2str(ret)); |
|
115 |
+ goto fail; |
|
116 |
+ } |
|
117 |
+ |
|
118 |
+ pix_fmt = get_pixfmt_from_fb_varinfo(&fbdev->varinfo); |
|
119 |
+ if (pix_fmt == AV_PIX_FMT_NONE) { |
|
120 |
+ ret = AVERROR(EINVAL); |
|
121 |
+ av_log(h, AV_LOG_ERROR, "Framebuffer pixel format not supported.\n"); |
|
122 |
+ goto fail; |
|
123 |
+ } |
|
124 |
+ |
|
125 |
+ fbdev->data = mmap(NULL, fbdev->fixinfo.smem_len, PROT_WRITE, MAP_SHARED, fbdev->fd, 0); |
|
126 |
+ if (fbdev->data == MAP_FAILED) { |
|
127 |
+ ret = AVERROR(errno); |
|
128 |
+ av_log(h, AV_LOG_ERROR, "Error in mmap(): %s\n", av_err2str(ret)); |
|
129 |
+ goto fail; |
|
130 |
+ } |
|
131 |
+ |
|
132 |
+ return 0; |
|
133 |
+ fail: |
|
134 |
+ close(fbdev->fd); |
|
135 |
+ return ret; |
|
136 |
+} |
|
137 |
+ |
|
138 |
+static int fbdev_write_packet(AVFormatContext *h, AVPacket *pkt) |
|
139 |
+{ |
|
140 |
+ FBDevContext *fbdev = h->priv_data; |
|
141 |
+ uint8_t *pin, *pout; |
|
142 |
+ enum AVPixelFormat fb_pix_fmt; |
|
143 |
+ int disp_height; |
|
144 |
+ int bytes_to_copy; |
|
145 |
+ AVCodecContext *codec_ctx = h->streams[fbdev->index]->codec; |
|
146 |
+ enum AVPixelFormat video_pix_fmt = codec_ctx->pix_fmt; |
|
147 |
+ int video_width = codec_ctx->width; |
|
148 |
+ int video_height = codec_ctx->height; |
|
149 |
+ int bytes_per_pixel = ((codec_ctx->bits_per_coded_sample + 7) >> 3); |
|
150 |
+ int src_line_size = video_width * bytes_per_pixel; |
|
151 |
+ |
|
152 |
+ if (fbdev->index != pkt->stream_index) |
|
153 |
+ return 0; |
|
154 |
+ |
|
155 |
+ if (ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->varinfo) < 0) |
|
156 |
+ av_log(h, AV_LOG_WARNING, |
|
157 |
+ "Error refreshing variable info: %s\n", av_err2str(AVERROR(errno))); |
|
158 |
+ |
|
159 |
+ fb_pix_fmt = get_pixfmt_from_fb_varinfo(&fbdev->varinfo); |
|
160 |
+ |
|
161 |
+ if (fb_pix_fmt != video_pix_fmt) { |
|
162 |
+ av_log(h, AV_LOG_ERROR, "Pixel format %s is not supported, use %s\n", |
|
163 |
+ av_get_pix_fmt_name(video_pix_fmt), av_get_pix_fmt_name(fb_pix_fmt)); |
|
164 |
+ return AVERROR(EINVAL); |
|
165 |
+ } |
|
166 |
+ |
|
167 |
+ disp_height = FFMIN(fbdev->varinfo.yres, video_height); |
|
168 |
+ bytes_to_copy = FFMIN(fbdev->varinfo.xres, video_width) * bytes_per_pixel; |
|
169 |
+ |
|
170 |
+ pin = pkt->data; |
|
171 |
+ pout = fbdev->data + |
|
172 |
+ bytes_per_pixel * fbdev->varinfo.xoffset + |
|
173 |
+ fbdev->varinfo.yoffset * fbdev->fixinfo.line_length; |
|
174 |
+ |
|
175 |
+ if (fbdev->xoffset) { |
|
176 |
+ if (fbdev->xoffset < 0) { |
|
177 |
+ if (-fbdev->xoffset >= video_width) //nothing to display |
|
178 |
+ return 0; |
|
179 |
+ bytes_to_copy += fbdev->xoffset * bytes_per_pixel; |
|
180 |
+ pin -= fbdev->xoffset * bytes_per_pixel; |
|
181 |
+ } else { |
|
182 |
+ int diff = (video_width + fbdev->xoffset) - fbdev->varinfo.xres; |
|
183 |
+ if (diff > 0) { |
|
184 |
+ if (diff >= video_width) //nothing to display |
|
185 |
+ return 0; |
|
186 |
+ bytes_to_copy -= diff * bytes_per_pixel; |
|
187 |
+ } |
|
188 |
+ pout += bytes_per_pixel * fbdev->xoffset; |
|
189 |
+ } |
|
190 |
+ } |
|
191 |
+ |
|
192 |
+ if (fbdev->yoffset) { |
|
193 |
+ if (fbdev->yoffset < 0) { |
|
194 |
+ if (-fbdev->yoffset >= video_height) //nothing to display |
|
195 |
+ return 0; |
|
196 |
+ disp_height += fbdev->yoffset; |
|
197 |
+ pin -= fbdev->yoffset * src_line_size; |
|
198 |
+ } else { |
|
199 |
+ int diff = (video_height + fbdev->yoffset) - fbdev->varinfo.yres; |
|
200 |
+ if (diff > 0) { |
|
201 |
+ if (diff >= video_height) //nothing to display |
|
202 |
+ return 0; |
|
203 |
+ disp_height -= diff; |
|
204 |
+ } |
|
205 |
+ pout += fbdev->yoffset * fbdev->fixinfo.line_length; |
|
206 |
+ } |
|
207 |
+ } |
|
208 |
+ |
|
209 |
+ for (int i = 0; i < disp_height; i++) { |
|
210 |
+ memcpy(pout, pin, bytes_to_copy); |
|
211 |
+ pout += fbdev->fixinfo.line_length; |
|
212 |
+ pin += src_line_size; |
|
213 |
+ } |
|
214 |
+ |
|
215 |
+ return 0; |
|
216 |
+} |
|
217 |
+ |
|
218 |
+static av_cold int fbdev_write_trailer(AVFormatContext *h) |
|
219 |
+{ |
|
220 |
+ FBDevContext *fbdev = h->priv_data; |
|
221 |
+ munmap(fbdev->data, fbdev->fixinfo.smem_len); |
|
222 |
+ close(fbdev->fd); |
|
223 |
+ return 0; |
|
224 |
+} |
|
225 |
+ |
|
226 |
+#define OFFSET(x) offsetof(FBDevContext, x) |
|
227 |
+#define ENC AV_OPT_FLAG_ENCODING_PARAM |
|
228 |
+static const AVOption options[] = { |
|
229 |
+ { "xoffset", "set x coordinate of top left corner", OFFSET(xoffset), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, ENC }, |
|
230 |
+ { "yoffset", "set y coordinate of top left corner", OFFSET(yoffset), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, ENC }, |
|
231 |
+ { NULL } |
|
232 |
+}; |
|
233 |
+ |
|
234 |
+static const AVClass fbdev_class = { |
|
235 |
+ .class_name = "fbdev outdev", |
|
236 |
+ .item_name = av_default_item_name, |
|
237 |
+ .option = options, |
|
238 |
+ .version = LIBAVUTIL_VERSION_INT, |
|
239 |
+}; |
|
240 |
+ |
|
241 |
+AVOutputFormat ff_fbdev_muxer = { |
|
242 |
+ .name = "fbdev", |
|
243 |
+ .long_name = NULL_IF_CONFIG_SMALL("Linux framebuffer"), |
|
244 |
+ .priv_data_size = sizeof(FBDevContext), |
|
245 |
+ .audio_codec = AV_CODEC_ID_NONE, |
|
246 |
+ .video_codec = AV_CODEC_ID_RAWVIDEO, |
|
247 |
+ .write_header = fbdev_write_header, |
|
248 |
+ .write_packet = fbdev_write_packet, |
|
249 |
+ .write_trailer = fbdev_write_trailer, |
|
250 |
+ .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, |
|
251 |
+ .priv_class = &fbdev_class, |
|
252 |
+}; |
... | ... |
@@ -28,7 +28,7 @@ |
28 | 28 |
#include "libavutil/avutil.h" |
29 | 29 |
|
30 | 30 |
#define LIBAVDEVICE_VERSION_MAJOR 55 |
31 |
-#define LIBAVDEVICE_VERSION_MINOR 4 |
|
31 |
+#define LIBAVDEVICE_VERSION_MINOR 5 |
|
32 | 32 |
#define LIBAVDEVICE_VERSION_MICRO 100 |
33 | 33 |
|
34 | 34 |
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ |