Skip to content

Commit 9948fd4

Browse files
committed
Add Capture::Mode class for explicit resolution and format control
Introduces Mode class to represent specific capture configurations with: - Resolution (width/height) - Frame rate - Codec (Uncompressed, JPEG, H264, HEVC) - Pixel format (RGB/YUV variants) Adds Device::getModes() to query supported capture modes and new Capture::create(device, mode) factory method for explicit mode selection. Implements comparison operators and stream output for Mode class.
1 parent 70cbee3 commit 9948fd4

File tree

4 files changed

+282
-3
lines changed

4 files changed

+282
-3
lines changed

include/cinder/Capture.h

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "cinder/Cinder.h"
2626
#include "cinder/Surface.h"
2727
#include "cinder/Exception.h"
28+
#include "cinder/MediaTime.h"
2829

2930
#if defined( CINDER_ANDROID )
3031
#include "cinder/gl/Texture.h"
@@ -46,6 +47,10 @@
4647
namespace cinder {
4748
class CaptureImplDirectShow;
4849
}
50+
#elif defined( CINDER_LINUX )
51+
namespace cinder {
52+
class CaptureImplGStreamer;
53+
}
4954
#elif defined( CINDER_ANDROID )
5055
namespace cinder {
5156
class CaptureImplJni;
@@ -60,11 +65,14 @@ typedef std::shared_ptr<class Capture> CaptureRef;
6065

6166
class CI_API Capture {
6267
public:
68+
class Mode;
6369
class Device;
6470
typedef std::shared_ptr<Device> DeviceRef;
6571

6672
//! Creates a new Capture requesting (but not promising) a resolution of \a width x \a height pixels.
6773
static CaptureRef create( int32_t width, int32_t height, const DeviceRef device = DeviceRef() ) { return CaptureRef( new Capture( width, height, device ) ); }
74+
//! Creates a new Capture using a specific Mode supported by the Device. The Mode must be from the Device's getModes() list.
75+
static CaptureRef create( const DeviceRef& device, const Mode& mode ) { return CaptureRef( new Capture( device, mode ) ); }
6876

6977
~Capture();
7078

@@ -106,12 +114,84 @@ class CI_API Capture {
106114
//! Finds the first device whose name contains the string \a nameFragment
107115
static DeviceRef findDeviceByNameContains( const std::string &nameFragment );
108116

109-
#if defined( CINDER_COCOA ) || defined( CINDER_ANDROID )
117+
#if defined( CINDER_COCOA ) || defined( CINDER_ANDROID ) || defined( CINDER_LINUX )
110118
typedef std::string DeviceIdentifier;
111119
#else
112120
typedef int DeviceIdentifier;
113121
#endif
114122

123+
//! Represents a specific capture mode with resolution, framerate, codec and pixel format
124+
class CI_API Mode {
125+
public:
126+
enum class Codec {
127+
Uncompressed, // Raw uncompressed data
128+
JPEG, // Motion JPEG
129+
H264, // H.264/AVC
130+
HEVC, // H.265/HEVC
131+
Unknown
132+
};
133+
134+
enum class PixelFormat {
135+
// RGB formats
136+
RGB24, // 24-bit RGB
137+
BGR24, // 24-bit BGR
138+
ARGB32, // 32-bit ARGB
139+
BGRA32, // 32-bit BGRA
140+
141+
// YUV formats
142+
YUV420P, // Planar 4:2:0 YUV
143+
NV12, // Semi-planar 4:2:0 YUV
144+
YUY2, // Packed 4:2:2 YUV
145+
UYVY, // Packed 4:2:2 YUV (U first)
146+
I420, // Same as YUV420P but explicit
147+
YV12, // YUV 4:2:0 with swapped U/V planes
148+
149+
Unknown
150+
};
151+
152+
// Construction
153+
Mode( int32_t width, int32_t height, const MediaTime& frameRate, Codec codec, PixelFormat pixelFormat, const std::string& description = "" );
154+
Mode(); // Default constructor
155+
156+
// Core properties
157+
const ivec2& getSize() const { return mSize; }
158+
int32_t getWidth() const { return mSize.x; }
159+
int32_t getHeight() const { return mSize.y; }
160+
const MediaTime& getFrameRate() const { return mFrameRate; }
161+
float getFrameRateFloat() const { return static_cast<float>( 1.0 / mFrameRate.getSeconds() ); }
162+
Codec getCodec() const { return mCodec; }
163+
PixelFormat getPixelFormat() const { return mPixelFormat; }
164+
const std::string& getDescription() const { return mDescription; }
165+
166+
// Utility
167+
float getAspectRatio() const { return mSize.x / static_cast<float>( mSize.y ); }
168+
int32_t getPixelCount() const { return mSize.x * mSize.y; }
169+
bool isRGBFormat() const;
170+
bool isYUVFormat() const;
171+
bool isCompressed() const { return mCodec != Codec::Uncompressed; }
172+
173+
// Human-readable representation
174+
std::string getCodecString() const;
175+
std::string getPixelFormatString() const;
176+
177+
// Comparison
178+
bool operator==( const Mode& other ) const;
179+
bool operator!=( const Mode& other ) const { return !( *this == other ); }
180+
bool operator<( const Mode& other ) const;
181+
182+
// Platform-specific data access
183+
const std::string& getPlatformData() const { return mPlatformData; }
184+
void setPlatformData( const std::string& data ) { mPlatformData = data; }
185+
186+
private:
187+
ivec2 mSize;
188+
MediaTime mFrameRate;
189+
Codec mCodec;
190+
PixelFormat mPixelFormat;
191+
std::string mDescription;
192+
std::string mPlatformData;
193+
};
194+
115195
// This is an abstract base class for implementing platform specific devices
116196
class CI_API Device {
117197
public:
@@ -132,32 +212,49 @@ class CI_API Capture {
132212
//! Returns whether device is front-facing. False implies rear-facing.
133213
virtual bool isFrontFacing() const = 0;
134214
#endif
215+
//! Returns supported capture modes for this device
216+
virtual std::vector<Mode> getModes() const = 0;
217+
135218
protected:
136219
Device() {}
137220
std::string mName;
138221
};
139222

140223
protected:
141224
Capture( int32_t width, int32_t height, const DeviceRef device );
225+
Capture( const DeviceRef& device, const Mode& mode );
142226

143227
#if defined( CINDER_MAC ) || defined( CINDER_COCOA_TOUCH_DEVICE )
144228
CaptureImplAvFoundation *mImpl;
145229
#elif defined( CINDER_COCOA_TOUCH_SIMULATOR )
146230
CaptureImplCocoaDummy *mImpl;
147231
#elif defined( CINDER_MSW )
148232
CaptureImplDirectShow *mImpl;
233+
#elif defined( CINDER_LINUX )
234+
CaptureImplGStreamer *mImpl;
149235
#elif defined( CINDER_ANDROID )
150236
CaptureImplJni *mImpl;
151237
#endif
152238
};
153239

154240
class CI_API CaptureExc : public Exception {
241+
public:
242+
CaptureExc() {}
243+
CaptureExc( const std::string &description ) : Exception( description ) {}
155244
};
156245

157246
class CI_API CaptureExcInitFail : public CaptureExc {
247+
public:
248+
CaptureExcInitFail() {}
249+
CaptureExcInitFail( const std::string &description ) : CaptureExc( description ) {}
158250
};
159251

160252
class CI_API CaptureExcInvalidChannelOrder : public CaptureExc {
161253
};
162254

163-
} //namespace cinder
255+
// Stream operators for Mode - enables Cinder's toString() to work
256+
CI_API std::ostream& operator<<( std::ostream& os, const Capture::Mode& mode );
257+
CI_API std::ostream& operator<<( std::ostream& os, const Capture::Mode::Codec& codec );
258+
CI_API std::ostream& operator<<( std::ostream& os, const Capture::Mode::PixelFormat& format );
259+
260+
} //namespace cinder

include/cinder/CaptureImplCocoaDummy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class CaptureImplCocoaDummyDevice : public Capture::Device {
4545
Capture::DeviceIdentifier getUniqueId() const { return mUniqueId; }
4646
bool isFrontFacing() const { return mFrontFacing; }
4747
void* getNative() const { return NULL; }
48+
std::vector<Capture::Mode> getModes() const override;
4849
private:
4950
Capture::DeviceIdentifier mUniqueId;
5051
bool mFrontFacing;

src/cinder/Capture.cpp

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "cinder/Cinder.h"
2424
#include "cinder/Capture.h"
25+
#include <iomanip>
2526
#if defined( CINDER_MAC ) || defined( CINDER_COCOA_TOUCH_DEVICE )
2627
#import "cinder/CaptureImplAvFoundation.h"
2728
typedef CaptureImplAvFoundation CapturePlatformImpl;
@@ -31,6 +32,9 @@
3132
#elif defined( CINDER_MSW )
3233
#include "cinder/CaptureImplDirectShow.h"
3334
typedef cinder::CaptureImplDirectShow CapturePlatformImpl;
35+
#elif defined( CINDER_LINUX )
36+
#include "cinder/CaptureImplGStreamer.h"
37+
typedef cinder::CaptureImplGStreamer CapturePlatformImpl;
3438
#elif defined( CINDER_ANDROID )
3539
#include "cinder/CaptureImplJni.h"
3640
typedef cinder::CaptureImplJni CapturePlatformImpl;
@@ -42,6 +46,156 @@ using namespace std;
4246

4347
namespace cinder {
4448

49+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
50+
// Capture::Mode
51+
52+
Capture::Mode::Mode()
53+
: mSize( 0, 0 ), mFrameRate( MediaTime() ), mCodec( Codec::Unknown ), mPixelFormat( PixelFormat::Unknown )
54+
{
55+
}
56+
57+
Capture::Mode::Mode( int32_t width, int32_t height, const MediaTime& frameRate, Codec codec, PixelFormat pixelFormat, const std::string& description )
58+
: mSize( width, height ), mFrameRate( frameRate ), mCodec( codec ), mPixelFormat( pixelFormat ), mDescription( description )
59+
{
60+
}
61+
62+
bool Capture::Mode::isRGBFormat() const
63+
{
64+
switch( mPixelFormat ) {
65+
case PixelFormat::RGB24:
66+
case PixelFormat::BGR24:
67+
case PixelFormat::ARGB32:
68+
case PixelFormat::BGRA32:
69+
return true;
70+
default:
71+
return false;
72+
}
73+
}
74+
75+
bool Capture::Mode::isYUVFormat() const
76+
{
77+
switch( mPixelFormat ) {
78+
case PixelFormat::YUV420P:
79+
case PixelFormat::NV12:
80+
case PixelFormat::YUY2:
81+
case PixelFormat::UYVY:
82+
case PixelFormat::I420:
83+
case PixelFormat::YV12:
84+
return true;
85+
default:
86+
return false;
87+
}
88+
}
89+
90+
std::string Capture::Mode::getCodecString() const
91+
{
92+
switch( mCodec ) {
93+
case Codec::Uncompressed: return "Uncompressed";
94+
case Codec::JPEG: return "JPEG";
95+
case Codec::H264: return "H264";
96+
case Codec::HEVC: return "HEVC";
97+
case Codec::Unknown: return "Unknown";
98+
default: return "Unknown";
99+
}
100+
}
101+
102+
std::string Capture::Mode::getPixelFormatString() const
103+
{
104+
switch( mPixelFormat ) {
105+
case PixelFormat::RGB24: return "RGB24";
106+
case PixelFormat::BGR24: return "BGR24";
107+
case PixelFormat::ARGB32: return "ARGB32";
108+
case PixelFormat::BGRA32: return "BGRA32";
109+
case PixelFormat::YUV420P: return "YUV420P";
110+
case PixelFormat::NV12: return "NV12";
111+
case PixelFormat::YUY2: return "YUY2";
112+
case PixelFormat::UYVY: return "UYVY";
113+
case PixelFormat::I420: return "I420";
114+
case PixelFormat::YV12: return "YV12";
115+
case PixelFormat::Unknown: return "Unknown";
116+
default: return "Unknown";
117+
}
118+
}
119+
120+
bool Capture::Mode::operator==( const Mode& other ) const
121+
{
122+
return mSize == other.mSize &&
123+
mFrameRate == other.mFrameRate &&
124+
mCodec == other.mCodec &&
125+
mPixelFormat == other.mPixelFormat;
126+
}
127+
128+
bool Capture::Mode::operator<( const Mode& other ) const
129+
{
130+
// Sort by resolution (pixel count), then frame rate, then codec, then pixel format
131+
int32_t thisPixels = getPixelCount();
132+
int32_t otherPixels = other.getPixelCount();
133+
134+
if( thisPixels != otherPixels )
135+
return thisPixels < otherPixels;
136+
137+
if( mFrameRate != other.mFrameRate )
138+
return mFrameRate < other.mFrameRate;
139+
140+
if( mCodec != other.mCodec )
141+
return static_cast<int>( mCodec ) < static_cast<int>( other.mCodec );
142+
143+
return static_cast<int>( mPixelFormat ) < static_cast<int>( other.mPixelFormat );
144+
}
145+
146+
// Stream operators
147+
std::ostream& operator<<( std::ostream& os, const Capture::Mode& mode )
148+
{
149+
// Format FPS to 2 decimal places
150+
os << mode.getWidth() << "x" << mode.getHeight()
151+
<< " @";
152+
153+
// Use fixed format with 2 decimal precision for FPS
154+
std::ios_base::fmtflags oldFlags = os.flags();
155+
std::streamsize oldPrecision = os.precision();
156+
os << std::fixed << std::setprecision(2) << mode.getFrameRateFloat();
157+
os.flags(oldFlags);
158+
os.precision(oldPrecision);
159+
160+
os << "fps"
161+
<< " " << mode.getCodecString()
162+
<< " " << mode.getPixelFormatString();
163+
return os;
164+
}
165+
166+
std::ostream& operator<<( std::ostream& os, const Capture::Mode::Codec& codec )
167+
{
168+
switch( codec ) {
169+
case Capture::Mode::Codec::Uncompressed: return os << "Uncompressed";
170+
case Capture::Mode::Codec::JPEG: return os << "JPEG";
171+
case Capture::Mode::Codec::H264: return os << "H264";
172+
case Capture::Mode::Codec::HEVC: return os << "HEVC";
173+
case Capture::Mode::Codec::Unknown: return os << "Unknown";
174+
default: return os << "Unknown";
175+
}
176+
}
177+
178+
std::ostream& operator<<( std::ostream& os, const Capture::Mode::PixelFormat& format )
179+
{
180+
switch( format ) {
181+
case Capture::Mode::PixelFormat::RGB24: return os << "RGB24";
182+
case Capture::Mode::PixelFormat::BGR24: return os << "BGR24";
183+
case Capture::Mode::PixelFormat::ARGB32: return os << "ARGB32";
184+
case Capture::Mode::PixelFormat::BGRA32: return os << "BGRA32";
185+
case Capture::Mode::PixelFormat::YUV420P: return os << "YUV420P";
186+
case Capture::Mode::PixelFormat::NV12: return os << "NV12";
187+
case Capture::Mode::PixelFormat::YUY2: return os << "YUY2";
188+
case Capture::Mode::PixelFormat::UYVY: return os << "UYVY";
189+
case Capture::Mode::PixelFormat::I420: return os << "I420";
190+
case Capture::Mode::PixelFormat::YV12: return os << "YV12";
191+
case Capture::Mode::PixelFormat::Unknown: return os << "Unknown";
192+
default: return os << "Unknown";
193+
}
194+
}
195+
196+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
197+
// Capture
198+
45199
const vector<Capture::DeviceRef>& Capture::getDevices( bool forceRefresh )
46200
{
47201
#if defined( CINDER_COCOA )
@@ -71,7 +225,7 @@ Capture::DeviceRef Capture::findDeviceByNameContains( const string &nameFragment
71225
return DeviceRef();
72226
}
73227

74-
Capture::Capture( int32_t width, int32_t height, const DeviceRef device )
228+
Capture::Capture( int32_t width, int32_t height, const DeviceRef device )
75229
{
76230
#if defined( CINDER_COCOA )
77231
mImpl = [[::CapturePlatformImpl alloc] initWithDevice:device width:width height:height];
@@ -80,6 +234,15 @@ Capture::Capture( int32_t width, int32_t height, const DeviceRef device )
80234
#endif
81235
}
82236

237+
Capture::Capture( const DeviceRef& device, const Mode& mode )
238+
{
239+
#if defined( CINDER_COCOA )
240+
mImpl = [[::CapturePlatformImpl alloc] initWithDevice:device mode:mode];
241+
#else
242+
mImpl = new CapturePlatformImpl( device, mode );
243+
#endif
244+
}
245+
83246
Capture::~Capture()
84247
{
85248
#if defined( CINDER_COCOA )

0 commit comments

Comments
 (0)