From beedb65f0045c71e916f5983728813e95bb3d4ea Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 5 Jul 2016 17:58:34 +0200 Subject: [PATCH] video: add video format start working on v4l2 source --- pinos/modules/spa/spa-alsa-sink.c | 12 +- spa/include/spa/audio/format.h | 13 + spa/include/spa/audio/raw.h | 13 - spa/include/spa/buffer.h | 20 +- spa/include/spa/format.h | 1 + spa/include/spa/props.h | 11 +- spa/include/spa/video/chroma.h | 58 +++ spa/include/spa/video/color.h | 157 +++++++ spa/include/spa/video/format.h | 66 +++ spa/include/spa/video/multiview.h | 136 ++++++ spa/include/spa/video/raw.h | 191 +++++++++ spa/lib/audio-raw.c | 1 + spa/lib/meson.build | 3 +- spa/lib/video-raw.c | 530 ++++++++++++++++++++++++ spa/meson.build | 1 + spa/plugins/alsa/alsa-sink.c | 2 +- spa/plugins/audiomixer/audiomixer.c | 2 +- spa/plugins/audiotestsrc/audiotestsrc.c | 2 +- spa/plugins/meson.build | 1 + spa/plugins/v4l2/meson.build | 9 + spa/plugins/v4l2/v4l2-source.c | 528 +++++++++++++++++++++++ spa/plugins/v4l2/v4l2-utils.c | 82 ++++ spa/plugins/v4l2/v4l2.c | 78 ++++ spa/plugins/volume/volume.c | 2 +- spa/tests/test-mixer.c | 2 +- 25 files changed, 1878 insertions(+), 43 deletions(-) create mode 100644 spa/include/spa/video/chroma.h create mode 100644 spa/include/spa/video/color.h create mode 100644 spa/include/spa/video/format.h create mode 100644 spa/include/spa/video/multiview.h create mode 100644 spa/include/spa/video/raw.h create mode 100644 spa/lib/video-raw.c create mode 100644 spa/plugins/v4l2/meson.build create mode 100644 spa/plugins/v4l2/v4l2-source.c create mode 100644 spa/plugins/v4l2/v4l2-utils.c create mode 100644 spa/plugins/v4l2/v4l2.c diff --git a/pinos/modules/spa/spa-alsa-sink.c b/pinos/modules/spa/spa-alsa-sink.c index f5fd312bc..a456ec92d 100644 --- a/pinos/modules/spa/spa-alsa-sink.c +++ b/pinos/modules/spa/spa-alsa-sink.c @@ -296,12 +296,12 @@ static void on_linked (PinosPort *port, PinosPort *peer, gpointer user_data) { PinosNode *node = user_data; - gint n_peers; + guint n_links; g_debug ("port %p: linked", port); - n_peers = pinos_port_get_n_links (port); - if (n_peers == 1) + pinos_port_get_links (port, &n_links); + if (n_links == 1) pinos_node_report_busy (node); } @@ -309,11 +309,11 @@ static void on_unlinked (PinosPort *port, PinosPort *peer, gpointer user_data) { PinosNode *node = user_data; - gint n_peers; + guint n_links; g_debug ("port %p: unlinked", port); - n_peers = pinos_port_get_n_links (port); - if (n_peers == 0) + pinos_port_get_links (port, &n_links); + if (n_links == 1) pinos_node_report_idle (node); } diff --git a/spa/include/spa/audio/format.h b/spa/include/spa/audio/format.h index 7acddb0be..54a7d91b6 100644 --- a/spa/include/spa/audio/format.h +++ b/spa/include/spa/audio/format.h @@ -25,6 +25,9 @@ extern "C" { #endif #include +#include + +typedef struct _SpaAudioRawFormat SpaAudioRawFormat; typedef enum { SPA_PROP_ID_AUDIO_FORMAT = SPA_PROP_ID_MEDIA_CUSTOM_START, @@ -36,6 +39,16 @@ typedef enum { SPA_PROP_ID_AUDIO_RAW_INFO, } SpaPropIdAudio; +struct _SpaAudioRawFormat { + SpaFormat format; + uint32_t unset_mask; + SpaAudioRawInfo info; +}; + +SpaResult spa_audio_raw_format_init (SpaAudioRawFormat *format); +SpaResult spa_audio_raw_format_parse (const SpaFormat *format, + SpaAudioRawFormat *rawformat); + #ifdef __cplusplus } /* extern "C" */ diff --git a/spa/include/spa/audio/raw.h b/spa/include/spa/audio/raw.h index 0c1f87abd..1a99c7d53 100644 --- a/spa/include/spa/audio/raw.h +++ b/spa/include/spa/audio/raw.h @@ -25,12 +25,9 @@ extern "C" { #endif typedef struct _SpaAudioRawInfo SpaAudioRawInfo; -typedef struct _SpaAudioRawFormat SpaAudioRawFormat; #include -#include - #if __BYTE_ORDER == __BIG_ENDIAN #define _SPA_AUDIO_FORMAT_NE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## BE #define _SPA_AUDIO_FORMAT_OE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## LE @@ -141,16 +138,6 @@ struct _SpaAudioRawInfo { uint32_t channel_mask; }; -struct _SpaAudioRawFormat { - SpaFormat format; - uint32_t unset_mask; - SpaAudioRawInfo info; -}; - -SpaResult spa_audio_raw_format_init (SpaAudioRawFormat *format); -SpaResult spa_audio_raw_format_parse (const SpaFormat *format, - SpaAudioRawFormat *rawformat); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/spa/include/spa/buffer.h b/spa/include/spa/buffer.h index 9ca658ab6..478b62d42 100644 --- a/spa/include/spa/buffer.h +++ b/spa/include/spa/buffer.h @@ -82,7 +82,7 @@ typedef struct { * SpaDataType: * @SPA_DATA_TYPE_INVALID: invalid data type, is ignored * @SPA_DATA_TYPE_MEMPTR: data and size point to memory - * @SPA_DATA_TYPE_FD: data points to SpaDataFd + * @SPA_DATA_TYPE_FD: data points to an int file descriptor */ typedef enum { SPA_DATA_TYPE_INVALID = 0, @@ -90,29 +90,21 @@ typedef enum { SPA_DATA_TYPE_FD, } SpaDataType; -/** - * SpaDataFd - * fd: a file descriptor - * offset: offset in the data referenced by @fd - * @size: size of data referenced by fd - */ -typedef struct { - int fd; - unsigned int offset; - size_t size; -} SpaDataFD; - /** * SpaData: * @id: user id * @type: the type of data - * @data: pointer to data + * @data: pointer to data or fd + * @offset: offset of data * @size: size of data + * @stride: stride of data */ typedef struct { SpaDataType type; void *data; + unsigned int offset; size_t size; + size_t stride; } SpaData; /** diff --git a/spa/include/spa/format.h b/spa/include/spa/format.h index 711568d44..d415fe832 100644 --- a/spa/include/spa/format.h +++ b/spa/include/spa/format.h @@ -32,6 +32,7 @@ typedef struct _SpaFormat SpaFormat; typedef enum { SPA_MEDIA_TYPE_INVALID = 0, SPA_MEDIA_TYPE_AUDIO = 1, + SPA_MEDIA_TYPE_VIDEO = 2, } SpaMediaType; typedef enum { diff --git a/spa/include/spa/props.h b/spa/include/spa/props.h index 4a93641ec..56bb5392a 100644 --- a/spa/include/spa/props.h +++ b/spa/include/spa/props.h @@ -53,6 +53,11 @@ typedef enum { SPA_PROP_TYPE_STRUCT, } SpaPropType; +typedef struct { + int32_t num; + int32_t denom; +} SpaFraction; + /** * SpaPropFlags: * @SPA_PROP_FLAG_NONE: no flags @@ -74,10 +79,8 @@ typedef enum { /* SpaPropRangeType: * @SPA_PROP_RANGE_TYPE_NONE: no range specified, full range of type applies * @SPA_PROP_RANGE_TYPE_MIN_MAX: range contains 2 values, min and max - * @SPA_PROP_RANGE_TYPE_ENUM: range contains enum of possible values with - * NULL-terminated name - * @SPA_PROP_RANGE_TYPE_FLAGS: range contains flags of possible values with - * NULL-terminated name + * @SPA_PROP_RANGE_TYPE_ENUM: range contains enum of possible values + * @SPA_PROP_RANGE_TYPE_FLAGS: range contains flags of possible values */ typedef enum { SPA_PROP_RANGE_TYPE_NONE = 0, diff --git a/spa/include/spa/video/chroma.h b/spa/include/spa/video/chroma.h new file mode 100644 index 000000000..8d2a1965f --- /dev/null +++ b/spa/include/spa/video/chroma.h @@ -0,0 +1,58 @@ +/* Simple Plugin API + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __SPA_VIDEO_CHROMA_H__ +#define __SPA_VIDEO_CHROMA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * SpaVideoChromaSite: + * @SPA_VIDEO_CHROMA_SITE_UNKNOWN: unknown cositing + * @SPA_VIDEO_CHROMA_SITE_NONE: no cositing + * @SPA_VIDEO_CHROMA_SITE_H_COSITED: chroma is horizontally cosited + * @SPA_VIDEO_CHROMA_SITE_V_COSITED: chroma is vertically cosited + * @SPA_VIDEO_CHROMA_SITE_ALT_LINE: choma samples are sited on alternate lines + * @SPA_VIDEO_CHROMA_SITE_COSITED: chroma samples cosited with luma samples + * @SPA_VIDEO_CHROMA_SITE_JPEG: jpeg style cositing, also for mpeg1 and mjpeg + * @SPA_VIDEO_CHROMA_SITE_MPEG2: mpeg2 style cositing + * @SPA_VIDEO_CHROMA_SITE_DV: DV style cositing + * + * Various Chroma sitings. + */ +typedef enum { + SPA_VIDEO_CHROMA_SITE_UNKNOWN = 0, + SPA_VIDEO_CHROMA_SITE_NONE = (1 << 0), + SPA_VIDEO_CHROMA_SITE_H_COSITED = (1 << 1), + SPA_VIDEO_CHROMA_SITE_V_COSITED = (1 << 2), + SPA_VIDEO_CHROMA_SITE_ALT_LINE = (1 << 3), + /* some common chroma cositing */ + SPA_VIDEO_CHROMA_SITE_COSITED = (SPA_VIDEO_CHROMA_SITE_H_COSITED | SPA_VIDEO_CHROMA_SITE_V_COSITED), + SPA_VIDEO_CHROMA_SITE_JPEG = (SPA_VIDEO_CHROMA_SITE_NONE), + SPA_VIDEO_CHROMA_SITE_MPEG2 = (SPA_VIDEO_CHROMA_SITE_H_COSITED), + SPA_VIDEO_CHROMA_SITE_DV = (SPA_VIDEO_CHROMA_SITE_COSITED | SPA_VIDEO_CHROMA_SITE_ALT_LINE), +} SpaVideoChromaSite; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPA_VIDEO_CHROMA_H__ */ diff --git a/spa/include/spa/video/color.h b/spa/include/spa/video/color.h new file mode 100644 index 000000000..444031b1d --- /dev/null +++ b/spa/include/spa/video/color.h @@ -0,0 +1,157 @@ +/* Simple Plugin API + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __SPA_VIDEO_COLOR_H__ +#define __SPA_VIDEO_COLOR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * SpaVideoColorRange: + * @SPA_VIDEO_COLOR_RANGE_UNKNOWN: unknown range + * @SPA_VIDEO_COLOR_RANGE_0_255: [0..255] for 8 bit components + * @SPA_VIDEO_COLOR_RANGE_16_235: [16..235] for 8 bit components. Chroma has + * [16..240] range. + * + * Possible color range values. These constants are defined for 8 bit color + * values and can be scaled for other bit depths. + */ +typedef enum { + SPA_VIDEO_COLOR_RANGE_UNKNOWN = 0, + SPA_VIDEO_COLOR_RANGE_0_255, + SPA_VIDEO_COLOR_RANGE_16_235 +} SpaVideoColorRange; + +/** + * SpaVideoColorMatrix: + * @SPA_VIDEO_COLOR_MATRIX_UNKNOWN: unknown matrix + * @SPA_VIDEO_COLOR_MATRIX_RGB: identity matrix + * @SPA_VIDEO_COLOR_MATRIX_FCC: FCC color matrix + * @SPA_VIDEO_COLOR_MATRIX_BT709: ITU-R BT.709 color matrix + * @SPA_VIDEO_COLOR_MATRIX_BT601: ITU-R BT.601 color matrix + * @SPA_VIDEO_COLOR_MATRIX_SMPTE240M: SMPTE 240M color matrix + * @SPA_VIDEO_COLOR_MATRIX_BT2020: ITU-R BT.2020 color matrix. Since: 1.6. + * + * The color matrix is used to convert between Y'PbPr and + * non-linear RGB (R'G'B') + */ +typedef enum { + SPA_VIDEO_COLOR_MATRIX_UNKNOWN = 0, + SPA_VIDEO_COLOR_MATRIX_RGB, + SPA_VIDEO_COLOR_MATRIX_FCC, + SPA_VIDEO_COLOR_MATRIX_BT709, + SPA_VIDEO_COLOR_MATRIX_BT601, + SPA_VIDEO_COLOR_MATRIX_SMPTE240M, + SPA_VIDEO_COLOR_MATRIX_BT2020 +} SpaVideoColorMatrix; + +/** + * SpaVideoTransferFunction: + * @SPA_VIDEO_TRANSFER_UNKNOWN: unknown transfer function + * @SPA_VIDEO_TRANSFER_GAMMA10: linear RGB, gamma 1.0 curve + * @SPA_VIDEO_TRANSFER_GAMMA18: Gamma 1.8 curve + * @SPA_VIDEO_TRANSFER_GAMMA20: Gamma 2.0 curve + * @SPA_VIDEO_TRANSFER_GAMMA22: Gamma 2.2 curve + * @SPA_VIDEO_TRANSFER_BT709: Gamma 2.2 curve with a linear segment in the lower + * range + * @SPA_VIDEO_TRANSFER_SMPTE240M: Gamma 2.2 curve with a linear segment in the + * lower range + * @SPA_VIDEO_TRANSFER_SRGB: Gamma 2.4 curve with a linear segment in the lower + * range + * @SPA_VIDEO_TRANSFER_GAMMA28: Gamma 2.8 curve + * @SPA_VIDEO_TRANSFER_LOG100: Logarithmic transfer characteristic + * 100:1 range + * @SPA_VIDEO_TRANSFER_LOG316: Logarithmic transfer characteristic + * 316.22777:1 range + * @SPA_VIDEO_TRANSFER_BT2020_12: Gamma 2.2 curve with a linear segment in the lower + * range. Used for BT.2020 with 12 bits per + * component. Since: 1.6. + * @SPA_VIDEO_TRANSFER_ADOBERGB: Gamma 2.19921875. Since: 1.8 + * + * The video transfer function defines the formula for converting between + * non-linear RGB (R'G'B') and linear RGB + */ +typedef enum { + SPA_VIDEO_TRANSFER_UNKNOWN = 0, + SPA_VIDEO_TRANSFER_GAMMA10, + SPA_VIDEO_TRANSFER_GAMMA18, + SPA_VIDEO_TRANSFER_GAMMA20, + SPA_VIDEO_TRANSFER_GAMMA22, + SPA_VIDEO_TRANSFER_BT709, + SPA_VIDEO_TRANSFER_SMPTE240M, + SPA_VIDEO_TRANSFER_SRGB, + SPA_VIDEO_TRANSFER_GAMMA28, + SPA_VIDEO_TRANSFER_LOG100, + SPA_VIDEO_TRANSFER_LOG316, + SPA_VIDEO_TRANSFER_BT2020_12, + SPA_VIDEO_TRANSFER_ADOBERGB +} SpaVideoTransferFunction; + +/** + * SpaVideoColorPrimaries: + * @SPA_VIDEO_COLOR_PRIMARIES_UNKNOWN: unknown color primaries + * @SPA_VIDEO_COLOR_PRIMARIES_BT709: BT709 primaries + * @SPA_VIDEO_COLOR_PRIMARIES_BT470M: BT470M primaries + * @SPA_VIDEO_COLOR_PRIMARIES_BT470BG: BT470BG primaries + * @SPA_VIDEO_COLOR_PRIMARIES_SMPTE170M: SMPTE170M primaries + * @SPA_VIDEO_COLOR_PRIMARIES_SMPTE240M: SMPTE240M primaries + * @SPA_VIDEO_COLOR_PRIMARIES_FILM: Generic film + * @SPA_VIDEO_COLOR_PRIMARIES_BT2020: BT2020 primaries. Since: 1.6. + * @SPA_VIDEO_COLOR_PRIMARIES_ADOBERGB: Adobe RGB primaries. Since: 1.8 + * + * The color primaries define the how to transform linear RGB values to and from + * the CIE XYZ colorspace. + */ +typedef enum { + SPA_VIDEO_COLOR_PRIMARIES_UNKNOWN = 0, + SPA_VIDEO_COLOR_PRIMARIES_BT709, + SPA_VIDEO_COLOR_PRIMARIES_BT470M, + SPA_VIDEO_COLOR_PRIMARIES_BT470BG, + SPA_VIDEO_COLOR_PRIMARIES_SMPTE170M, + SPA_VIDEO_COLOR_PRIMARIES_SMPTE240M, + SPA_VIDEO_COLOR_PRIMARIES_FILM, + SPA_VIDEO_COLOR_PRIMARIES_BT2020, + SPA_VIDEO_COLOR_PRIMARIES_ADOBERGB +} SpaVideoColorPrimaries; + +/** + * SpaVideoColorimetry: + * @range: the color range. This is the valid range for the samples. + * It is used to convert the samples to Y'PbPr values. + * @matrix: the color matrix. Used to convert between Y'PbPr and + * non-linear RGB (R'G'B') + * @transfer: the transfer function. used to convert between R'G'B' and RGB + * @primaries: color primaries. used to convert between R'G'B' and CIE XYZ + * + * Structure describing the color info. + */ +typedef struct { + SpaVideoColorRange range; + SpaVideoColorMatrix matrix; + SpaVideoTransferFunction transfer; + SpaVideoColorPrimaries primaries; +} SpaVideoColorimetry; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPA_VIDEO_COLOR_H__ */ diff --git a/spa/include/spa/video/format.h b/spa/include/spa/video/format.h new file mode 100644 index 000000000..b3fabf0b5 --- /dev/null +++ b/spa/include/spa/video/format.h @@ -0,0 +1,66 @@ +/* Simple Plugin API + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __SPA_VIDEO_FORMAT_H__ +#define __SPA_VIDEO_FORMAT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct _SpaVideoRawFormat SpaVideoRawFormat; + +typedef enum { + SPA_PROP_ID_VIDEO_FORMAT = SPA_PROP_ID_MEDIA_CUSTOM_START, + SPA_PROP_ID_VIDEO_WIDTH, + SPA_PROP_ID_VIDEO_HEIGHT, + SPA_PROP_ID_VIDEO_FRAMERATE, + SPA_PROP_ID_VIDEO_MAX_FRAMERATE, + SPA_PROP_ID_VIDEO_VIEWS, + SPA_PROP_ID_VIDEO_INTERLACE_MODE, + SPA_PROP_ID_VIDEO_PIXEL_ASPECT_RATIO, + SPA_PROP_ID_VIDEO_MULTIVIEW_MODE, + SPA_PROP_ID_VIDEO_MULTIVIEW_FLAGS, + SPA_PROP_ID_VIDEO_CHROMA_SITE, + SPA_PROP_ID_VIDEO_COLOR_RANGE, + SPA_PROP_ID_VIDEO_COLOR_MATRIX, + SPA_PROP_ID_VIDEO_TRANSFER_FUNCTION, + SPA_PROP_ID_VIDEO_COLOR_PRIMARIES, + SPA_PROP_ID_VIDEO_RAW_INFO, +} SpaPropIdVideo; + +struct _SpaVideoRawFormat { + SpaFormat format; + uint32_t unset_mask; + SpaVideoRawInfo info; +}; + +SpaResult spa_video_raw_format_init (SpaVideoRawFormat *format); +SpaResult spa_video_raw_format_parse (const SpaFormat *format, + SpaVideoRawFormat *rawformat); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPA_VIDEO_FORMAT */ diff --git a/spa/include/spa/video/multiview.h b/spa/include/spa/video/multiview.h new file mode 100644 index 000000000..9573d0d76 --- /dev/null +++ b/spa/include/spa/video/multiview.h @@ -0,0 +1,136 @@ +/* Simple Plugin API + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __SPA_VIDEO_MULTIVIEW_H__ +#define __SPA_VIDEO_MULTIVIEW_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * SpaVideoMultiviewMode: + * @SPA_VIDEO_MULTIVIEW_MODE_NONE: A special value indicating + * no multiview information. Used in SpaVideoInfo and other places to + * indicate that no specific multiview handling has been requested or + * provided. This value is never carried on caps. + * @SPA_VIDEO_MULTIVIEW_MODE_MONO: All frames are monoscopic. + * @SPA_VIDEO_MULTIVIEW_MODE_LEFT: All frames represent a left-eye view. + * @SPA_VIDEO_MULTIVIEW_MODE_RIGHT: All frames represent a right-eye view. + * @SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE: Left and right eye views are + * provided in the left and right half of the frame respectively. + * @SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX: Left and right eye + * views are provided in the left and right half of the frame, but + * have been sampled using quincunx method, with half-pixel offset + * between the 2 views. + * @SPA_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED: Alternating vertical + * columns of pixels represent the left and right eye view respectively. + * @SPA_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED: Alternating horizontal + * rows of pixels represent the left and right eye view respectively. + * @SPA_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM: The top half of the frame + * contains the left eye, and the bottom half the right eye. + * @SPA_VIDEO_MULTIVIEW_MODE_CHECKERBOARD: Pixels are arranged with + * alternating pixels representing left and right eye views in a + * checkerboard fashion. + * @SPA_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME: Left and right eye views + * are provided in separate frames alternately. + * @SPA_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME: Multiple + * independent views are provided in separate frames in sequence. + * This method only applies to raw video buffers at the moment. + * Specific view identification is via the #SpaVideoMultiviewMeta + * and #SpaVideoMeta(s) on raw video buffers. + * @SPA_VIDEO_MULTIVIEW_MODE_SEPARATED: Multiple views are + * provided as separate #SpaMemory framebuffers attached to each + * #SpaBuffer, described by the #SpaVideoMultiviewMeta + * and #SpaVideoMeta(s) + * + * All possible stereoscopic 3D and multiview representations. + * In conjunction with #SpaVideoMultiviewFlags, describes how + * multiview content is being transported in the stream. + */ +typedef enum { + SPA_VIDEO_MULTIVIEW_MODE_NONE = -1, + SPA_VIDEO_MULTIVIEW_MODE_MONO = 0, + /* Single view modes */ + SPA_VIDEO_MULTIVIEW_MODE_LEFT, + SPA_VIDEO_MULTIVIEW_MODE_RIGHT, + /* Stereo view modes */ + SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE, + SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX, + SPA_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED, + SPA_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED, + SPA_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM, + SPA_VIDEO_MULTIVIEW_MODE_CHECKERBOARD, + /* Padding for new frame packing modes */ + + SPA_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME = 32, + /* Multivew mode(s) */ + SPA_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME, + SPA_VIDEO_MULTIVIEW_MODE_SEPARATED + /* future expansion for annotated modes */ +} SpaVideoMultiviewMode; + +/** + * SpaVideoMultiviewFlags: + * @SPA_VIDEO_MULTIVIEW_FLAGS_NONE: No flags + * @SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST: For stereo streams, the + * normal arrangement of left and right views is reversed. + * @SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED: The left view is vertically + * mirrored. + * @SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED: The left view is horizontally + * mirrored. + * @SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED: The right view is + * vertically mirrored. + * @SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED: The right view is + * horizontally mirrored. + * @SPA_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT: For frame-packed + * multiview modes, indicates that the individual + * views have been encoded with half the true width or height + * and should be scaled back up for display. This flag + * is used for overriding input layout interpretation + * by adjusting pixel-aspect-ratio. + * For side-by-side, column interleaved or checkerboard packings, the + * pixel width will be doubled. For row interleaved and top-bottom + * encodings, pixel height will be doubled. + * @SPA_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO: The video stream contains both + * mono and multiview portions, signalled on each buffer by the + * absence or presence of the @SPA_VIDEO_BUFFER_FLAG_MULTIPLE_VIEW + * buffer flag. + * + * SpaVideoMultiviewFlags are used to indicate extra properties of a + * stereo/multiview stream beyond the frame layout and buffer mapping + * that is conveyed in the #SpaMultiviewMode. + */ +typedef enum { + SPA_VIDEO_MULTIVIEW_FLAGS_NONE = 0, + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST = (1 << 0), + SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED = (1 << 1), + SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED = (1 << 2), + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED = (1 << 3), + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED = (1 << 4), + SPA_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT = (1 << 14), + SPA_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO = (1 << 15) +} SpaVideoMultiviewFlags; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPA_VIDEO_MULTIVIEW_H__ */ diff --git a/spa/include/spa/video/raw.h b/spa/include/spa/video/raw.h new file mode 100644 index 000000000..ee5681e07 --- /dev/null +++ b/spa/include/spa/video/raw.h @@ -0,0 +1,191 @@ +/* Simple Plugin API + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __SPA_VIDEO_RAW_H__ +#define __SPA_VIDEO_RAW_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _SpaVideoRawInfo SpaVideoRawInfo; + +#include +#include +#include +#include + +#include + +#define SPA_VIDEO_MAX_PLANES 4 +#define SPA_VIDEO_MAX_COMPONENTS 4 + +typedef enum { + SPA_VIDEO_FORMAT_UNKNOWN, + SPA_VIDEO_FORMAT_ENCODED, + SPA_VIDEO_FORMAT_I420, + SPA_VIDEO_FORMAT_YV12, + SPA_VIDEO_FORMAT_YUY2, + SPA_VIDEO_FORMAT_UYVY, + SPA_VIDEO_FORMAT_AYUV, + SPA_VIDEO_FORMAT_RGBx, + SPA_VIDEO_FORMAT_BGRx, + SPA_VIDEO_FORMAT_xRGB, + SPA_VIDEO_FORMAT_xBGR, + SPA_VIDEO_FORMAT_RGBA, + SPA_VIDEO_FORMAT_BGRA, + SPA_VIDEO_FORMAT_ARGB, + SPA_VIDEO_FORMAT_ABGR, + SPA_VIDEO_FORMAT_RGB, + SPA_VIDEO_FORMAT_BGR, + SPA_VIDEO_FORMAT_Y41B, + SPA_VIDEO_FORMAT_Y42B, + SPA_VIDEO_FORMAT_YVYU, + SPA_VIDEO_FORMAT_Y444, + SPA_VIDEO_FORMAT_v210, + SPA_VIDEO_FORMAT_v216, + SPA_VIDEO_FORMAT_NV12, + SPA_VIDEO_FORMAT_NV21, + SPA_VIDEO_FORMAT_GRAY8, + SPA_VIDEO_FORMAT_GRAY16_BE, + SPA_VIDEO_FORMAT_GRAY16_LE, + SPA_VIDEO_FORMAT_v308, + SPA_VIDEO_FORMAT_RGB16, + SPA_VIDEO_FORMAT_BGR16, + SPA_VIDEO_FORMAT_RGB15, + SPA_VIDEO_FORMAT_BGR15, + SPA_VIDEO_FORMAT_UYVP, + SPA_VIDEO_FORMAT_A420, + SPA_VIDEO_FORMAT_RGB8P, + SPA_VIDEO_FORMAT_YUV9, + SPA_VIDEO_FORMAT_YVU9, + SPA_VIDEO_FORMAT_IYU1, + SPA_VIDEO_FORMAT_ARGB64, + SPA_VIDEO_FORMAT_AYUV64, + SPA_VIDEO_FORMAT_r210, + SPA_VIDEO_FORMAT_I420_10BE, + SPA_VIDEO_FORMAT_I420_10LE, + SPA_VIDEO_FORMAT_I422_10BE, + SPA_VIDEO_FORMAT_I422_10LE, + SPA_VIDEO_FORMAT_Y444_10BE, + SPA_VIDEO_FORMAT_Y444_10LE, + SPA_VIDEO_FORMAT_GBR, + SPA_VIDEO_FORMAT_GBR_10BE, + SPA_VIDEO_FORMAT_GBR_10LE, + SPA_VIDEO_FORMAT_NV16, + SPA_VIDEO_FORMAT_NV24, + SPA_VIDEO_FORMAT_NV12_64Z32, + SPA_VIDEO_FORMAT_A420_10BE, + SPA_VIDEO_FORMAT_A420_10LE, + SPA_VIDEO_FORMAT_A422_10BE, + SPA_VIDEO_FORMAT_A422_10LE, + SPA_VIDEO_FORMAT_A444_10BE, + SPA_VIDEO_FORMAT_A444_10LE, + SPA_VIDEO_FORMAT_NV61, + SPA_VIDEO_FORMAT_P010_10BE, + SPA_VIDEO_FORMAT_P010_10LE, + SPA_VIDEO_FORMAT_IYU2, +} SpaVideoFormat; + + +/** + * SpaVideoFlags: + * @SPA_VIDEO_FLAG_NONE: no flags + * @SPA_VIDEO_FLAG_VARIABLE_FPS: a variable fps is selected, fps_n and fps_d + * denote the maximum fps of the video + * @SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA: Each color has been scaled by the alpha + * value. + * + * Extra video flags + */ +typedef enum { + SPA_VIDEO_FLAG_NONE = 0, + SPA_VIDEO_FLAG_VARIABLE_FPS = (1 << 0), + SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA = (1 << 1) +} SpaVideoFlags; + +/** + * SpaVideoInterlaceMode: + * @SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE: all frames are progressive + * @SPA_VIDEO_INTERLACE_MODE_INTERLEAVED: 2 fields are interleaved in one video + * frame. Extra buffer flags describe the field order. + * @SPA_VIDEO_INTERLACE_MODE_MIXED: frames contains both interlaced and + * progressive video, the buffer flags describe the frame and fields. + * @SPA_VIDEO_INTERLACE_MODE_FIELDS: 2 fields are stored in one buffer, use the + * frame ID to get access to the required field. For multiview (the + * 'views' property > 1) the fields of view N can be found at frame ID + * (N * 2) and (N * 2) + 1. + * Each field has only half the amount of lines as noted in the + * height property. This mode requires multiple SpaVideoMeta metadata + * to describe the fields. + * + * The possible values of the #SpaVideoInterlaceMode describing the interlace + * mode of the stream. + */ +typedef enum { + SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE = 0, + SPA_VIDEO_INTERLACE_MODE_INTERLEAVED, + SPA_VIDEO_INTERLACE_MODE_MIXED, + SPA_VIDEO_INTERLACE_MODE_FIELDS +} SpaVideoInterlaceMode; + +/** + * SpaVideoRawInfo: + * @format: the format + * @width: the width of the video + * @height: the height of the video + * @framerate: the framerate of the video 0/1 means variable rate + * @max_framerate: the maximum framerate of the video. This is only valid when + * @framerate is 0/1 + * @views: the number of views in this video + * @interlace_mode: the interlace mode + * @pixel_aspect_ratio: The pixel aspect ratio + * @multiview_mode: multiview mode + * @multiview_flags: multiview flags + * @chroma_site: the chroma siting + * @color_range: the color range. This is the valid range for the samples. + * It is used to convert the samples to Y'PbPr values. + * @color_matrix: the color matrix. Used to convert between Y'PbPr and + * non-linear RGB (R'G'B') + * @transfer_function: the transfer function. used to convert between R'G'B' and RGB + * @color_primaries: color primaries. used to convert between R'G'B' and CIE XYZ + */ +struct _SpaVideoRawInfo { + SpaVideoFormat format; + unsigned int width; + unsigned int height; + SpaFraction framerate; + SpaFraction max_framerate; + unsigned int views; + SpaVideoInterlaceMode interlace_mode; + SpaFraction pixel_aspect_ratio; + SpaVideoMultiviewMode multiview_mode; + SpaVideoMultiviewFlags multiview_flags; + SpaVideoChromaSite chroma_site; + SpaVideoColorRange color_range; + SpaVideoColorMatrix color_matrix; + SpaVideoTransferFunction transfer_function; + SpaVideoColorPrimaries color_primaries; +}; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPA_VIDEO_RAW_H__ */ diff --git a/spa/lib/audio-raw.c b/spa/lib/audio-raw.c index 670a6c172..80f590af2 100644 --- a/spa/lib/audio-raw.c +++ b/spa/lib/audio-raw.c @@ -23,6 +23,7 @@ #include #include +#include static const uint32_t format_values[] = { SPA_AUDIO_FORMAT_S8, diff --git a/spa/lib/meson.build b/spa/lib/meson.build index 6a4588a32..d598b9b02 100644 --- a/spa/lib/meson.build +++ b/spa/lib/meson.build @@ -1,6 +1,7 @@ spalib_sources = ['audio-raw.c', 'props.c', - 'ringbuffer.c'] + 'ringbuffer.c', + 'video-raw.c'] spalib = shared_library('spa-lib', spalib_sources, diff --git a/spa/lib/video-raw.c b/spa/lib/video-raw.c new file mode 100644 index 000000000..c370cd6dc --- /dev/null +++ b/spa/lib/video-raw.c @@ -0,0 +1,530 @@ +/* Simple Plugin API + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include +#include + +static const SpaVideoRawInfo default_info = { + SPA_VIDEO_FORMAT_UNKNOWN, + 320, + 240, + { 0, 1 }, + { 0, 1 }, + 1, + SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE, + { 1, 1}, + SPA_VIDEO_MULTIVIEW_MODE_MONO, + SPA_VIDEO_MULTIVIEW_FLAGS_NONE, + SPA_VIDEO_CHROMA_SITE_UNKNOWN, + SPA_VIDEO_COLOR_RANGE_UNKNOWN, + SPA_VIDEO_COLOR_MATRIX_UNKNOWN, + SPA_VIDEO_TRANSFER_UNKNOWN, + SPA_VIDEO_COLOR_PRIMARIES_UNKNOWN +}; + +static const uint32_t format_values[] = { + SPA_VIDEO_FORMAT_UNKNOWN, + SPA_VIDEO_FORMAT_ENCODED, + SPA_VIDEO_FORMAT_I420, + SPA_VIDEO_FORMAT_YV12, + SPA_VIDEO_FORMAT_YUY2, + SPA_VIDEO_FORMAT_UYVY, + SPA_VIDEO_FORMAT_AYUV, + SPA_VIDEO_FORMAT_RGBx, + SPA_VIDEO_FORMAT_BGRx, + SPA_VIDEO_FORMAT_xRGB, + SPA_VIDEO_FORMAT_xBGR, + SPA_VIDEO_FORMAT_RGBA, + SPA_VIDEO_FORMAT_BGRA, + SPA_VIDEO_FORMAT_ARGB, + SPA_VIDEO_FORMAT_ABGR, + SPA_VIDEO_FORMAT_RGB, + SPA_VIDEO_FORMAT_BGR, + SPA_VIDEO_FORMAT_Y41B, + SPA_VIDEO_FORMAT_Y42B, + SPA_VIDEO_FORMAT_YVYU, + SPA_VIDEO_FORMAT_Y444, + SPA_VIDEO_FORMAT_v210, + SPA_VIDEO_FORMAT_v216, + SPA_VIDEO_FORMAT_NV12, + SPA_VIDEO_FORMAT_NV21, + SPA_VIDEO_FORMAT_GRAY8, + SPA_VIDEO_FORMAT_GRAY16_BE, + SPA_VIDEO_FORMAT_GRAY16_LE, + SPA_VIDEO_FORMAT_v308, + SPA_VIDEO_FORMAT_RGB16, + SPA_VIDEO_FORMAT_BGR16, + SPA_VIDEO_FORMAT_RGB15, + SPA_VIDEO_FORMAT_BGR15, + SPA_VIDEO_FORMAT_UYVP, + SPA_VIDEO_FORMAT_A420, + SPA_VIDEO_FORMAT_RGB8P, + SPA_VIDEO_FORMAT_YUV9, + SPA_VIDEO_FORMAT_YVU9, + SPA_VIDEO_FORMAT_IYU1, + SPA_VIDEO_FORMAT_ARGB64, + SPA_VIDEO_FORMAT_AYUV64, + SPA_VIDEO_FORMAT_r210, + SPA_VIDEO_FORMAT_I420_10BE, + SPA_VIDEO_FORMAT_I420_10LE, + SPA_VIDEO_FORMAT_I422_10BE, + SPA_VIDEO_FORMAT_I422_10LE, + SPA_VIDEO_FORMAT_Y444_10BE, + SPA_VIDEO_FORMAT_Y444_10LE, + SPA_VIDEO_FORMAT_GBR, + SPA_VIDEO_FORMAT_GBR_10BE, + SPA_VIDEO_FORMAT_GBR_10LE, + SPA_VIDEO_FORMAT_NV16, + SPA_VIDEO_FORMAT_NV24, + SPA_VIDEO_FORMAT_NV12_64Z32, + SPA_VIDEO_FORMAT_A420_10BE, + SPA_VIDEO_FORMAT_A420_10LE, + SPA_VIDEO_FORMAT_A422_10BE, + SPA_VIDEO_FORMAT_A422_10LE, + SPA_VIDEO_FORMAT_A444_10BE, + SPA_VIDEO_FORMAT_A444_10LE, + SPA_VIDEO_FORMAT_NV61, + SPA_VIDEO_FORMAT_P010_10BE, + SPA_VIDEO_FORMAT_P010_10LE, + SPA_VIDEO_FORMAT_IYU2, +}; + +static const SpaPropRangeInfo format_range[] = { + { "UNKNOWN", "UNKNOWN,", sizeof (uint32_t), &format_values[0] }, + { "ENCODED,", "ENCODED", sizeof (uint32_t), &format_values[1] }, + { "S16LE", "S16LE", sizeof (uint32_t), &format_values[2] }, + { "S16BE", "S16BE", sizeof (uint32_t), &format_values[3] }, + { "U16LE", "U16LE", sizeof (uint32_t), &format_values[4] }, + { "U16BE", "U16BE", sizeof (uint32_t), &format_values[5] }, + { "S24_32LE", "S24_32LE", sizeof (uint32_t), &format_values[6] }, + { "S24_32BE", "S24_32BE", sizeof (uint32_t), &format_values[7] }, + { "U24_32LE", "U24_32LE", sizeof (uint32_t), &format_values[8] }, + { "U24_32BE", "U24_32BE", sizeof (uint32_t), &format_values[9] }, + { "S32LE", "S32LE", sizeof (uint32_t), &format_values[10] }, + { "S32BE", "S32BE", sizeof (uint32_t), &format_values[11] }, + { "U32LE", "U32LE", sizeof (uint32_t), &format_values[12] }, + { "U32BE", "U32BE", sizeof (uint32_t), &format_values[13] }, + { "S24LE", "S24LE", sizeof (uint32_t), &format_values[14] }, + { "S24BE", "S24BE", sizeof (uint32_t), &format_values[15] }, + { "U24LE", "U24LE", sizeof (uint32_t), &format_values[16] }, + { "U24BE", "U24BE", sizeof (uint32_t), &format_values[17] }, + { "S20LE", "S20LE", sizeof (uint32_t), &format_values[18] }, + { "S20BE", "S20BE", sizeof (uint32_t), &format_values[19] }, + { "U20LE", "U20LE", sizeof (uint32_t), &format_values[20] }, + { "U20BE", "U20BE", sizeof (uint32_t), &format_values[21] }, + { "S18LE", "S18LE", sizeof (uint32_t), &format_values[22] }, + { "S18BE", "S18BE", sizeof (uint32_t), &format_values[23] }, + { "U18LE", "U18LE", sizeof (uint32_t), &format_values[24] }, + { "U18BE", "U18BE", sizeof (uint32_t), &format_values[25] }, + { "F32LE", "F32LE", sizeof (uint32_t), &format_values[26] }, + { "F32BE", "F32BE", sizeof (uint32_t), &format_values[27] }, + { "F64LE", "F64LE", sizeof (uint32_t), &format_values[28] }, + { "F64BE", "F64BE", sizeof (uint32_t), &format_values[29] }, +}; + +static const uint32_t interlace_modes[] = { + SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE, + SPA_VIDEO_INTERLACE_MODE_INTERLEAVED, + SPA_VIDEO_INTERLACE_MODE_MIXED, + SPA_VIDEO_INTERLACE_MODE_FIELDS +}; + +static const SpaPropRangeInfo interlace_mode_range[] = { + { "progressive", "Progressive video", sizeof (uint32_t), &interlace_modes[0] }, + { "interleaved", "Interleaved video", sizeof (uint32_t), &interlace_modes[1] }, + { "mixed", "Mixed interlaced video", sizeof (uint32_t), &interlace_modes[2] }, + { "fields", "Fields interlaced video", sizeof (uint32_t), &interlace_modes[3] }, +}; + + +static const uint32_t multiview_modes[] = { + SPA_VIDEO_MULTIVIEW_MODE_NONE, + SPA_VIDEO_MULTIVIEW_MODE_MONO, + SPA_VIDEO_MULTIVIEW_MODE_LEFT, + SPA_VIDEO_MULTIVIEW_MODE_RIGHT, + SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE, + SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX, + SPA_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED, + SPA_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED, + SPA_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM, + SPA_VIDEO_MULTIVIEW_MODE_CHECKERBOARD, + SPA_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME, + SPA_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME, + SPA_VIDEO_MULTIVIEW_MODE_SEPARATED +}; + +static const SpaPropRangeInfo multiview_mode_range[] = { + { "none", "None", sizeof (uint32_t), &multiview_modes[0] }, + { "mono", "Mono", sizeof (uint32_t), &multiview_modes[1] }, + { "left", "Left", sizeof (uint32_t), &multiview_modes[2] }, + { "right", "Right", sizeof (uint32_t), &multiview_modes[3] }, + { "side-by-side", "Side by side", sizeof (uint32_t), &multiview_modes[4] }, + { "side-by-side-quincunx", "Side by side Cuincunx", sizeof (uint32_t), &multiview_modes[5] }, + { "column-interleaved", "Column Interleaved", sizeof (uint32_t), &multiview_modes[6] }, + { "row-interleaved", "Row Interleaved", sizeof (uint32_t), &multiview_modes[7] }, + { "top-bottom", "Top Bottom", sizeof (uint32_t), &multiview_modes[8] }, + { "checkerboard", "Checkerboard", sizeof (uint32_t), &multiview_modes[9] }, + { "frame-by-frame", "Frame by frame", sizeof (uint32_t), &multiview_modes[10] }, + { "multiview-frame-by-frame", "Multiview Frame by frame", sizeof (uint32_t), &multiview_modes[11] }, + { "separated", "Separated", sizeof (uint32_t), &multiview_modes[12] }, +}; + +static const uint32_t multiview_flags[] = { + SPA_VIDEO_MULTIVIEW_FLAGS_NONE, + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST, + SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED, + SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED, + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED, + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED, + SPA_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, + SPA_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO, +}; + +static const SpaPropRangeInfo multiview_flags_range[] = { + { "none", "None", sizeof (uint32_t), &multiview_flags[0] }, + { "right-view-first", "Right view first", sizeof (uint32_t), &multiview_flags[1] }, + { "left-flipped", "Left flipped", sizeof (uint32_t), &multiview_flags[2] }, + { "left-flopped", "Left flopped", sizeof (uint32_t), &multiview_flags[3] }, + { "right-flipped", "Right flipped", sizeof (uint32_t), &multiview_flags[4] }, + { "right-flopped", "Right flopped", sizeof (uint32_t), &multiview_flags[5] }, + { "half-aspect", "Half aspect", sizeof (uint32_t), &multiview_flags[6] }, + { "mixed-mono", "Mixed mono", sizeof (uint32_t), &multiview_flags[7] }, +}; + +static const uint32_t chroma_sites[] = { + SPA_VIDEO_CHROMA_SITE_UNKNOWN, + SPA_VIDEO_CHROMA_SITE_NONE, + SPA_VIDEO_CHROMA_SITE_H_COSITED, + SPA_VIDEO_CHROMA_SITE_V_COSITED, + SPA_VIDEO_CHROMA_SITE_ALT_LINE, +}; + +static const SpaPropRangeInfo chroma_site_range[] = { + { "unknown", "Unknown", sizeof (uint32_t), &chroma_sites[0] }, + { "none", "None", sizeof (uint32_t), &chroma_sites[1] }, + { "h-cosited", "H-cosited", sizeof (uint32_t), &chroma_sites[2] }, + { "v-cosited", "V-cosited", sizeof (uint32_t), &chroma_sites[3] }, + { "alt-line", "Alt line", sizeof (uint32_t), &chroma_sites[4] } +}; + +static const uint32_t color_ranges[] = { + SPA_VIDEO_COLOR_RANGE_UNKNOWN, + SPA_VIDEO_COLOR_RANGE_0_255, + SPA_VIDEO_COLOR_RANGE_16_235 +}; + +static const SpaPropRangeInfo color_range_range[] = { + { "unknown", "Unknown color range", sizeof (uint32_t), &color_ranges[0] }, + { "0_255", "0-255", sizeof (uint32_t), &color_ranges[1] }, + { "16_235", "16-235", sizeof (uint32_t), &color_ranges[2] }, +}; + +static const uint32_t color_matrices[] = { + SPA_VIDEO_COLOR_MATRIX_UNKNOWN, + SPA_VIDEO_COLOR_MATRIX_RGB, + SPA_VIDEO_COLOR_MATRIX_FCC, + SPA_VIDEO_COLOR_MATRIX_BT709, + SPA_VIDEO_COLOR_MATRIX_BT601, + SPA_VIDEO_COLOR_MATRIX_SMPTE240M, + SPA_VIDEO_COLOR_MATRIX_BT2020 +}; + +static const SpaPropRangeInfo color_matrix_range[] = { + { "unknown", "Unknown color matrix", sizeof (uint32_t), &color_matrices[0] }, + { "rgb", "identity matrix", sizeof (uint32_t), &color_matrices[1] }, + { "fcc", "FCC color matrix", sizeof (uint32_t), &color_matrices[2] }, + { "bt709", "ITU-R BT.709 color matrix", sizeof (uint32_t), &color_matrices[3] }, + { "bt601", "ITU-R BT.601 color matrix", sizeof (uint32_t), &color_matrices[4] }, + { "smpte240m", "SMPTE 240M color matrix", sizeof (uint32_t), &color_matrices[5] }, + { "bt2020", "ITU-R BT.2020 color matrix", sizeof (uint32_t), &color_matrices[6] }, +}; + +static const uint32_t transfer_functions[] = { + SPA_VIDEO_TRANSFER_UNKNOWN, + SPA_VIDEO_TRANSFER_GAMMA10, + SPA_VIDEO_TRANSFER_GAMMA18, + SPA_VIDEO_TRANSFER_GAMMA20, + SPA_VIDEO_TRANSFER_GAMMA22, + SPA_VIDEO_TRANSFER_BT709, + SPA_VIDEO_TRANSFER_SMPTE240M, + SPA_VIDEO_TRANSFER_SRGB, + SPA_VIDEO_TRANSFER_GAMMA28, + SPA_VIDEO_TRANSFER_LOG100, + SPA_VIDEO_TRANSFER_LOG316, + SPA_VIDEO_TRANSFER_BT2020_12, + SPA_VIDEO_TRANSFER_ADOBERGB +}; + +static const SpaPropRangeInfo transfer_function_range[] = { + { "unknown", "Unknown transfer function", sizeof (uint32_t), &transfer_functions[0] }, + { "gamma10", "linear RGB, gamma 1.0 curve", sizeof (uint32_t), &transfer_functions[1] }, + { "gamma18", "gamma 1.8 curve", sizeof (uint32_t), &transfer_functions[2] }, + { "gamma20", "gamma 2.0 curve", sizeof (uint32_t), &transfer_functions[3] }, + { "gamma22", "gamma 2.2 curve", sizeof (uint32_t), &transfer_functions[4] }, + { "bt709", "Gamma 2.2 curve with a linear segment", sizeof (uint32_t), &transfer_functions[5] }, + { "smpte240m", "Gamma 2.2 curve with a linear segment", sizeof (uint32_t), &transfer_functions[6] }, + { "srgb", "Gamma 2.4 curve with a linear segment", sizeof (uint32_t), &transfer_functions[7] }, + { "gamma28", "Gamma 2.8 curve", sizeof (uint32_t), &transfer_functions[8] }, + { "log100", "Logarithmic transfer characteristic 100:1 range", sizeof (uint32_t), &transfer_functions[9] }, + { "log316", "Logarithmic transfer characteristic 316.22777:1 range", sizeof (uint32_t), &transfer_functions[10] }, + { "bt2020_12", "Gamma 2.2 curve with a linear segment", sizeof (uint32_t), &transfer_functions[11] }, + { "adobergb", "Gamma 2.19921875", sizeof (uint32_t), &transfer_functions[12] }, +}; + +static const uint32_t color_primaries[] = { + SPA_VIDEO_COLOR_PRIMARIES_UNKNOWN, + SPA_VIDEO_COLOR_PRIMARIES_BT709, + SPA_VIDEO_COLOR_PRIMARIES_BT470M, + SPA_VIDEO_COLOR_PRIMARIES_BT470BG, + SPA_VIDEO_COLOR_PRIMARIES_SMPTE170M, + SPA_VIDEO_COLOR_PRIMARIES_SMPTE240M, + SPA_VIDEO_COLOR_PRIMARIES_FILM, + SPA_VIDEO_COLOR_PRIMARIES_BT2020, + SPA_VIDEO_COLOR_PRIMARIES_ADOBERGB +}; + +static const SpaPropRangeInfo color_primaries_range[] = { + { "unknown", "Unknown color primaries", sizeof (uint32_t), &color_primaries[0] }, + { "bt709", "BT709 primaries", sizeof (uint32_t), &color_primaries[1] }, + { "bt470M", "BT470M primaries", sizeof (uint32_t), &color_primaries[2] }, + { "bt470BG", "BT470BG primaries", sizeof (uint32_t), &color_primaries[3] }, + { "smpte170m", "SMPTE170M primaries", sizeof (uint32_t), &color_primaries[4] }, + { "smpte240m", "SMPTE240M primaries", sizeof (uint32_t), &color_primaries[5] }, + { "film", "Generic film primaries", sizeof (uint32_t), &color_primaries[6] }, + { "bt2020", "BT2020 primaries", sizeof (uint32_t), &color_primaries[7] }, + { "adobergb", "Adobe RGB primaries", sizeof (uint32_t), &color_primaries[8] }, +}; + +static const uint32_t min_uint32 = 1; +static const uint32_t max_uint32 = UINT32_MAX; + +static const SpaPropRangeInfo uint32_range[] = { + { "min", "Minimum value", sizeof (uint32_t), &min_uint32 }, + { "max", "Maximum value", sizeof (uint32_t), &max_uint32 }, +}; + +static const SpaFraction min_framerate = { 0, 1 }; +static const SpaFraction max_framerate = { UINT32_MAX, 1 }; + +static const SpaPropRangeInfo framerate_range[] = { + { "min", "Minimum value", sizeof (SpaFraction), &min_framerate }, + { "max", "Maximum value", sizeof (SpaFraction), &max_framerate }, +}; + +static const SpaPropInfo raw_format_prop_info[] = +{ + { SPA_PROP_ID_VIDEO_FORMAT, "format", "The media format", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.format, + SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (format_range), format_range, + NULL, + offsetof (SpaVideoRawFormat, info.format), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 0, + NULL }, + { SPA_PROP_ID_VIDEO_WIDTH, "width", "Video width", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.width, + SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range, + NULL, + offsetof (SpaVideoRawFormat, info.width), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 1, + NULL }, + { SPA_PROP_ID_VIDEO_HEIGHT, "height", "Video height", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.height, + SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range, + NULL, + offsetof (SpaVideoRawFormat, info.height), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 2, + NULL }, + { SPA_PROP_ID_VIDEO_FRAMERATE, "framerate", "Video framerate", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_FRACTION, sizeof (SpaFraction), + sizeof (SpaFraction), &default_info.framerate, + SPA_PROP_RANGE_TYPE_MIN_MAX, 2, framerate_range, + NULL, + offsetof (SpaVideoRawFormat, info.framerate), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 3, + NULL }, + { SPA_PROP_ID_VIDEO_MAX_FRAMERATE, "max-framerate", "Video max framerate", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_FRACTION, sizeof (SpaFraction), + sizeof (SpaFraction), &default_info.max_framerate, + SPA_PROP_RANGE_TYPE_MIN_MAX, 2, framerate_range, + NULL, + offsetof (SpaVideoRawFormat, info.max_framerate), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 4, + NULL }, + { SPA_PROP_ID_VIDEO_VIEWS, "views", "Video number of views", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.views, + SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range, + NULL, + offsetof (SpaVideoRawFormat, info.views), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 5, + NULL }, + { SPA_PROP_ID_VIDEO_INTERLACE_MODE, "interlace-mode", "Interlace mode", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.interlace_mode, + SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (interlace_mode_range), interlace_mode_range, + NULL, + offsetof (SpaVideoRawFormat, info.interlace_mode), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 6, + NULL }, + { SPA_PROP_ID_VIDEO_PIXEL_ASPECT_RATIO, "pixel-aspect-ratio", "Video pixel aspect ratio", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_FRACTION, sizeof (SpaFraction), + sizeof (SpaFraction), &default_info.pixel_aspect_ratio, + SPA_PROP_RANGE_TYPE_MIN_MAX, 2, framerate_range, + NULL, + offsetof (SpaVideoRawFormat, info.pixel_aspect_ratio), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 7, + NULL }, + { SPA_PROP_ID_VIDEO_MULTIVIEW_MODE, "multiview-mode", "Multiview mode", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.multiview_mode, + SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (multiview_mode_range), multiview_mode_range, + NULL, + offsetof (SpaVideoRawFormat, info.multiview_mode), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 8, + NULL }, + { SPA_PROP_ID_VIDEO_MULTIVIEW_FLAGS, "multiview-flags", "Multiview flags", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.multiview_flags, + SPA_PROP_RANGE_TYPE_FLAGS, SPA_N_ELEMENTS (multiview_flags_range), multiview_flags_range, + NULL, + offsetof (SpaVideoRawFormat, info.multiview_flags), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 9, + NULL }, + { SPA_PROP_ID_VIDEO_CHROMA_SITE, "chroma-site", "Chroma site", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.chroma_site, + SPA_PROP_RANGE_TYPE_FLAGS, SPA_N_ELEMENTS (chroma_site_range), chroma_site_range, + NULL, + offsetof (SpaVideoRawFormat, info.chroma_site), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 10, + NULL }, + { SPA_PROP_ID_VIDEO_COLOR_RANGE, "color-range", "Color range", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.color_range, + SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (color_range_range), color_range_range, + NULL, + offsetof (SpaVideoRawFormat, info.color_range), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 11, + NULL }, + { SPA_PROP_ID_VIDEO_COLOR_MATRIX, "color-matrix", "Color matrix", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.color_matrix, + SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (color_matrix_range), color_matrix_range, + NULL, + offsetof (SpaVideoRawFormat, info.color_matrix), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 12, + NULL }, + { SPA_PROP_ID_VIDEO_TRANSFER_FUNCTION, "transfer-function", "Transfer function", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.transfer_function, + SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (transfer_function_range), transfer_function_range, + NULL, + offsetof (SpaVideoRawFormat, info.transfer_function), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 13, + NULL }, + { SPA_PROP_ID_VIDEO_COLOR_PRIMARIES, "color-primaries", "Color primaries", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_info.color_primaries, + SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (color_primaries_range), color_primaries_range, + NULL, + offsetof (SpaVideoRawFormat, info.color_primaries), + offsetof (SpaVideoRawFormat, unset_mask), 1 << 14, + NULL }, + { SPA_PROP_ID_VIDEO_RAW_INFO, "info", "the SpaVideoRawInfo structure", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_STRUCT, sizeof (SpaVideoRawInfo), + 0, NULL, + SPA_PROP_RANGE_TYPE_NONE, 0, NULL, + NULL, + offsetof (SpaVideoRawFormat, info), + offsetof (SpaVideoRawFormat, unset_mask), ~0, + NULL }, +}; + +SpaResult +spa_video_raw_format_init (SpaVideoRawFormat *format) +{ + format->format.media_type = SPA_MEDIA_TYPE_VIDEO; + format->format.media_subtype = SPA_MEDIA_SUBTYPE_RAW; + format->format.props.n_prop_info = SPA_N_ELEMENTS (raw_format_prop_info); + format->format.props.prop_info = raw_format_prop_info; + format->format.props.set_prop = spa_props_generic_set_prop; + format->format.props.get_prop = spa_props_generic_get_prop; + format->unset_mask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 4); + format->info = default_info; + + return SPA_RESULT_OK; +} + +SpaResult +spa_video_raw_format_parse (const SpaFormat *format, + SpaVideoRawFormat *rawformat) +{ + SpaPropValue value; + const SpaProps *props; + SpaResult res; + + if ((void *)format == (void *)rawformat) + return SPA_RESULT_OK; + + if (format->media_type != SPA_MEDIA_TYPE_VIDEO || + format->media_subtype != SPA_MEDIA_SUBTYPE_RAW) + return SPA_RESULT_INVALID_MEDIA_TYPE; + + spa_video_raw_format_init (rawformat); + + props = &format->props; + if ((res = props->get_prop (props, SPA_PROP_ID_VIDEO_RAW_INFO, &value)) < 0) + goto fallback; + + if (value.type != SPA_PROP_TYPE_STRUCT || value.size != sizeof (SpaVideoRawInfo)) + goto fallback; + + memcpy (&rawformat->info, value.value, sizeof (SpaVideoRawInfo)); + + return SPA_RESULT_OK; + +fallback: + res = spa_props_copy (props, &rawformat->format.props); + + return res; +} diff --git a/spa/meson.build b/spa/meson.build index 5c0de29c2..f4bda1182 100644 --- a/spa/meson.build +++ b/spa/meson.build @@ -1,6 +1,7 @@ project('spa', 'c') alsa_dep = dependency('alsa') +v4l2_dep = dependency('libv4l2') dl_lib = find_library('dl', required : true) inc = include_directories('include') diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c index 87cd8ef9c..9b7816761 100644 --- a/spa/plugins/alsa/alsa-sink.c +++ b/spa/plugins/alsa/alsa-sink.c @@ -23,7 +23,7 @@ #include #include -#include +#include typedef struct _SpaALSASink SpaALSASink; diff --git a/spa/plugins/audiomixer/audiomixer.c b/spa/plugins/audiomixer/audiomixer.c index 692cbd9d0..59c492c65 100644 --- a/spa/plugins/audiomixer/audiomixer.c +++ b/spa/plugins/audiomixer/audiomixer.c @@ -21,7 +21,7 @@ #include #include -#include +#include #define MAX_PORTS 128 diff --git a/spa/plugins/audiotestsrc/audiotestsrc.c b/spa/plugins/audiotestsrc/audiotestsrc.c index 1b7b6a7be..002e5e85e 100644 --- a/spa/plugins/audiotestsrc/audiotestsrc.c +++ b/spa/plugins/audiotestsrc/audiotestsrc.c @@ -21,7 +21,7 @@ #include #include -#include +#include typedef struct _SpaAudioTestSrc SpaAudioTestSrc; diff --git a/spa/plugins/meson.build b/spa/plugins/meson.build index 52a3f253e..7badc599f 100644 --- a/spa/plugins/meson.build +++ b/spa/plugins/meson.build @@ -2,3 +2,4 @@ subdir('alsa') subdir('audiomixer') subdir('audiotestsrc') subdir('volume') +subdir('v4l2') diff --git a/spa/plugins/v4l2/meson.build b/spa/plugins/v4l2/meson.build new file mode 100644 index 000000000..0fbd5183e --- /dev/null +++ b/spa/plugins/v4l2/meson.build @@ -0,0 +1,9 @@ +v4l2_sources = ['v4l2.c', + 'v4l2-source.c'] + +v4l2lib = shared_library('spa-v4l2', + v4l2_sources, + include_directories : inc, + dependencies : v4l2_dep, + link_with : spalib, + install : true) diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c new file mode 100644 index 000000000..614fe5bfb --- /dev/null +++ b/spa/plugins/v4l2/v4l2-source.c @@ -0,0 +1,528 @@ +/* Spa V4l2 Source + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +typedef struct _SpaV4l2Source SpaV4l2Source; + +static const char default_device[] = "/dev/video0"; + +typedef struct { + SpaProps props; + char device[64]; + char device_name[128]; + int device_fd; +} SpaV4l2SourceProps; + +static void +reset_v4l2_source_props (SpaV4l2SourceProps *props) +{ + strncpy (props->device, default_device, 64); +} + +typedef struct { + int fd; + pthread_t thread; + bool running; +} SpaV4l2State; + +typedef struct _V4l2Buffer V4l2Buffer; + +struct _V4l2Buffer { + SpaBuffer buffer; + SpaMeta meta[1]; + SpaMetaHeader header; + SpaData data[1]; + V4l2Buffer *next; +}; + +struct _SpaV4l2Source { + SpaHandle handle; + + SpaV4l2SourceProps tmp_props; + SpaV4l2SourceProps props; + + bool activated; + + SpaEventCallback event_cb; + void *user_data; + + bool have_format; + SpaVideoRawFormat query_format; + SpaVideoRawFormat current_format; + + SpaV4l2State state; + + SpaPortInfo info; + SpaPortStatus status; + + V4l2Buffer buffer; +}; + +#include "v4l2-utils.c" + +static const uint32_t min_uint32 = 1; +static const uint32_t max_uint32 = UINT32_MAX; + +static const SpaPropRangeInfo uint32_range[] = { + { "min", "Minimum value", 4, &min_uint32 }, + { "max", "Maximum value", 4, &max_uint32 }, +}; + +enum { + PROP_ID_DEVICE, + PROP_ID_DEVICE_NAME, + PROP_ID_DEVICE_FD, + PROP_ID_LAST, +}; + +static const SpaPropInfo prop_info[] = +{ + { PROP_ID_DEVICE, "device", "V4l2 device location", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_STRING, 63, + strlen (default_device)+1, default_device, + SPA_PROP_RANGE_TYPE_NONE, 0, NULL, + NULL, + offsetof (SpaV4l2SourceProps, device), + 0, 0, + NULL }, + { PROP_ID_DEVICE_NAME, "device-name", "Human-readable name of the device", + SPA_PROP_FLAG_READABLE, + SPA_PROP_TYPE_STRING, 127, + 0, NULL, + SPA_PROP_RANGE_TYPE_NONE, 0, NULL, + NULL, + offsetof (SpaV4l2SourceProps, device_name), + 0, 0, + NULL }, + { PROP_ID_DEVICE_FD, "device-fd", "Device file descriptor", + SPA_PROP_FLAG_READABLE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + 0, NULL, + SPA_PROP_RANGE_TYPE_NONE, 0, NULL, + NULL, + offsetof (SpaV4l2SourceProps, device_fd), + 0, 0, + NULL }, +}; + +static SpaResult +spa_v4l2_source_node_get_props (SpaHandle *handle, + SpaProps **props) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + + if (handle == NULL || props == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + memcpy (&this->tmp_props, &this->props, sizeof (this->props)); + *props = &this->tmp_props.props; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_set_props (SpaHandle *handle, + const SpaProps *props) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + SpaV4l2SourceProps *p = &this->props; + SpaResult res; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (props == NULL) { + reset_v4l2_source_props (p); + return SPA_RESULT_OK; + } + + res = spa_props_copy (props, &p->props); + + return res; +} + +static SpaResult +spa_v4l2_source_node_send_command (SpaHandle *handle, + SpaCommand *command) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + + if (handle == NULL || command == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (command->type) { + case SPA_COMMAND_INVALID: + return SPA_RESULT_INVALID_COMMAND; + + case SPA_COMMAND_ACTIVATE: + if (!this->activated) { + spa_v4l2_open (this); + this->activated = true; + } + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_ACTIVATED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + case SPA_COMMAND_DEACTIVATE: + if (this->activated) { + spa_v4l2_close (this); + this->activated = false; + } + if (this->event_cb) { + SpaEvent event; + + event.refcount = 1; + event.notify = NULL; + event.type = SPA_EVENT_TYPE_DEACTIVATED; + event.port_id = -1; + event.data = NULL; + event.size = 0; + + this->event_cb (handle, &event, this->user_data); + } + break; + case SPA_COMMAND_START: + spa_v4l2_start (this); + break; + case SPA_COMMAND_STOP: + spa_v4l2_stop (this); + break; + case SPA_COMMAND_FLUSH: + case SPA_COMMAND_DRAIN: + case SPA_COMMAND_MARKER: + return SPA_RESULT_NOT_IMPLEMENTED; + } + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_set_event_callback (SpaHandle *handle, + SpaEventCallback event, + void *user_data) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this->event_cb = event; + this->user_data = user_data; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_get_n_ports (SpaHandle *handle, + unsigned int *n_input_ports, + unsigned int *max_input_ports, + unsigned int *n_output_ports, + unsigned int *max_output_ports) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports) + *n_input_ports = 0; + if (n_output_ports) + *n_output_ports = 1; + if (max_input_ports) + *max_input_ports = 0; + if (max_output_ports) + *max_output_ports = 1; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_get_port_ids (SpaHandle *handle, + unsigned int n_input_ports, + uint32_t *input_ids, + unsigned int n_output_ports, + uint32_t *output_ids) +{ + if (handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_output_ports > 0) + output_ids[0] = 0; + + return SPA_RESULT_OK; +} + + +static SpaResult +spa_v4l2_source_node_add_port (SpaHandle *handle, + SpaDirection direction, + uint32_t *port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_v4l2_source_node_remove_port (SpaHandle *handle, + uint32_t port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_v4l2_source_node_enum_port_formats (SpaHandle *handle, + uint32_t port_id, + unsigned int index, + SpaFormat **format) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + switch (index) { + case 0: + spa_video_raw_format_init (&this->query_format); + break; + default: + return SPA_RESULT_ENUM_END; + } + *format = &this->query_format.format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_set_port_format (SpaHandle *handle, + uint32_t port_id, + int test_only, + const SpaFormat *format) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + SpaResult res; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + if (format == NULL) { + this->have_format = false; + return SPA_RESULT_OK; + } + + if ((res = spa_video_raw_format_parse (format, &this->current_format)) < 0) + return res; + + this->have_format = true; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_get_port_format (SpaHandle *handle, + uint32_t port_id, + const SpaFormat **format) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + + if (handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + if (!this->have_format) + return SPA_RESULT_NO_FORMAT; + + *format = &this->current_format.format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_get_port_info (SpaHandle *handle, + uint32_t port_id, + const SpaPortInfo **info) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + + if (handle == NULL || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + *info = &this->info; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_get_port_props (SpaHandle *handle, + uint32_t port_id, + SpaProps **props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_v4l2_source_node_set_port_props (SpaHandle *handle, + uint32_t port_id, + const SpaProps *props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_v4l2_source_node_get_port_status (SpaHandle *handle, + uint32_t port_id, + const SpaPortStatus **status) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + + if (handle == NULL || status == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + *status = &this->status; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_v4l2_source_node_push_port_input (SpaHandle *handle, + unsigned int n_info, + SpaInputInfo *info) +{ + return SPA_RESULT_INVALID_PORT; +} + +static SpaResult +spa_v4l2_source_node_pull_port_output (SpaHandle *handle, + unsigned int n_info, + SpaOutputInfo *info) +{ + SpaV4l2Source *this = (SpaV4l2Source *) handle; + unsigned int i; + bool have_error = false; + + if (handle == NULL || n_info == 0 || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + for (i = 0; i < n_info; i++) { + if (info[i].port_id != 0) { + info[i].status = SPA_RESULT_INVALID_PORT; + have_error = true; + continue; + } + if (!this->have_format) { + info[i].status = SPA_RESULT_NO_FORMAT; + have_error = true; + continue; + } + info[i].status = SPA_RESULT_OK; + } + if (have_error) + return SPA_RESULT_ERROR; + + return SPA_RESULT_OK; +} + +static const SpaNode v4l2source_node = { + sizeof (SpaNode), + spa_v4l2_source_node_get_props, + spa_v4l2_source_node_set_props, + spa_v4l2_source_node_send_command, + spa_v4l2_source_node_set_event_callback, + spa_v4l2_source_node_get_n_ports, + spa_v4l2_source_node_get_port_ids, + spa_v4l2_source_node_add_port, + spa_v4l2_source_node_remove_port, + spa_v4l2_source_node_enum_port_formats, + spa_v4l2_source_node_set_port_format, + spa_v4l2_source_node_get_port_format, + spa_v4l2_source_node_get_port_info, + spa_v4l2_source_node_get_port_props, + spa_v4l2_source_node_set_port_props, + spa_v4l2_source_node_get_port_status, + spa_v4l2_source_node_push_port_input, + spa_v4l2_source_node_pull_port_output, +}; + +static SpaResult +spa_v4l2_source_get_interface (SpaHandle *handle, + uint32_t interface_id, + const void **interface) +{ + if (handle == NULL || interface == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + switch (interface_id) { + case SPA_INTERFACE_ID_NODE: + *interface = &v4l2source_node; + break; + default: + return SPA_RESULT_UNKNOWN_INTERFACE; + } + return SPA_RESULT_OK; +} + +SpaHandle * +spa_v4l2_source_new (void) +{ + SpaHandle *handle; + SpaV4l2Source *this; + + handle = calloc (1, sizeof (SpaV4l2Source)); + handle->get_interface = spa_v4l2_source_get_interface; + + this = (SpaV4l2Source *) handle; + this->props.props.n_prop_info = PROP_ID_LAST; + this->props.props.prop_info = prop_info; + this->props.props.set_prop = spa_props_generic_set_prop; + this->props.props.get_prop = spa_props_generic_get_prop; + reset_v4l2_source_props (&this->props); + + this->info.flags = SPA_PORT_INFO_FLAG_NONE; + this->status.flags = SPA_PORT_STATUS_FLAG_NONE; + return handle; +} diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c new file mode 100644 index 000000000..8c797dc7a --- /dev/null +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHECK(s,msg) if ((err = (s)) < 0) { printf (msg ": %s\n", snd_strerror(err)); return err; } + + +static int +spa_v4l2_open (SpaV4l2Source *this) +{ + struct stat st; + + fprintf (stderr, "Playback device is '%s'\n", this->props.device); + + if (stat (this->props.device, &st) < 0) { + fprintf(stderr, "Cannot identify '%s': %d, %s\n", + this->props.device, errno, strerror (errno)); + return -1; + } + + if (!S_ISCHR (st.st_mode)) { + fprintf(stderr, "%s is no device\n", this->props.device); + return -1; + } + + this->state.fd = open (this->props.device, O_RDWR | O_NONBLOCK, 0); + + if (this->state.fd == -1) { + fprintf(stderr, "Cannot open '%s': %d, %s\n", + this->props.device, errno, strerror (errno)); + return -1; + } + return 0; +} + +static int +spa_v4l2_close (SpaV4l2Source *this) +{ + if (close(this->state.fd)) + fprintf(stderr, "Cannot close %d, %s\n", + errno, strerror (errno)); + + this->state.fd = -1; + return 0; +} + +static void * +v4l2_loop (void *user_data) +{ + return NULL; +} + +static int +spa_v4l2_start (SpaV4l2Source *this) +{ + SpaV4l2State *state = &this->state; + int err; + + state->running = true; + if ((err = pthread_create (&state->thread, NULL, v4l2_loop, this)) != 0) { + printf ("can't create thread: %d", err); + state->running = false; + } + return err; +} + +static int +spa_v4l2_stop (SpaV4l2Source *this) +{ + SpaV4l2State *state = &this->state; + + if (state->running) { + state->running = false; + pthread_join (state->thread, NULL); + } + return 0; +} diff --git a/spa/plugins/v4l2/v4l2.c b/spa/plugins/v4l2/v4l2.c new file mode 100644 index 000000000..95a3e5cb5 --- /dev/null +++ b/spa/plugins/v4l2/v4l2.c @@ -0,0 +1,78 @@ +/* Spa V4l2 support + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +SpaHandle * spa_v4l2_source_new (void); + +static SpaResult +v4l2_source_instantiate (const SpaHandleFactory *factory, + SpaHandle **handle) +{ + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + *handle = spa_v4l2_source_new (); + + return SPA_RESULT_OK; +} + +static const SpaInterfaceInfo v4l2_source_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, +}; + +static SpaResult +v4l2_source_enum_interface_info (const SpaHandleFactory *factory, + unsigned int index, + const SpaInterfaceInfo **info) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *info = &v4l2_source_interfaces[index]; + + return SPA_RESULT_OK; +} + +static const SpaHandleFactory factories[] = +{ + { "v4l2-source", + NULL, + v4l2_source_instantiate, + v4l2_source_enum_interface_info, + }, +}; + + +SpaResult +spa_enum_handle_factory (unsigned int index, + const SpaHandleFactory **factory) +{ + if (index >= 1) + return SPA_RESULT_ENUM_END; + + *factory = &factories[index]; + + return SPA_RESULT_OK; +} diff --git a/spa/plugins/volume/volume.c b/spa/plugins/volume/volume.c index 14f3ee8ef..ba8ffe9e1 100644 --- a/spa/plugins/volume/volume.c +++ b/spa/plugins/volume/volume.c @@ -21,7 +21,7 @@ #include #include -#include +#include typedef struct _SpaVolume SpaVolume; diff --git a/spa/tests/test-mixer.c b/spa/tests/test-mixer.c index b9a6ea2ac..2f94e3708 100644 --- a/spa/tests/test-mixer.c +++ b/spa/tests/test-mixer.c @@ -24,7 +24,7 @@ #include #include -#include +#include typedef struct { SpaHandle *sink;