New upstream version 24.0.1+dfsg1
This commit is contained in:
parent
b14f9eae6d
commit
5a730d6ec3
842 changed files with 42245 additions and 33385 deletions
|
|
@ -1,6 +1,6 @@
|
|||
CoreAudioAAC="CoreAudio AAC Kodierer"
|
||||
Bitrate="Bitrate"
|
||||
AllowHEAAC="Erlaube HE-AAC"
|
||||
AllowHEAAC="Erlaube HE‐AAC"
|
||||
OutputSamplerate="Ausgabeabtastrate"
|
||||
UseInputSampleRate="Verwenden Sie Eingabe (OBS) Abtastrate (kann nicht unterstützte Bitraten auflisten)"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
CoreAudioAAC="Codificador CoreAudio AAC"
|
||||
Bitrate="Velocidade de bits"
|
||||
CoreAudioAAC="Codificador AAC de CoreAudio"
|
||||
Bitrate="Taxa de bits"
|
||||
AllowHEAAC="Permitir HE-AAC"
|
||||
OutputSamplerate="Taxa de mostra de saída"
|
||||
UseInputSampleRate="Utilizar a taxa de mostra de entrada do OBS (pode listar as taxas de bits que non son compatíbeis)"
|
||||
|
||||
|
|
|
|||
6
plugins/coreaudio-encoder/data/locale/sl-SI.ini
Normal file
6
plugins/coreaudio-encoder/data/locale/sl-SI.ini
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
CoreAudioAAC="Kodirnik AAC CoreAudio"
|
||||
Bitrate="Bitna hitrost"
|
||||
AllowHEAAC="Omogoči HE-AAC"
|
||||
OutputSamplerate="Izhodna hitrost vzorčenja"
|
||||
UseInputSampleRate="Uporabo vhodno hitrost vzorčenja (OBS) (lahko so prikazane nepodprte bitne hitrosti)"
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,18 +5,17 @@
|
|||
|
||||
#include <util/dstr.h>
|
||||
|
||||
typedef unsigned long UInt32;
|
||||
typedef signed long SInt32;
|
||||
typedef signed long long SInt64;
|
||||
typedef double Float64;
|
||||
typedef unsigned long UInt32;
|
||||
typedef signed long SInt32;
|
||||
typedef signed long long SInt64;
|
||||
typedef double Float64;
|
||||
|
||||
typedef SInt32 OSStatus;
|
||||
typedef unsigned char Boolean;
|
||||
typedef SInt32 OSStatus;
|
||||
typedef unsigned char Boolean;
|
||||
|
||||
typedef UInt32 AudioFormatPropertyID;
|
||||
|
||||
enum {
|
||||
kVariableLengthArray = 1
|
||||
enum { kVariableLengthArray = 1,
|
||||
};
|
||||
|
||||
struct OpaqueAudioConverter;
|
||||
|
|
@ -30,344 +29,305 @@ struct AudioValueRange {
|
|||
typedef struct AudioValueRange AudioValueRange;
|
||||
|
||||
struct AudioBuffer {
|
||||
UInt32 mNumberChannels;
|
||||
UInt32 mDataByteSize;
|
||||
void* mData;
|
||||
UInt32 mNumberChannels;
|
||||
UInt32 mDataByteSize;
|
||||
void *mData;
|
||||
};
|
||||
typedef struct AudioBuffer AudioBuffer;
|
||||
|
||||
struct AudioBufferList {
|
||||
UInt32 mNumberBuffers;
|
||||
UInt32 mNumberBuffers;
|
||||
AudioBuffer mBuffers[kVariableLengthArray];
|
||||
};
|
||||
typedef struct AudioBufferList AudioBufferList;
|
||||
|
||||
struct AudioStreamBasicDescription {
|
||||
Float64 mSampleRate;
|
||||
UInt32 mFormatID;
|
||||
UInt32 mFormatFlags;
|
||||
UInt32 mBytesPerPacket;
|
||||
UInt32 mFramesPerPacket;
|
||||
UInt32 mBytesPerFrame;
|
||||
UInt32 mChannelsPerFrame;
|
||||
UInt32 mBitsPerChannel;
|
||||
UInt32 mReserved;
|
||||
UInt32 mFormatID;
|
||||
UInt32 mFormatFlags;
|
||||
UInt32 mBytesPerPacket;
|
||||
UInt32 mFramesPerPacket;
|
||||
UInt32 mBytesPerFrame;
|
||||
UInt32 mChannelsPerFrame;
|
||||
UInt32 mBitsPerChannel;
|
||||
UInt32 mReserved;
|
||||
};
|
||||
typedef struct AudioStreamBasicDescription AudioStreamBasicDescription;
|
||||
|
||||
struct AudioStreamPacketDescription {
|
||||
SInt64 mStartOffset;
|
||||
UInt32 mVariableFramesInPacket;
|
||||
UInt32 mDataByteSize;
|
||||
SInt64 mStartOffset;
|
||||
UInt32 mVariableFramesInPacket;
|
||||
UInt32 mDataByteSize;
|
||||
};
|
||||
typedef struct AudioStreamPacketDescription AudioStreamPacketDescription;
|
||||
|
||||
typedef OSStatus (*AudioConverterComplexInputDataProc) (
|
||||
AudioConverterRef inAudioConverter,
|
||||
UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription **outDataPacketDescription,
|
||||
void *inUserData
|
||||
);
|
||||
typedef OSStatus (*AudioConverterComplexInputDataProc)(
|
||||
AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription **outDataPacketDescription,
|
||||
void *inUserData);
|
||||
|
||||
enum {
|
||||
kAudioCodecPropertyNameCFString = 'lnam',
|
||||
kAudioCodecPropertyManufacturerCFString = 'lmak',
|
||||
kAudioCodecPropertyFormatCFString = 'lfor',
|
||||
//kAudioCodecPropertyHasVariablePacketByteSizes = 'vpk?',
|
||||
kAudioCodecPropertySupportedInputFormats = 'ifm#',
|
||||
kAudioCodecPropertySupportedOutputFormats = 'ofm#',
|
||||
kAudioCodecPropertyAvailableInputSampleRates = 'aisr',
|
||||
kAudioCodecPropertyAvailableOutputSampleRates = 'aosr',
|
||||
kAudioCodecPropertyAvailableBitRateRange = 'abrt',
|
||||
kAudioCodecPropertyMinimumNumberInputPackets = 'mnip',
|
||||
kAudioCodecPropertyMinimumNumberOutputPackets = 'mnop',
|
||||
kAudioCodecPropertyAvailableNumberChannels = 'cmnc',
|
||||
kAudioCodecPropertyDoesSampleRateConversion = 'lmrc',
|
||||
kAudioCodecPropertyAvailableInputChannelLayoutTags = 'aicl',
|
||||
kAudioCodecPropertyAvailableOutputChannelLayoutTags = 'aocl',
|
||||
kAudioCodecPropertyInputFormatsForOutputFormat = 'if4o',
|
||||
kAudioCodecPropertyOutputFormatsForInputFormat = 'of4i',
|
||||
kAudioCodecPropertyFormatInfo = 'acfi',
|
||||
enum { kAudioCodecPropertyNameCFString = 'lnam',
|
||||
kAudioCodecPropertyManufacturerCFString = 'lmak',
|
||||
kAudioCodecPropertyFormatCFString = 'lfor',
|
||||
//kAudioCodecPropertyHasVariablePacketByteSizes = 'vpk?',
|
||||
kAudioCodecPropertySupportedInputFormats = 'ifm#',
|
||||
kAudioCodecPropertySupportedOutputFormats = 'ofm#',
|
||||
kAudioCodecPropertyAvailableInputSampleRates = 'aisr',
|
||||
kAudioCodecPropertyAvailableOutputSampleRates = 'aosr',
|
||||
kAudioCodecPropertyAvailableBitRateRange = 'abrt',
|
||||
kAudioCodecPropertyMinimumNumberInputPackets = 'mnip',
|
||||
kAudioCodecPropertyMinimumNumberOutputPackets = 'mnop',
|
||||
kAudioCodecPropertyAvailableNumberChannels = 'cmnc',
|
||||
kAudioCodecPropertyDoesSampleRateConversion = 'lmrc',
|
||||
kAudioCodecPropertyAvailableInputChannelLayoutTags = 'aicl',
|
||||
kAudioCodecPropertyAvailableOutputChannelLayoutTags = 'aocl',
|
||||
kAudioCodecPropertyInputFormatsForOutputFormat = 'if4o',
|
||||
kAudioCodecPropertyOutputFormatsForInputFormat = 'of4i',
|
||||
kAudioCodecPropertyFormatInfo = 'acfi',
|
||||
};
|
||||
|
||||
enum {
|
||||
kAudioCodecPropertyInputBufferSize = 'tbuf',
|
||||
kAudioCodecPropertyPacketFrameSize = 'pakf',
|
||||
kAudioCodecPropertyMaximumPacketByteSize = 'pakb',
|
||||
kAudioCodecPropertyCurrentInputFormat = 'ifmt',
|
||||
kAudioCodecPropertyCurrentOutputFormat = 'ofmt',
|
||||
kAudioCodecPropertyMagicCookie = 'kuki',
|
||||
kAudioCodecPropertyUsedInputBufferSize = 'ubuf',
|
||||
kAudioCodecPropertyIsInitialized = 'init',
|
||||
kAudioCodecPropertyCurrentTargetBitRate = 'brat',
|
||||
kAudioCodecPropertyCurrentInputSampleRate = 'cisr',
|
||||
kAudioCodecPropertyCurrentOutputSampleRate = 'cosr',
|
||||
kAudioCodecPropertyQualitySetting = 'srcq',
|
||||
kAudioCodecPropertyApplicableBitRateRange = 'brta',
|
||||
kAudioCodecPropertyApplicableInputSampleRates = 'isra',
|
||||
kAudioCodecPropertyApplicableOutputSampleRates = 'osra',
|
||||
kAudioCodecPropertyPaddedZeros = 'pad0',
|
||||
kAudioCodecPropertyPrimeMethod = 'prmm',
|
||||
kAudioCodecPropertyPrimeInfo = 'prim',
|
||||
kAudioCodecPropertyCurrentInputChannelLayout = 'icl ',
|
||||
kAudioCodecPropertyCurrentOutputChannelLayout = 'ocl ',
|
||||
kAudioCodecPropertySettings = 'acs ',
|
||||
kAudioCodecPropertyFormatList = 'acfl',
|
||||
kAudioCodecPropertyBitRateControlMode = 'acbf',
|
||||
kAudioCodecPropertySoundQualityForVBR = 'vbrq',
|
||||
kAudioCodecPropertyMinimumDelayMode = 'mdel'
|
||||
enum { kAudioCodecPropertyInputBufferSize = 'tbuf',
|
||||
kAudioCodecPropertyPacketFrameSize = 'pakf',
|
||||
kAudioCodecPropertyMaximumPacketByteSize = 'pakb',
|
||||
kAudioCodecPropertyCurrentInputFormat = 'ifmt',
|
||||
kAudioCodecPropertyCurrentOutputFormat = 'ofmt',
|
||||
kAudioCodecPropertyMagicCookie = 'kuki',
|
||||
kAudioCodecPropertyUsedInputBufferSize = 'ubuf',
|
||||
kAudioCodecPropertyIsInitialized = 'init',
|
||||
kAudioCodecPropertyCurrentTargetBitRate = 'brat',
|
||||
kAudioCodecPropertyCurrentInputSampleRate = 'cisr',
|
||||
kAudioCodecPropertyCurrentOutputSampleRate = 'cosr',
|
||||
kAudioCodecPropertyQualitySetting = 'srcq',
|
||||
kAudioCodecPropertyApplicableBitRateRange = 'brta',
|
||||
kAudioCodecPropertyApplicableInputSampleRates = 'isra',
|
||||
kAudioCodecPropertyApplicableOutputSampleRates = 'osra',
|
||||
kAudioCodecPropertyPaddedZeros = 'pad0',
|
||||
kAudioCodecPropertyPrimeMethod = 'prmm',
|
||||
kAudioCodecPropertyPrimeInfo = 'prim',
|
||||
kAudioCodecPropertyCurrentInputChannelLayout = 'icl ',
|
||||
kAudioCodecPropertyCurrentOutputChannelLayout = 'ocl ',
|
||||
kAudioCodecPropertySettings = 'acs ',
|
||||
kAudioCodecPropertyFormatList = 'acfl',
|
||||
kAudioCodecPropertyBitRateControlMode = 'acbf',
|
||||
kAudioCodecPropertySoundQualityForVBR = 'vbrq',
|
||||
kAudioCodecPropertyMinimumDelayMode = 'mdel' };
|
||||
|
||||
enum { kAudioCodecBitRateControlMode_Constant = 0,
|
||||
kAudioCodecBitRateControlMode_LongTermAverage = 1,
|
||||
kAudioCodecBitRateControlMode_VariableConstrained = 2,
|
||||
kAudioCodecBitRateControlMode_Variable = 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
kAudioCodecBitRateControlMode_Constant = 0,
|
||||
kAudioCodecBitRateControlMode_LongTermAverage = 1,
|
||||
kAudioCodecBitRateControlMode_VariableConstrained = 2,
|
||||
kAudioCodecBitRateControlMode_Variable = 3,
|
||||
enum { kAudioFormatLinearPCM = 'lpcm',
|
||||
kAudioFormatAC3 = 'ac-3',
|
||||
kAudioFormat60958AC3 = 'cac3',
|
||||
kAudioFormatAppleIMA4 = 'ima4',
|
||||
kAudioFormatMPEG4AAC = 'aac ',
|
||||
kAudioFormatMPEG4CELP = 'celp',
|
||||
kAudioFormatMPEG4HVXC = 'hvxc',
|
||||
kAudioFormatMPEG4TwinVQ = 'twvq',
|
||||
kAudioFormatMACE3 = 'MAC3',
|
||||
kAudioFormatMACE6 = 'MAC6',
|
||||
kAudioFormatULaw = 'ulaw',
|
||||
kAudioFormatALaw = 'alaw',
|
||||
kAudioFormatQDesign = 'QDMC',
|
||||
kAudioFormatQDesign2 = 'QDM2',
|
||||
kAudioFormatQUALCOMM = 'Qclp',
|
||||
kAudioFormatMPEGLayer1 = '.mp1',
|
||||
kAudioFormatMPEGLayer2 = '.mp2',
|
||||
kAudioFormatMPEGLayer3 = '.mp3',
|
||||
kAudioFormatTimeCode = 'time',
|
||||
kAudioFormatMIDIStream = 'midi',
|
||||
kAudioFormatParameterValueStream = 'apvs',
|
||||
kAudioFormatAppleLossless = 'alac',
|
||||
kAudioFormatMPEG4AAC_HE = 'aach',
|
||||
kAudioFormatMPEG4AAC_LD = 'aacl',
|
||||
kAudioFormatMPEG4AAC_ELD = 'aace',
|
||||
kAudioFormatMPEG4AAC_ELD_SBR = 'aacf',
|
||||
kAudioFormatMPEG4AAC_ELD_V2 = 'aacg',
|
||||
kAudioFormatMPEG4AAC_HE_V2 = 'aacp',
|
||||
kAudioFormatMPEG4AAC_Spatial = 'aacs',
|
||||
kAudioFormatAMR = 'samr',
|
||||
kAudioFormatAudible = 'AUDB',
|
||||
kAudioFormatiLBC = 'ilbc',
|
||||
kAudioFormatDVIIntelIMA = 0x6D730011,
|
||||
kAudioFormatMicrosoftGSM = 0x6D730031,
|
||||
kAudioFormatAES3 = 'aes3',
|
||||
};
|
||||
|
||||
enum {
|
||||
kAudioFormatLinearPCM = 'lpcm',
|
||||
kAudioFormatAC3 = 'ac-3',
|
||||
kAudioFormat60958AC3 = 'cac3',
|
||||
kAudioFormatAppleIMA4 = 'ima4',
|
||||
kAudioFormatMPEG4AAC = 'aac ',
|
||||
kAudioFormatMPEG4CELP = 'celp',
|
||||
kAudioFormatMPEG4HVXC = 'hvxc',
|
||||
kAudioFormatMPEG4TwinVQ = 'twvq',
|
||||
kAudioFormatMACE3 = 'MAC3',
|
||||
kAudioFormatMACE6 = 'MAC6',
|
||||
kAudioFormatULaw = 'ulaw',
|
||||
kAudioFormatALaw = 'alaw',
|
||||
kAudioFormatQDesign = 'QDMC',
|
||||
kAudioFormatQDesign2 = 'QDM2',
|
||||
kAudioFormatQUALCOMM = 'Qclp',
|
||||
kAudioFormatMPEGLayer1 = '.mp1',
|
||||
kAudioFormatMPEGLayer2 = '.mp2',
|
||||
kAudioFormatMPEGLayer3 = '.mp3',
|
||||
kAudioFormatTimeCode = 'time',
|
||||
kAudioFormatMIDIStream = 'midi',
|
||||
kAudioFormatParameterValueStream = 'apvs',
|
||||
kAudioFormatAppleLossless = 'alac',
|
||||
kAudioFormatMPEG4AAC_HE = 'aach',
|
||||
kAudioFormatMPEG4AAC_LD = 'aacl',
|
||||
kAudioFormatMPEG4AAC_ELD = 'aace',
|
||||
kAudioFormatMPEG4AAC_ELD_SBR = 'aacf',
|
||||
kAudioFormatMPEG4AAC_ELD_V2 = 'aacg',
|
||||
kAudioFormatMPEG4AAC_HE_V2 = 'aacp',
|
||||
kAudioFormatMPEG4AAC_Spatial = 'aacs',
|
||||
kAudioFormatAMR = 'samr',
|
||||
kAudioFormatAudible = 'AUDB',
|
||||
kAudioFormatiLBC = 'ilbc',
|
||||
kAudioFormatDVIIntelIMA = 0x6D730011,
|
||||
kAudioFormatMicrosoftGSM = 0x6D730031,
|
||||
kAudioFormatAES3 = 'aes3'
|
||||
enum { kAudioFormatFlagIsFloat = (1L << 0),
|
||||
kAudioFormatFlagIsBigEndian = (1L << 1),
|
||||
kAudioFormatFlagIsSignedInteger = (1L << 2),
|
||||
kAudioFormatFlagIsPacked = (1L << 3),
|
||||
kAudioFormatFlagIsAlignedHigh = (1L << 4),
|
||||
kAudioFormatFlagIsNonInterleaved = (1L << 5),
|
||||
kAudioFormatFlagIsNonMixable = (1L << 6),
|
||||
kAudioFormatFlagsAreAllClear = (1L << 31),
|
||||
|
||||
kLinearPCMFormatFlagIsFloat = kAudioFormatFlagIsFloat,
|
||||
kLinearPCMFormatFlagIsBigEndian = kAudioFormatFlagIsBigEndian,
|
||||
kLinearPCMFormatFlagIsSignedInteger = kAudioFormatFlagIsSignedInteger,
|
||||
kLinearPCMFormatFlagIsPacked = kAudioFormatFlagIsPacked,
|
||||
kLinearPCMFormatFlagIsAlignedHigh = kAudioFormatFlagIsAlignedHigh,
|
||||
kLinearPCMFormatFlagIsNonInterleaved = kAudioFormatFlagIsNonInterleaved,
|
||||
kLinearPCMFormatFlagIsNonMixable = kAudioFormatFlagIsNonMixable,
|
||||
kLinearPCMFormatFlagsAreAllClear = kAudioFormatFlagsAreAllClear,
|
||||
|
||||
kAppleLosslessFormatFlag_16BitSourceData = 1,
|
||||
kAppleLosslessFormatFlag_20BitSourceData = 2,
|
||||
kAppleLosslessFormatFlag_24BitSourceData = 3,
|
||||
kAppleLosslessFormatFlag_32BitSourceData = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
kAudioFormatFlagIsFloat = (1L << 0),
|
||||
kAudioFormatFlagIsBigEndian = (1L << 1),
|
||||
kAudioFormatFlagIsSignedInteger = (1L << 2),
|
||||
kAudioFormatFlagIsPacked = (1L << 3),
|
||||
kAudioFormatFlagIsAlignedHigh = (1L << 4),
|
||||
kAudioFormatFlagIsNonInterleaved = (1L << 5),
|
||||
kAudioFormatFlagIsNonMixable = (1L << 6),
|
||||
kAudioFormatFlagsAreAllClear = (1L << 31),
|
||||
|
||||
kLinearPCMFormatFlagIsFloat =
|
||||
kAudioFormatFlagIsFloat,
|
||||
kLinearPCMFormatFlagIsBigEndian =
|
||||
kAudioFormatFlagIsBigEndian,
|
||||
kLinearPCMFormatFlagIsSignedInteger =
|
||||
kAudioFormatFlagIsSignedInteger,
|
||||
kLinearPCMFormatFlagIsPacked =
|
||||
kAudioFormatFlagIsPacked,
|
||||
kLinearPCMFormatFlagIsAlignedHigh =
|
||||
kAudioFormatFlagIsAlignedHigh,
|
||||
kLinearPCMFormatFlagIsNonInterleaved =
|
||||
kAudioFormatFlagIsNonInterleaved,
|
||||
kLinearPCMFormatFlagIsNonMixable =
|
||||
kAudioFormatFlagIsNonMixable,
|
||||
kLinearPCMFormatFlagsAreAllClear =
|
||||
kAudioFormatFlagsAreAllClear,
|
||||
|
||||
kAppleLosslessFormatFlag_16BitSourceData = 1,
|
||||
kAppleLosslessFormatFlag_20BitSourceData = 2,
|
||||
kAppleLosslessFormatFlag_24BitSourceData = 3,
|
||||
kAppleLosslessFormatFlag_32BitSourceData = 4
|
||||
};
|
||||
|
||||
enum {
|
||||
kAudioFormatFlagsNativeEndian = 0,
|
||||
};
|
||||
enum { kAudioFormatFlagsNativeEndian = 0 };
|
||||
|
||||
enum {
|
||||
// AudioStreamBasicDescription structure properties
|
||||
kAudioFormatProperty_FormatInfo = 'fmti',
|
||||
kAudioFormatProperty_FormatName = 'fnam',
|
||||
kAudioFormatProperty_EncodeFormatIDs = 'acof',
|
||||
kAudioFormatProperty_DecodeFormatIDs = 'acif',
|
||||
kAudioFormatProperty_FormatList = 'flst',
|
||||
kAudioFormatProperty_ASBDFromESDS = 'essd',
|
||||
kAudioFormatProperty_ChannelLayoutFromESDS = 'escl',
|
||||
kAudioFormatProperty_OutputFormatList = 'ofls',
|
||||
kAudioFormatProperty_Encoders = 'aven',
|
||||
kAudioFormatProperty_Decoders = 'avde',
|
||||
kAudioFormatProperty_FormatIsVBR = 'fvbr',
|
||||
kAudioFormatProperty_FormatIsExternallyFramed = 'fexf',
|
||||
kAudioFormatProperty_AvailableEncodeBitRates = 'aebr',
|
||||
kAudioFormatProperty_AvailableEncodeSampleRates = 'aesr',
|
||||
kAudioFormatProperty_FormatInfo = 'fmti',
|
||||
kAudioFormatProperty_FormatName = 'fnam',
|
||||
kAudioFormatProperty_EncodeFormatIDs = 'acof',
|
||||
kAudioFormatProperty_DecodeFormatIDs = 'acif',
|
||||
kAudioFormatProperty_FormatList = 'flst',
|
||||
kAudioFormatProperty_ASBDFromESDS = 'essd',
|
||||
kAudioFormatProperty_ChannelLayoutFromESDS = 'escl',
|
||||
kAudioFormatProperty_OutputFormatList = 'ofls',
|
||||
kAudioFormatProperty_Encoders = 'aven',
|
||||
kAudioFormatProperty_Decoders = 'avde',
|
||||
kAudioFormatProperty_FormatIsVBR = 'fvbr',
|
||||
kAudioFormatProperty_FormatIsExternallyFramed = 'fexf',
|
||||
kAudioFormatProperty_AvailableEncodeBitRates = 'aebr',
|
||||
kAudioFormatProperty_AvailableEncodeSampleRates = 'aesr',
|
||||
kAudioFormatProperty_AvailableEncodeChannelLayoutTags = 'aecl',
|
||||
kAudioFormatProperty_AvailableEncodeNumberChannels = 'avnc',
|
||||
kAudioFormatProperty_ASBDFromMPEGPacket = 'admp',
|
||||
kAudioFormatProperty_AvailableEncodeNumberChannels = 'avnc',
|
||||
kAudioFormatProperty_ASBDFromMPEGPacket = 'admp',
|
||||
//
|
||||
// AudioChannelLayout structure properties
|
||||
kAudioFormatProperty_BitmapForLayoutTag = 'bmtg',
|
||||
kAudioFormatProperty_MatrixMixMap = 'mmap',
|
||||
kAudioFormatProperty_ChannelMap = 'chmp',
|
||||
kAudioFormatProperty_NumberOfChannelsForLayout = 'nchm',
|
||||
kAudioFormatProperty_ValidateChannelLayout = 'vacl',
|
||||
kAudioFormatProperty_ChannelLayoutForTag = 'cmpl',
|
||||
kAudioFormatProperty_TagForChannelLayout = 'cmpt',
|
||||
kAudioFormatProperty_ChannelLayoutName = 'lonm',
|
||||
kAudioFormatProperty_ChannelLayoutSimpleName = 'lsnm',
|
||||
kAudioFormatProperty_ChannelLayoutForBitmap = 'cmpb',
|
||||
kAudioFormatProperty_ChannelName = 'cnam',
|
||||
kAudioFormatProperty_ChannelShortName = 'csnm',
|
||||
kAudioFormatProperty_TagsForNumberOfChannels = 'tagc',
|
||||
kAudioFormatProperty_PanningMatrix = 'panm',
|
||||
kAudioFormatProperty_BalanceFade = 'balf',
|
||||
kAudioFormatProperty_BitmapForLayoutTag = 'bmtg',
|
||||
kAudioFormatProperty_MatrixMixMap = 'mmap',
|
||||
kAudioFormatProperty_ChannelMap = 'chmp',
|
||||
kAudioFormatProperty_NumberOfChannelsForLayout = 'nchm',
|
||||
kAudioFormatProperty_ValidateChannelLayout = 'vacl',
|
||||
kAudioFormatProperty_ChannelLayoutForTag = 'cmpl',
|
||||
kAudioFormatProperty_TagForChannelLayout = 'cmpt',
|
||||
kAudioFormatProperty_ChannelLayoutName = 'lonm',
|
||||
kAudioFormatProperty_ChannelLayoutSimpleName = 'lsnm',
|
||||
kAudioFormatProperty_ChannelLayoutForBitmap = 'cmpb',
|
||||
kAudioFormatProperty_ChannelName = 'cnam',
|
||||
kAudioFormatProperty_ChannelShortName = 'csnm',
|
||||
kAudioFormatProperty_TagsForNumberOfChannels = 'tagc',
|
||||
kAudioFormatProperty_PanningMatrix = 'panm',
|
||||
kAudioFormatProperty_BalanceFade = 'balf',
|
||||
//
|
||||
// ID3 tag (MP3 metadata) properties
|
||||
kAudioFormatProperty_ID3TagSize = 'id3s',
|
||||
kAudioFormatProperty_ID3TagToDictionary = 'id3d'
|
||||
kAudioFormatProperty_ID3TagSize = 'id3s',
|
||||
kAudioFormatProperty_ID3TagToDictionary = 'id3d',
|
||||
};
|
||||
|
||||
enum {
|
||||
kAudioConverterPropertyMinimumInputBufferSize = 'mibs',
|
||||
kAudioConverterPropertyMinimumOutputBufferSize = 'mobs',
|
||||
kAudioConverterPropertyMaximumInputBufferSize = 'xibs',
|
||||
kAudioConverterPropertyMaximumInputPacketSize = 'xips',
|
||||
kAudioConverterPropertyMaximumOutputPacketSize = 'xops',
|
||||
kAudioConverterPropertyCalculateInputBufferSize = 'cibs',
|
||||
kAudioConverterPropertyCalculateOutputBufferSize = 'cobs',
|
||||
kAudioConverterPropertyInputCodecParameters = 'icdp',
|
||||
kAudioConverterPropertyOutputCodecParameters = 'ocdp',
|
||||
kAudioConverterSampleRateConverterAlgorithm = 'srci',
|
||||
kAudioConverterSampleRateConverterComplexity = 'srca',
|
||||
kAudioConverterSampleRateConverterQuality = 'srcq',
|
||||
kAudioConverterSampleRateConverterInitialPhase = 'srcp',
|
||||
kAudioConverterCodecQuality = 'cdqu',
|
||||
kAudioConverterPrimeMethod = 'prmm',
|
||||
kAudioConverterPrimeInfo = 'prim',
|
||||
kAudioConverterChannelMap = 'chmp',
|
||||
kAudioConverterDecompressionMagicCookie = 'dmgc',
|
||||
kAudioConverterCompressionMagicCookie = 'cmgc',
|
||||
kAudioConverterEncodeBitRate = 'brat',
|
||||
kAudioConverterEncodeAdjustableSampleRate = 'ajsr',
|
||||
kAudioConverterInputChannelLayout = 'icl ',
|
||||
kAudioConverterOutputChannelLayout = 'ocl ',
|
||||
kAudioConverterApplicableEncodeBitRates = 'aebr',
|
||||
kAudioConverterAvailableEncodeBitRates = 'vebr',
|
||||
kAudioConverterApplicableEncodeSampleRates = 'aesr',
|
||||
kAudioConverterAvailableEncodeSampleRates = 'vesr',
|
||||
kAudioConverterAvailableEncodeChannelLayoutTags = 'aecl',
|
||||
kAudioConverterCurrentOutputStreamDescription = 'acod',
|
||||
kAudioConverterCurrentInputStreamDescription = 'acid',
|
||||
kAudioConverterPropertySettings = 'acps',
|
||||
kAudioConverterPropertyBitDepthHint = 'acbd',
|
||||
kAudioConverterPropertyFormatList = 'flst',
|
||||
enum { kAudioConverterPropertyMinimumInputBufferSize = 'mibs',
|
||||
kAudioConverterPropertyMinimumOutputBufferSize = 'mobs',
|
||||
kAudioConverterPropertyMaximumInputBufferSize = 'xibs',
|
||||
kAudioConverterPropertyMaximumInputPacketSize = 'xips',
|
||||
kAudioConverterPropertyMaximumOutputPacketSize = 'xops',
|
||||
kAudioConverterPropertyCalculateInputBufferSize = 'cibs',
|
||||
kAudioConverterPropertyCalculateOutputBufferSize = 'cobs',
|
||||
kAudioConverterPropertyInputCodecParameters = 'icdp',
|
||||
kAudioConverterPropertyOutputCodecParameters = 'ocdp',
|
||||
kAudioConverterSampleRateConverterAlgorithm = 'srci',
|
||||
kAudioConverterSampleRateConverterComplexity = 'srca',
|
||||
kAudioConverterSampleRateConverterQuality = 'srcq',
|
||||
kAudioConverterSampleRateConverterInitialPhase = 'srcp',
|
||||
kAudioConverterCodecQuality = 'cdqu',
|
||||
kAudioConverterPrimeMethod = 'prmm',
|
||||
kAudioConverterPrimeInfo = 'prim',
|
||||
kAudioConverterChannelMap = 'chmp',
|
||||
kAudioConverterDecompressionMagicCookie = 'dmgc',
|
||||
kAudioConverterCompressionMagicCookie = 'cmgc',
|
||||
kAudioConverterEncodeBitRate = 'brat',
|
||||
kAudioConverterEncodeAdjustableSampleRate = 'ajsr',
|
||||
kAudioConverterInputChannelLayout = 'icl ',
|
||||
kAudioConverterOutputChannelLayout = 'ocl ',
|
||||
kAudioConverterApplicableEncodeBitRates = 'aebr',
|
||||
kAudioConverterAvailableEncodeBitRates = 'vebr',
|
||||
kAudioConverterApplicableEncodeSampleRates = 'aesr',
|
||||
kAudioConverterAvailableEncodeSampleRates = 'vesr',
|
||||
kAudioConverterAvailableEncodeChannelLayoutTags = 'aecl',
|
||||
kAudioConverterCurrentOutputStreamDescription = 'acod',
|
||||
kAudioConverterCurrentInputStreamDescription = 'acid',
|
||||
kAudioConverterPropertySettings = 'acps',
|
||||
kAudioConverterPropertyBitDepthHint = 'acbd',
|
||||
kAudioConverterPropertyFormatList = 'flst',
|
||||
};
|
||||
|
||||
enum {
|
||||
kAudioConverterQuality_Max = 0x7F,
|
||||
kAudioConverterQuality_High = 0x60,
|
||||
kAudioConverterQuality_Medium = 0x40,
|
||||
kAudioConverterQuality_Low = 0x20,
|
||||
kAudioConverterQuality_Min = 0
|
||||
enum { kAudioConverterQuality_Max = 0x7F,
|
||||
kAudioConverterQuality_High = 0x60,
|
||||
kAudioConverterQuality_Medium = 0x40,
|
||||
kAudioConverterQuality_Low = 0x20,
|
||||
kAudioConverterQuality_Min = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
kAudio_UnimplementedError = -4,
|
||||
kAudio_FileNotFoundError = -43,
|
||||
kAudio_FilePermissionError = -54,
|
||||
kAudio_TooManyFilesOpenError = -42,
|
||||
kAudio_BadFilePathError = '!pth', // 0x21707468, 561017960
|
||||
kAudio_ParamError = -50,
|
||||
kAudio_MemFullError = -108,
|
||||
enum { kAudio_UnimplementedError = -4,
|
||||
kAudio_FileNotFoundError = -43,
|
||||
kAudio_FilePermissionError = -54,
|
||||
kAudio_TooManyFilesOpenError = -42,
|
||||
kAudio_BadFilePathError = '!pth', // 0x21707468, 561017960
|
||||
kAudio_ParamError = -50,
|
||||
kAudio_MemFullError = -108,
|
||||
|
||||
kAudioConverterErr_FormatNotSupported = 'fmt?',
|
||||
kAudioConverterErr_OperationNotSupported = 0x6F703F3F,
|
||||
// 'op??', integer used because of trigraph
|
||||
kAudioConverterErr_PropertyNotSupported = 'prop',
|
||||
kAudioConverterErr_InvalidInputSize = 'insz',
|
||||
kAudioConverterErr_InvalidOutputSize = 'otsz',
|
||||
// e.g. byte size is not a multiple of the frame size
|
||||
kAudioConverterErr_UnspecifiedError = 'what',
|
||||
kAudioConverterErr_BadPropertySizeError = '!siz',
|
||||
kAudioConverterErr_RequiresPacketDescriptionsError = '!pkd',
|
||||
kAudioConverterErr_InputSampleRateOutOfRange = '!isr',
|
||||
kAudioConverterErr_OutputSampleRateOutOfRange = '!osr'
|
||||
kAudioConverterErr_FormatNotSupported = 'fmt?',
|
||||
kAudioConverterErr_OperationNotSupported = 0x6F703F3F,
|
||||
// 'op??', integer used because of trigraph
|
||||
kAudioConverterErr_PropertyNotSupported = 'prop',
|
||||
kAudioConverterErr_InvalidInputSize = 'insz',
|
||||
kAudioConverterErr_InvalidOutputSize = 'otsz',
|
||||
// e.g. byte size is not a multiple of the frame size
|
||||
kAudioConverterErr_UnspecifiedError = 'what',
|
||||
kAudioConverterErr_BadPropertySizeError = '!siz',
|
||||
kAudioConverterErr_RequiresPacketDescriptionsError = '!pkd',
|
||||
kAudioConverterErr_InputSampleRateOutOfRange = '!isr',
|
||||
kAudioConverterErr_OutputSampleRateOutOfRange = '!osr',
|
||||
};
|
||||
|
||||
typedef OSStatus (*AudioConverterNew_t) (
|
||||
typedef OSStatus (*AudioConverterNew_t)(
|
||||
const AudioStreamBasicDescription *inSourceFormat,
|
||||
const AudioStreamBasicDescription *inDestinationFormat,
|
||||
AudioConverterRef *outAudioConverter
|
||||
);
|
||||
AudioConverterRef *outAudioConverter);
|
||||
|
||||
typedef OSStatus (*AudioConverterDispose_t) (
|
||||
AudioConverterRef inAudioConverter
|
||||
);
|
||||
typedef OSStatus (*AudioConverterDispose_t)(AudioConverterRef inAudioConverter);
|
||||
|
||||
typedef OSStatus (*AudioConverterReset_t) (
|
||||
AudioConverterRef inAudioConverter
|
||||
);
|
||||
typedef OSStatus (*AudioConverterReset_t)(AudioConverterRef inAudioConverter);
|
||||
|
||||
typedef OSStatus (*AudioConverterGetProperty_t) (
|
||||
AudioConverterRef inAudioConverter,
|
||||
AudioConverterPropertyID inPropertyID,
|
||||
UInt32 *ioPropertyDataSize,
|
||||
void *outPropertyData
|
||||
);
|
||||
typedef OSStatus (*AudioConverterGetProperty_t)(
|
||||
AudioConverterRef inAudioConverter,
|
||||
AudioConverterPropertyID inPropertyID, UInt32 *ioPropertyDataSize,
|
||||
void *outPropertyData);
|
||||
|
||||
typedef OSStatus (*AudioConverterGetPropertyInfo_t) (
|
||||
AudioConverterRef inAudioConverter,
|
||||
AudioConverterPropertyID inPropertyID,
|
||||
UInt32 *outSize,
|
||||
Boolean *outWritable
|
||||
);
|
||||
typedef OSStatus (*AudioConverterGetPropertyInfo_t)(
|
||||
AudioConverterRef inAudioConverter,
|
||||
AudioConverterPropertyID inPropertyID, UInt32 *outSize,
|
||||
Boolean *outWritable);
|
||||
|
||||
typedef OSStatus (*AudioConverterSetProperty_t) (
|
||||
AudioConverterRef inAudioConverter,
|
||||
AudioConverterPropertyID inPropertyID,
|
||||
UInt32 inPropertyDataSize,
|
||||
const void *inPropertyData
|
||||
);
|
||||
typedef OSStatus (*AudioConverterSetProperty_t)(
|
||||
AudioConverterRef inAudioConverter,
|
||||
AudioConverterPropertyID inPropertyID, UInt32 inPropertyDataSize,
|
||||
const void *inPropertyData);
|
||||
|
||||
typedef OSStatus (*AudioConverterFillComplexBuffer_t) (
|
||||
AudioConverterRef inAudioConverter,
|
||||
typedef OSStatus (*AudioConverterFillComplexBuffer_t)(
|
||||
AudioConverterRef inAudioConverter,
|
||||
AudioConverterComplexInputDataProc inInputDataProc,
|
||||
void *inInputDataProcUserData,
|
||||
UInt32 *ioOutputDataPacketSize,
|
||||
AudioBufferList *outOutputData,
|
||||
AudioStreamPacketDescription *outPacketDescription
|
||||
);
|
||||
void *inInputDataProcUserData, UInt32 *ioOutputDataPacketSize,
|
||||
AudioBufferList *outOutputData,
|
||||
AudioStreamPacketDescription *outPacketDescription);
|
||||
|
||||
typedef OSStatus (*AudioFormatGetProperty_t) (
|
||||
AudioFormatPropertyID inPropertyID,
|
||||
UInt32 inSpecifierSize,
|
||||
const void *inSpecifier,
|
||||
UInt32 *ioPropertyDataSize,
|
||||
void *outPropertyData
|
||||
);
|
||||
typedef OSStatus (*AudioFormatGetProperty_t)(AudioFormatPropertyID inPropertyID,
|
||||
UInt32 inSpecifierSize,
|
||||
const void *inSpecifier,
|
||||
UInt32 *ioPropertyDataSize,
|
||||
void *outPropertyData);
|
||||
|
||||
typedef OSStatus (*AudioFormatGetPropertyInfo_t) (
|
||||
AudioFormatPropertyID inPropertyID,
|
||||
UInt32 inSpecifierSize,
|
||||
const void *inSpecifier,
|
||||
UInt32 *outPropertyDataSize
|
||||
);
|
||||
typedef OSStatus (*AudioFormatGetPropertyInfo_t)(
|
||||
AudioFormatPropertyID inPropertyID, UInt32 inSpecifierSize,
|
||||
const void *inSpecifier, UInt32 *outPropertyDataSize);
|
||||
|
||||
static AudioConverterNew_t AudioConverterNew = NULL;
|
||||
static AudioConverterDispose_t AudioConverterDispose = NULL;
|
||||
|
|
@ -383,9 +343,10 @@ static HMODULE audio_toolbox = NULL;
|
|||
|
||||
static void release_lib(void)
|
||||
{
|
||||
#define RELEASE_LIB(x) if (x) { \
|
||||
#define RELEASE_LIB(x) \
|
||||
if (x) { \
|
||||
FreeLibrary(x); \
|
||||
x = NULL; \
|
||||
x = NULL; \
|
||||
}
|
||||
|
||||
RELEASE_LIB(audio_toolbox);
|
||||
|
|
@ -396,12 +357,12 @@ static bool load_lib(void)
|
|||
{
|
||||
PWSTR common_path;
|
||||
if (SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, NULL,
|
||||
&common_path) != S_OK) {
|
||||
&common_path) != S_OK) {
|
||||
CA_LOG(LOG_WARNING, "Could not retrieve common files path");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dstr path = { 0 };
|
||||
struct dstr path = {0};
|
||||
dstr_printf(&path, "%S\\Apple\\Apple Application Support", common_path);
|
||||
CoTaskMemFree(common_path);
|
||||
|
||||
|
|
@ -411,8 +372,9 @@ static bool load_lib(void)
|
|||
SetDllDirectory(w_path);
|
||||
bfree(w_path);
|
||||
|
||||
#define LOAD_LIB(x, n) x = LoadLibrary(TEXT(n)); \
|
||||
if (!x) \
|
||||
#define LOAD_LIB(x, n) \
|
||||
x = LoadLibrary(TEXT(n)); \
|
||||
if (!x) \
|
||||
CA_LOG(LOG_DEBUG, "Failed loading library '" n "'");
|
||||
|
||||
LOAD_LIB(audio_toolbox, "CoreAudioToolbox.dll");
|
||||
|
|
@ -444,19 +406,20 @@ static void unload_core_audio(void)
|
|||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4706)
|
||||
#pragma warning(disable : 4706)
|
||||
#endif
|
||||
static bool load_core_audio(void)
|
||||
{
|
||||
if (!load_lib())
|
||||
return false;
|
||||
|
||||
#define LOAD_SYM_FROM_LIB(sym, lib, dll) \
|
||||
if (!(sym = (sym ## _t)GetProcAddress(lib, #sym))) { \
|
||||
DWORD err = GetLastError(); \
|
||||
CA_LOG(LOG_ERROR, "Couldn't load " #sym " from " \
|
||||
dll ": %lu (0x%lx)", err, err); \
|
||||
goto unload_everything; \
|
||||
#define LOAD_SYM_FROM_LIB(sym, lib, dll) \
|
||||
if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) { \
|
||||
DWORD err = GetLastError(); \
|
||||
CA_LOG(LOG_ERROR, \
|
||||
"Couldn't load " #sym " from " dll ": %lu (0x%lx)", \
|
||||
err, err); \
|
||||
goto unload_everything; \
|
||||
}
|
||||
|
||||
#define LOAD_SYM(sym) \
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "DecklinkBase.h"
|
||||
|
||||
DecklinkBase::DecklinkBase(DeckLinkDeviceDiscovery *discovery_)
|
||||
: discovery(discovery_)
|
||||
: discovery(discovery_)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -10,11 +10,9 @@ DeckLinkDevice *DecklinkBase::GetDevice() const
|
|||
return instance ? instance->GetDevice() : nullptr;
|
||||
}
|
||||
|
||||
bool DecklinkBase::Activate(DeckLinkDevice*, long long)
|
||||
bool DecklinkBase::Activate(DeckLinkDevice *, long long)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void DecklinkBase::Deactivate()
|
||||
{
|
||||
}
|
||||
void DecklinkBase::Deactivate() {}
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@ class DecklinkBase {
|
|||
|
||||
protected:
|
||||
DecklinkBase(DeckLinkDeviceDiscovery *discovery_);
|
||||
ComPtr<DeckLinkDeviceInstance> instance;
|
||||
DeckLinkDeviceDiscovery *discovery;
|
||||
std::recursive_mutex deviceMutex;
|
||||
volatile long activateRefs = 0;
|
||||
BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
|
||||
video_colorspace colorSpace = VIDEO_CS_DEFAULT;
|
||||
video_range_type colorRange = VIDEO_RANGE_DEFAULT;
|
||||
speaker_layout channelFormat = SPEAKERS_STEREO;
|
||||
ComPtr<DeckLinkDeviceInstance> instance;
|
||||
DeckLinkDeviceDiscovery *discovery;
|
||||
std::recursive_mutex deviceMutex;
|
||||
volatile long activateRefs = 0;
|
||||
BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
|
||||
video_colorspace colorSpace = VIDEO_CS_DEFAULT;
|
||||
video_range_type colorRange = VIDEO_RANGE_DEFAULT;
|
||||
speaker_layout channelFormat = SPEAKERS_STEREO;
|
||||
|
||||
public:
|
||||
virtual bool Activate(DeckLinkDevice *device, long long modeId);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
#include <util/threading.h>
|
||||
|
||||
DeckLinkInput::DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_)
|
||||
: DecklinkBase(discovery_),
|
||||
source(source)
|
||||
DeckLinkInput::DeckLinkInput(obs_source_t *source,
|
||||
DeckLinkDeviceDiscovery *discovery_)
|
||||
: DecklinkBase(discovery_), source(source)
|
||||
{
|
||||
discovery->AddCallback(DeckLinkInput::DevicesChanged, this);
|
||||
}
|
||||
|
|
@ -15,9 +15,10 @@ DeckLinkInput::~DeckLinkInput(void)
|
|||
Deactivate();
|
||||
}
|
||||
|
||||
void DeckLinkInput::DevicesChanged(void *param, DeckLinkDevice *device, bool added)
|
||||
void DeckLinkInput::DevicesChanged(void *param, DeckLinkDevice *device,
|
||||
bool added)
|
||||
{
|
||||
DeckLinkInput *decklink = reinterpret_cast<DeckLinkInput*>(param);
|
||||
DeckLinkInput *decklink = reinterpret_cast<DeckLinkInput *>(param);
|
||||
std::lock_guard<std::recursive_mutex> lock(decklink->deviceMutex);
|
||||
|
||||
obs_source_update_properties(decklink->source);
|
||||
|
|
@ -31,15 +32,18 @@ void DeckLinkInput::DevicesChanged(void *param, DeckLinkDevice *device, bool add
|
|||
|
||||
settings = obs_source_get_settings(decklink->source);
|
||||
hash = obs_data_get_string(settings, "device_hash");
|
||||
videoConnection = (BMDVideoConnection) obs_data_get_int(settings, "video_connection");
|
||||
audioConnection = (BMDAudioConnection) obs_data_get_int(settings, "audio_connection");
|
||||
videoConnection = (BMDVideoConnection)obs_data_get_int(
|
||||
settings, "video_connection");
|
||||
audioConnection = (BMDAudioConnection)obs_data_get_int(
|
||||
settings, "audio_connection");
|
||||
mode = obs_data_get_int(settings, "mode_id");
|
||||
obs_data_release(settings);
|
||||
|
||||
if (device->GetHash().compare(hash) == 0) {
|
||||
if (!decklink->activateRefs)
|
||||
return;
|
||||
if (decklink->Activate(device, mode, videoConnection, audioConnection))
|
||||
if (decklink->Activate(device, mode, videoConnection,
|
||||
audioConnection))
|
||||
os_atomic_dec_long(&decklink->activateRefs);
|
||||
}
|
||||
|
||||
|
|
@ -52,8 +56,8 @@ void DeckLinkInput::DevicesChanged(void *param, DeckLinkDevice *device, bool add
|
|||
}
|
||||
|
||||
bool DeckLinkInput::Activate(DeckLinkDevice *device, long long modeId,
|
||||
BMDVideoConnection bmdVideoConnection,
|
||||
BMDAudioConnection bmdAudioConnection)
|
||||
BMDVideoConnection bmdVideoConnection,
|
||||
BMDAudioConnection bmdAudioConnection)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(deviceMutex);
|
||||
DeckLinkDevice *curDevice = GetDevice();
|
||||
|
|
@ -85,7 +89,8 @@ bool DeckLinkInput::Activate(DeckLinkDevice *device, long long modeId,
|
|||
return false;
|
||||
|
||||
if (GetDevice() == nullptr) {
|
||||
LOG(LOG_ERROR, "Tried to activate an input with nullptr device.");
|
||||
LOG(LOG_ERROR,
|
||||
"Tried to activate an input with nullptr device.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +100,8 @@ bool DeckLinkInput::Activate(DeckLinkDevice *device, long long modeId,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!instance->StartCapture(mode, bmdVideoConnection, bmdAudioConnection)) {
|
||||
if (!instance->StartCapture(mode, bmdVideoConnection,
|
||||
bmdAudioConnection)) {
|
||||
instance = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -133,10 +139,9 @@ void DeckLinkInput::SaveSettings()
|
|||
|
||||
obs_data_t *settings = obs_source_get_settings(source);
|
||||
|
||||
obs_data_set_string(settings, "device_hash",
|
||||
device->GetHash().c_str());
|
||||
obs_data_set_string(settings, "device_hash", device->GetHash().c_str());
|
||||
obs_data_set_string(settings, "device_name",
|
||||
device->GetDisplayName().c_str());
|
||||
device->GetDisplayName().c_str());
|
||||
obs_data_set_int(settings, "mode_id", instance->GetActiveModeId());
|
||||
obs_data_set_string(settings, "mode_name", mode->GetName().c_str());
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
|
||||
class DeckLinkInput : public DecklinkBase {
|
||||
protected:
|
||||
bool isCapturing = false;
|
||||
obs_source_t *source;
|
||||
bool isCapturing = false;
|
||||
obs_source_t *source;
|
||||
|
||||
void SaveSettings();
|
||||
static void DevicesChanged(void *param, DeckLinkDevice *device,
|
||||
bool added);
|
||||
bool added);
|
||||
|
||||
public:
|
||||
DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery);
|
||||
|
|
@ -18,30 +18,30 @@ public:
|
|||
long long GetActiveModeId(void) const;
|
||||
obs_source_t *GetSource(void) const;
|
||||
|
||||
inline BMDPixelFormat GetPixelFormat() const {return pixelFormat;}
|
||||
inline BMDPixelFormat GetPixelFormat() const { return pixelFormat; }
|
||||
inline void SetPixelFormat(BMDPixelFormat format)
|
||||
{
|
||||
pixelFormat = format;
|
||||
}
|
||||
inline video_colorspace GetColorSpace() const {return colorSpace;}
|
||||
inline video_colorspace GetColorSpace() const { return colorSpace; }
|
||||
inline void SetColorSpace(video_colorspace format)
|
||||
{
|
||||
colorSpace = format;
|
||||
}
|
||||
inline video_range_type GetColorRange() const {return colorRange;}
|
||||
inline video_range_type GetColorRange() const { return colorRange; }
|
||||
inline void SetColorRange(video_range_type format)
|
||||
{
|
||||
colorRange = format;
|
||||
}
|
||||
inline speaker_layout GetChannelFormat() const {return channelFormat;}
|
||||
inline speaker_layout GetChannelFormat() const { return channelFormat; }
|
||||
inline void SetChannelFormat(speaker_layout format)
|
||||
{
|
||||
channelFormat = format;
|
||||
}
|
||||
|
||||
bool Activate(DeckLinkDevice *device, long long modeId,
|
||||
BMDVideoConnection bmdVideoConnection,
|
||||
BMDAudioConnection bmdAudioConnection);
|
||||
BMDVideoConnection bmdVideoConnection,
|
||||
BMDAudioConnection bmdAudioConnection);
|
||||
void Deactivate();
|
||||
bool Capturing();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
#include <util/threading.h>
|
||||
|
||||
DeckLinkOutput::DeckLinkOutput(obs_output_t *output, DeckLinkDeviceDiscovery *discovery_)
|
||||
: DecklinkBase(discovery_),
|
||||
output(output)
|
||||
DeckLinkOutput::DeckLinkOutput(obs_output_t *output,
|
||||
DeckLinkDeviceDiscovery *discovery_)
|
||||
: DecklinkBase(discovery_), output(output)
|
||||
{
|
||||
discovery->AddCallback(DeckLinkOutput::DevicesChanged, this);
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ DeckLinkOutput::~DeckLinkOutput(void)
|
|||
|
||||
void DeckLinkOutput::DevicesChanged(void *param, DeckLinkDevice *device, bool)
|
||||
{
|
||||
auto *decklink = reinterpret_cast<DeckLinkOutput*>(param);
|
||||
auto *decklink = reinterpret_cast<DeckLinkOutput *>(param);
|
||||
std::lock_guard<std::recursive_mutex> lock(decklink->deviceMutex);
|
||||
|
||||
blog(LOG_DEBUG, "%s", device->GetHash().c_str());
|
||||
|
|
@ -35,10 +35,10 @@ bool DeckLinkOutput::Activate(DeckLinkDevice *device, long long modeId)
|
|||
return false;
|
||||
|
||||
if (instance->GetActiveModeId() == modeId &&
|
||||
instance->GetActivePixelFormat() == pixelFormat &&
|
||||
instance->GetActiveColorSpace() == colorSpace &&
|
||||
instance->GetActiveColorRange() == colorRange &&
|
||||
instance->GetActiveChannelFormat() == channelFormat)
|
||||
instance->GetActivePixelFormat() == pixelFormat &&
|
||||
instance->GetActiveColorSpace() == colorSpace &&
|
||||
instance->GetActiveColorRange() == colorRange &&
|
||||
instance->GetActiveChannelFormat() == channelFormat)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +57,6 @@ bool DeckLinkOutput::Activate(DeckLinkDevice *device, long long modeId)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!instance->StartOutput(mode)) {
|
||||
instance = nullptr;
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ protected:
|
|||
int width;
|
||||
int height;
|
||||
|
||||
static void DevicesChanged(void *param, DeckLinkDevice *device, bool added);
|
||||
static void DevicesChanged(void *param, DeckLinkDevice *device,
|
||||
bool added);
|
||||
|
||||
public:
|
||||
const char *deviceHash;
|
||||
|
|
@ -21,7 +22,8 @@ public:
|
|||
size_t audio_size;
|
||||
int keyerMode;
|
||||
|
||||
DeckLinkOutput(obs_output_t *output, DeckLinkDeviceDiscovery *discovery);
|
||||
DeckLinkOutput(obs_output_t *output,
|
||||
DeckLinkDeviceDiscovery *discovery);
|
||||
virtual ~DeckLinkOutput(void);
|
||||
obs_output_t *GetOutput(void) const;
|
||||
bool Activate(DeckLinkDevice *device, long long modeId) override;
|
||||
|
|
|
|||
|
|
@ -2,15 +2,14 @@
|
|||
|
||||
#include <emmintrin.h>
|
||||
|
||||
int check_buffer(struct audio_repack *repack,
|
||||
uint32_t frame_count)
|
||||
int check_buffer(struct audio_repack *repack, uint32_t frame_count)
|
||||
{
|
||||
const uint32_t new_size = frame_count * repack->base_dst_size
|
||||
+ repack->extra_dst_size;
|
||||
const uint32_t new_size =
|
||||
frame_count * repack->base_dst_size + repack->extra_dst_size;
|
||||
|
||||
if (repack->packet_size < new_size) {
|
||||
repack->packet_buffer = brealloc(
|
||||
repack->packet_buffer, new_size);
|
||||
repack->packet_buffer =
|
||||
brealloc(repack->packet_buffer, new_size);
|
||||
if (!repack->packet_buffer)
|
||||
return -1;
|
||||
|
||||
|
|
@ -30,8 +29,8 @@ int check_buffer(struct audio_repack *repack,
|
|||
* | FL | FR | LFE |
|
||||
*/
|
||||
|
||||
int repack_squash(struct audio_repack *repack,
|
||||
const uint8_t *bsrc, uint32_t frame_count)
|
||||
int repack_squash(struct audio_repack *repack, const uint8_t *bsrc,
|
||||
uint32_t frame_count)
|
||||
{
|
||||
if (check_buffer(repack, frame_count) < 0)
|
||||
return -1;
|
||||
|
|
@ -55,8 +54,8 @@ int repack_squash(struct audio_repack *repack,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int repack_squash_swap(struct audio_repack *repack,
|
||||
const uint8_t *bsrc, uint32_t frame_count)
|
||||
int repack_squash_swap(struct audio_repack *repack, const uint8_t *bsrc,
|
||||
uint32_t frame_count)
|
||||
{
|
||||
if (check_buffer(repack, frame_count) < 0)
|
||||
return -1;
|
||||
|
|
@ -66,7 +65,8 @@ int repack_squash_swap(struct audio_repack *repack,
|
|||
uint16_t *dst = (uint16_t *)repack->packet_buffer;
|
||||
while (src != esrc) {
|
||||
__m128i target = _mm_load_si128(src++);
|
||||
__m128i buf = _mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0));
|
||||
__m128i buf =
|
||||
_mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0));
|
||||
_mm_storeu_si128((__m128i *)dst, buf);
|
||||
dst += 8 - squash;
|
||||
}
|
||||
|
|
@ -74,20 +74,20 @@ int repack_squash_swap(struct audio_repack *repack,
|
|||
}
|
||||
|
||||
int audio_repack_init(struct audio_repack *repack,
|
||||
audio_repack_mode_t repack_mode, uint8_t sample_bit)
|
||||
audio_repack_mode_t repack_mode, uint8_t sample_bit)
|
||||
{
|
||||
memset(repack, 0, sizeof(*repack));
|
||||
|
||||
if (sample_bit != 16)
|
||||
return -1;
|
||||
int _audio_repack_ch[8] = { 3, 4, 5, 6, 5, 6, 8, 8 };
|
||||
int _audio_repack_ch[8] = {3, 4, 5, 6, 5, 6, 8, 8};
|
||||
repack->base_src_size = 8 * (16 / 8);
|
||||
repack->base_dst_size = _audio_repack_ch[repack_mode] * (16 / 8);
|
||||
repack->extra_dst_size = 8 - _audio_repack_ch[repack_mode];
|
||||
repack->repack_func = &repack_squash;
|
||||
if (repack_mode == repack_mode_8to5ch_swap ||
|
||||
repack_mode == repack_mode_8to6ch_swap ||
|
||||
repack_mode == repack_mode_8ch_swap)
|
||||
repack_mode == repack_mode_8to6ch_swap ||
|
||||
repack_mode == repack_mode_8ch_swap)
|
||||
repack->repack_func = &repack_squash_swap;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -11,22 +11,22 @@ extern "C" {
|
|||
|
||||
struct audio_repack;
|
||||
|
||||
typedef int (*audio_repack_func_t)(struct audio_repack *,
|
||||
const uint8_t *, uint32_t);
|
||||
typedef int (*audio_repack_func_t)(struct audio_repack *, const uint8_t *,
|
||||
uint32_t);
|
||||
|
||||
struct audio_repack {
|
||||
uint8_t *packet_buffer;
|
||||
uint32_t packet_size;
|
||||
uint8_t *packet_buffer;
|
||||
uint32_t packet_size;
|
||||
|
||||
uint32_t base_src_size;
|
||||
uint32_t base_dst_size;
|
||||
uint32_t extra_dst_size;
|
||||
uint32_t base_src_size;
|
||||
uint32_t base_dst_size;
|
||||
uint32_t extra_dst_size;
|
||||
|
||||
audio_repack_func_t repack_func;
|
||||
};
|
||||
|
||||
enum _audio_repack_mode {
|
||||
repack_mode_8to3ch=0,
|
||||
repack_mode_8to3ch = 0,
|
||||
repack_mode_8to4ch,
|
||||
repack_mode_8to5ch,
|
||||
repack_mode_8to6ch,
|
||||
|
|
@ -39,7 +39,8 @@ enum _audio_repack_mode {
|
|||
typedef enum _audio_repack_mode audio_repack_mode_t;
|
||||
|
||||
extern int audio_repack_init(struct audio_repack *repack,
|
||||
audio_repack_mode_t repack_mode, uint8_t sample_bit);
|
||||
audio_repack_mode_t repack_mode,
|
||||
uint8_t sample_bit);
|
||||
extern void audio_repack_free(struct audio_repack *repack);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -10,16 +10,13 @@ public:
|
|||
{
|
||||
audio_repack_init(&arepack, repack_mode, 16);
|
||||
}
|
||||
inline ~AudioRepacker()
|
||||
{
|
||||
audio_repack_free(&arepack);
|
||||
}
|
||||
inline ~AudioRepacker() { audio_repack_free(&arepack); }
|
||||
|
||||
inline int repack(const uint8_t *src, uint32_t frame_size)
|
||||
{
|
||||
return (*arepack.repack_func)(&arepack, src, frame_size);
|
||||
}
|
||||
|
||||
inline operator struct audio_repack*() {return &arepack;}
|
||||
inline struct audio_repack *operator->() {return &arepack;}
|
||||
inline operator struct audio_repack *() { return &arepack; }
|
||||
inline struct audio_repack *operator->() { return &arepack; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,41 +1,41 @@
|
|||
#define DEVICE_HASH "device_hash"
|
||||
#define DEVICE_NAME "device_name"
|
||||
#define VIDEO_CONNECTION "video_connection"
|
||||
#define AUDIO_CONNECTION "audio_connection"
|
||||
#define MODE_ID "mode_id"
|
||||
#define MODE_NAME "mode_name"
|
||||
#define CHANNEL_FORMAT "channel_format"
|
||||
#define PIXEL_FORMAT "pixel_format"
|
||||
#define COLOR_SPACE "color_space"
|
||||
#define COLOR_RANGE "color_range"
|
||||
#define BUFFERING "buffering"
|
||||
#define DEACTIVATE_WNS "deactivate_when_not_showing"
|
||||
#define AUTO_START "auto_start"
|
||||
#define KEYER "keyer"
|
||||
#define SWAP "swap"
|
||||
#define DEVICE_HASH "device_hash"
|
||||
#define DEVICE_NAME "device_name"
|
||||
#define VIDEO_CONNECTION "video_connection"
|
||||
#define AUDIO_CONNECTION "audio_connection"
|
||||
#define MODE_ID "mode_id"
|
||||
#define MODE_NAME "mode_name"
|
||||
#define CHANNEL_FORMAT "channel_format"
|
||||
#define PIXEL_FORMAT "pixel_format"
|
||||
#define COLOR_SPACE "color_space"
|
||||
#define COLOR_RANGE "color_range"
|
||||
#define BUFFERING "buffering"
|
||||
#define DEACTIVATE_WNS "deactivate_when_not_showing"
|
||||
#define AUTO_START "auto_start"
|
||||
#define KEYER "keyer"
|
||||
#define SWAP "swap"
|
||||
|
||||
#define TEXT_DEVICE obs_module_text("Device")
|
||||
#define TEXT_VIDEO_CONNECTION obs_module_text("VideoConnection")
|
||||
#define TEXT_AUDIO_CONNECTION obs_module_text("AudioConnection")
|
||||
#define TEXT_MODE obs_module_text("Mode")
|
||||
#define TEXT_PIXEL_FORMAT obs_module_text("PixelFormat")
|
||||
#define TEXT_COLOR_SPACE obs_module_text("ColorSpace")
|
||||
#define TEXT_COLOR_SPACE_DEFAULT obs_module_text("ColorSpace.Default")
|
||||
#define TEXT_COLOR_RANGE obs_module_text("ColorRange")
|
||||
#define TEXT_COLOR_RANGE_DEFAULT obs_module_text("ColorRange.Default")
|
||||
#define TEXT_COLOR_RANGE_PARTIAL obs_module_text("ColorRange.Partial")
|
||||
#define TEXT_COLOR_RANGE_FULL obs_module_text("ColorRange.Full")
|
||||
#define TEXT_CHANNEL_FORMAT obs_module_text("ChannelFormat")
|
||||
#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None")
|
||||
#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch")
|
||||
#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch")
|
||||
#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch")
|
||||
#define TEXT_BUFFERING obs_module_text("Buffering")
|
||||
#define TEXT_DWNS obs_module_text("DeactivateWhenNotShowing")
|
||||
#define TEXT_AUTO_START obs_module_text("AutoStart")
|
||||
#define TEXT_ENABLE_KEYER obs_module_text("Keyer")
|
||||
#define TEXT_SWAP obs_module_text("SwapFC-LFE")
|
||||
#define TEXT_SWAP_TOOLTIP obs_module_text("SwapFC-LFE.Tooltip")
|
||||
#define TEXT_DEVICE obs_module_text("Device")
|
||||
#define TEXT_VIDEO_CONNECTION obs_module_text("VideoConnection")
|
||||
#define TEXT_AUDIO_CONNECTION obs_module_text("AudioConnection")
|
||||
#define TEXT_MODE obs_module_text("Mode")
|
||||
#define TEXT_PIXEL_FORMAT obs_module_text("PixelFormat")
|
||||
#define TEXT_COLOR_SPACE obs_module_text("ColorSpace")
|
||||
#define TEXT_COLOR_SPACE_DEFAULT obs_module_text("ColorSpace.Default")
|
||||
#define TEXT_COLOR_RANGE obs_module_text("ColorRange")
|
||||
#define TEXT_COLOR_RANGE_DEFAULT obs_module_text("ColorRange.Default")
|
||||
#define TEXT_COLOR_RANGE_PARTIAL obs_module_text("ColorRange.Partial")
|
||||
#define TEXT_COLOR_RANGE_FULL obs_module_text("ColorRange.Full")
|
||||
#define TEXT_CHANNEL_FORMAT obs_module_text("ChannelFormat")
|
||||
#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None")
|
||||
#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch")
|
||||
#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch")
|
||||
#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch")
|
||||
#define TEXT_BUFFERING obs_module_text("Buffering")
|
||||
#define TEXT_DWNS obs_module_text("DeactivateWhenNotShowing")
|
||||
#define TEXT_AUTO_START obs_module_text("AutoStart")
|
||||
#define TEXT_ENABLE_KEYER obs_module_text("Keyer")
|
||||
#define TEXT_SWAP obs_module_text("SwapFC-LFE")
|
||||
#define TEXT_SWAP_TOOLTIP obs_module_text("SwapFC-LFE.Tooltip")
|
||||
|
|
|
|||
|
|
@ -17,4 +17,6 @@ ChannelFormat.5_1ch="5.1ch"
|
|||
ChannelFormat.7_1ch="7.1ch"
|
||||
DeactivateWhenNotShowing="التعطيل عندما لا يكون ظاهراً"
|
||||
AutoStart="البدء تلقائياً مع التشغيل"
|
||||
VideoConnection="مدخل الفيديو"
|
||||
AudioConnection="مدخل الصوت"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
BlackmagicDevice="Blackmagic-Gerät"
|
||||
BlackmagicDevice="Blackmagic‐Gerät"
|
||||
Device="Gerät"
|
||||
Mode="Modus"
|
||||
Buffering="Puffern benutzen"
|
||||
|
|
@ -11,16 +11,16 @@ ColorRange.Partial="Begrenzt"
|
|||
ColorRange.Full="Voll"
|
||||
ChannelFormat="Kanal"
|
||||
ChannelFormat.None="Keins"
|
||||
ChannelFormat.2_0ch="2 Kanal"
|
||||
ChannelFormat.2_1ch="2.1 Kanal"
|
||||
ChannelFormat.4_0ch="4 Kanal"
|
||||
ChannelFormat.4_1ch="4.1 Kanal"
|
||||
ChannelFormat.5_1ch="5.1 Kanal"
|
||||
ChannelFormat.7_1ch="7.1 Kanal"
|
||||
ChannelFormat.2_0ch="2‐Kanal"
|
||||
ChannelFormat.2_1ch="2.1‐Kanal"
|
||||
ChannelFormat.4_0ch="4‐Kanal"
|
||||
ChannelFormat.4_1ch="4.1‐Kanal"
|
||||
ChannelFormat.5_1ch="5.1‐Kanal"
|
||||
ChannelFormat.7_1ch="7.1‐Kanal"
|
||||
DeactivateWhenNotShowing="Deaktivieren, wenn die Quelle nicht angezeigt wird"
|
||||
AutoStart="Automatisch beim Öffnen starten"
|
||||
SwapFC-LFE="FC und LFE tauschen"
|
||||
SwapFC-LFE.Tooltip="Vorderen Front-Center-Kanal und LFE-Kanal tauschen"
|
||||
SwapFC-LFE.Tooltip="Vorderen Front‐Center‐Kanal und LFE‐Kanal tauschen"
|
||||
VideoConnection="Videoverbindung"
|
||||
AudioConnection="Audioverbindung"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,26 @@
|
|||
BlackmagicDevice="Dispositivo Blackmagic"
|
||||
Device="Dispositivo"
|
||||
Mode="Modo"
|
||||
Buffering="Utilizar o almacenamento no búfer"
|
||||
Buffering="Utilizar o almacenamento na memoria temporal"
|
||||
PixelFormat="Formato do píxel"
|
||||
ColorSpace="Espazo de cor"
|
||||
ColorSpace.Default="Predeterminado"
|
||||
ColorRange="Gama de cores"
|
||||
ColorRange.Default="Predeterminado"
|
||||
ColorRange.Partial="Parcial"
|
||||
ColorRange.Full="Total"
|
||||
ChannelFormat="Canle"
|
||||
ChannelFormat.None="Ningún"
|
||||
ChannelFormat.2_0ch="2ch"
|
||||
ChannelFormat.2_1ch="2.1ch"
|
||||
ChannelFormat.4_0ch="4ch"
|
||||
ChannelFormat.4_1ch="4.1ch"
|
||||
ChannelFormat.5_1ch="5.1ch"
|
||||
ChannelFormat.7_1ch="7.1ch"
|
||||
DeactivateWhenNotShowing="Desactivado cando non se amose"
|
||||
AutoStart="Comezar no inicio"
|
||||
SwapFC-LFE="Intercambiar FC e LFE"
|
||||
SwapFC-LFE.Tooltip="Intercambiar a canle central frontal e a canle LFE"
|
||||
VideoConnection="Conexión de vídeo"
|
||||
AudioConnection="Conexión de son"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,13 @@ BlackmagicDevice="Dispozitiv Blackmagic"
|
|||
Device="Dispozitiv"
|
||||
Mode="Mod"
|
||||
Buffering="Folosește zona tampon"
|
||||
PixelFormat="Formatul pixelilor"
|
||||
PixelFormat="Format pentru pixeli"
|
||||
ColorSpace="Spațiu de culori"
|
||||
ColorSpace.Default="Implicit"
|
||||
ColorRange="Gamă de culori"
|
||||
ColorRange.Default="Implicit"
|
||||
ColorRange.Partial="Parțială"
|
||||
ColorRange.Full="Completă"
|
||||
VideoConnection="Conexiune video"
|
||||
AudioConnection="Conexiune audio"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,4 +2,25 @@ BlackmagicDevice="Blackmagic naprave"
|
|||
Device="Naprava"
|
||||
Mode="Način"
|
||||
Buffering="Uporabi medpomnilnik"
|
||||
PixelFormat="Oblika sl. točk"
|
||||
ColorSpace="Barvni prostor"
|
||||
ColorSpace.Default="Privzeto"
|
||||
ColorRange="Barvni razpon"
|
||||
ColorRange.Default="Privzeto"
|
||||
ColorRange.Partial="Delno"
|
||||
ColorRange.Full="Polno"
|
||||
ChannelFormat="Kanal"
|
||||
ChannelFormat.None="Brez"
|
||||
ChannelFormat.2_0ch="2 kan."
|
||||
ChannelFormat.2_1ch="2.1 kan."
|
||||
ChannelFormat.4_0ch="4 kan."
|
||||
ChannelFormat.4_1ch="4.1 kan."
|
||||
ChannelFormat.5_1ch="5.1 kan."
|
||||
ChannelFormat.7_1ch="7.1 kan."
|
||||
DeactivateWhenNotShowing="Onemogoči, ko ni prikazano"
|
||||
AutoStart="Samodejno zaženi ob zagonu"
|
||||
SwapFC-LFE="Zamenjaj SS in nizke tone"
|
||||
SwapFC-LFE.Tooltip="Zamenjaj sprednji sredinski kanal in kanal z nizkimi toni"
|
||||
VideoConnection="Slikovna povezava"
|
||||
AudioConnection="Zvočna povezava"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ Device="Пристрій"
|
|||
Mode="Режим"
|
||||
Buffering="Увімкнути буферизацію"
|
||||
PixelFormat="Формат пікселів"
|
||||
ColorSpace="Колірний простір"
|
||||
ColorSpace.Default="За замовчуванням"
|
||||
ColorRange="Колірний діапазон"
|
||||
ColorRange.Default="За замовчуванням"
|
||||
ColorRange.Partial="Частковий"
|
||||
ColorRange.Full="Повний"
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ DeckLinkDevice *DeckLinkDeviceDiscovery::FindByHash(const char *hash)
|
|||
return ret;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DeckLinkDeviceDiscovery::DeckLinkDeviceArrived(
|
||||
IDeckLink *device)
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
DeckLinkDeviceDiscovery::DeckLinkDeviceArrived(IDeckLink *device)
|
||||
{
|
||||
DeckLinkDevice *newDev = new DeckLinkDevice(device);
|
||||
if (!newDev->Init()) {
|
||||
|
|
@ -73,8 +73,8 @@ HRESULT STDMETHODCALLTYPE DeckLinkDeviceDiscovery::DeckLinkDeviceArrived(
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DeckLinkDeviceDiscovery::DeckLinkDeviceRemoved(
|
||||
IDeckLink *device)
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
DeckLinkDeviceDiscovery::DeckLinkDeviceRemoved(IDeckLink *device)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(deviceMutex);
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ ULONG STDMETHODCALLTYPE DeckLinkDeviceDiscovery::AddRef(void)
|
|||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DeckLinkDeviceDiscovery::QueryInterface(REFIID iid,
|
||||
LPVOID *ppv)
|
||||
LPVOID *ppv)
|
||||
{
|
||||
HRESULT result = E_NOINTERFACE;
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ HRESULT STDMETHODCALLTYPE DeckLinkDeviceDiscovery::QueryInterface(REFIID iid,
|
|||
AddRef();
|
||||
result = S_OK;
|
||||
} else if (memcmp(&iid, &IID_IDeckLinkDeviceNotificationCallback,
|
||||
sizeof(REFIID)) == 0) {
|
||||
sizeof(REFIID)) == 0) {
|
||||
*ppv = (IDeckLinkDeviceNotificationCallback *)this;
|
||||
AddRef();
|
||||
result = S_OK;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
class DeckLinkDevice;
|
||||
|
||||
typedef void (*DeviceChangeCallback)(void *param, DeckLinkDevice *device,
|
||||
bool added);
|
||||
bool added);
|
||||
|
||||
struct DeviceChangeInfo {
|
||||
DeviceChangeCallback callback;
|
||||
|
|
@ -19,11 +19,11 @@ struct DeviceChangeInfo {
|
|||
class DeckLinkDeviceDiscovery : public IDeckLinkDeviceNotificationCallback {
|
||||
protected:
|
||||
ComPtr<IDeckLinkDiscovery> discovery;
|
||||
long refCount = 1;
|
||||
bool initialized = false;
|
||||
long refCount = 1;
|
||||
bool initialized = false;
|
||||
|
||||
std::recursive_mutex deviceMutex;
|
||||
std::vector<DeckLinkDevice*> devices;
|
||||
std::vector<DeckLinkDevice *> devices;
|
||||
std::vector<DeviceChangeInfo> callbacks;
|
||||
|
||||
public:
|
||||
|
|
@ -44,8 +44,7 @@ public:
|
|||
info.param = param;
|
||||
|
||||
for (DeviceChangeInfo &curCB : callbacks) {
|
||||
if (curCB.callback == callback &&
|
||||
curCB.param == param)
|
||||
if (curCB.callback == callback && curCB.param == param)
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +59,7 @@ public:
|
|||
DeviceChangeInfo &curCB = callbacks[i];
|
||||
|
||||
if (curCB.callback == callback &&
|
||||
curCB.param == param) {
|
||||
curCB.param == param) {
|
||||
callbacks.erase(callbacks.begin() + i);
|
||||
return;
|
||||
}
|
||||
|
|
@ -69,9 +68,9 @@ public:
|
|||
|
||||
DeckLinkDevice *FindByHash(const char *hash);
|
||||
|
||||
inline void Lock() {deviceMutex.lock();}
|
||||
inline void Unlock() {deviceMutex.unlock();}
|
||||
inline const std::vector<DeckLinkDevice*> &GetDevices() const
|
||||
inline void Lock() { deviceMutex.lock(); }
|
||||
inline void Unlock() { deviceMutex.unlock(); }
|
||||
inline const std::vector<DeckLinkDevice *> &GetDevices() const
|
||||
{
|
||||
return devices;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
static inline enum video_format ConvertPixelFormat(BMDPixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case bmdFormat8BitBGRA: return VIDEO_FORMAT_BGRX;
|
||||
case bmdFormat8BitBGRA:
|
||||
return VIDEO_FORMAT_BGRX;
|
||||
|
||||
default:
|
||||
case bmdFormat8BitYUV:;
|
||||
|
|
@ -38,7 +39,8 @@ static inline int ConvertChannelFormat(speaker_layout format)
|
|||
}
|
||||
}
|
||||
|
||||
static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format, bool swap)
|
||||
static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format,
|
||||
bool swap)
|
||||
{
|
||||
switch (format) {
|
||||
case SPEAKERS_2POINT1:
|
||||
|
|
@ -46,11 +48,11 @@ static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format, boo
|
|||
case SPEAKERS_4POINT0:
|
||||
return repack_mode_8to4ch;
|
||||
case SPEAKERS_4POINT1:
|
||||
return swap? repack_mode_8to5ch_swap:repack_mode_8to5ch;
|
||||
return swap ? repack_mode_8to5ch_swap : repack_mode_8to5ch;
|
||||
case SPEAKERS_5POINT1:
|
||||
return swap ? repack_mode_8to6ch_swap : repack_mode_8to6ch;
|
||||
case SPEAKERS_7POINT1:
|
||||
return swap ? repack_mode_8ch_swap: repack_mode_8ch;
|
||||
return swap ? repack_mode_8ch_swap : repack_mode_8ch;
|
||||
default:
|
||||
assert(false && "No repack requested");
|
||||
return (audio_repack_mode_t)-1;
|
||||
|
|
@ -58,21 +60,18 @@ static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format, boo
|
|||
}
|
||||
|
||||
DeckLinkDeviceInstance::DeckLinkDeviceInstance(DecklinkBase *decklink_,
|
||||
DeckLinkDevice *device_) :
|
||||
currentFrame(), currentPacket(), decklink(decklink_), device(device_)
|
||||
DeckLinkDevice *device_)
|
||||
: currentFrame(), currentPacket(), decklink(decklink_), device(device_)
|
||||
{
|
||||
currentPacket.samples_per_sec = 48000;
|
||||
currentPacket.speakers = SPEAKERS_STEREO;
|
||||
currentPacket.format = AUDIO_FORMAT_16BIT;
|
||||
currentPacket.speakers = SPEAKERS_STEREO;
|
||||
currentPacket.format = AUDIO_FORMAT_16BIT;
|
||||
}
|
||||
|
||||
DeckLinkDeviceInstance::~DeckLinkDeviceInstance()
|
||||
{
|
||||
}
|
||||
DeckLinkDeviceInstance::~DeckLinkDeviceInstance() {}
|
||||
|
||||
void DeckLinkDeviceInstance::HandleAudioPacket(
|
||||
IDeckLinkAudioInputPacket *audioPacket,
|
||||
const uint64_t timestamp)
|
||||
IDeckLinkAudioInputPacket *audioPacket, const uint64_t timestamp)
|
||||
{
|
||||
if (audioPacket == nullptr)
|
||||
return;
|
||||
|
|
@ -83,11 +82,12 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
|
|||
return;
|
||||
}
|
||||
|
||||
const uint32_t frameCount = (uint32_t)audioPacket->GetSampleFrameCount();
|
||||
currentPacket.frames = frameCount;
|
||||
currentPacket.timestamp = timestamp;
|
||||
const uint32_t frameCount =
|
||||
(uint32_t)audioPacket->GetSampleFrameCount();
|
||||
currentPacket.frames = frameCount;
|
||||
currentPacket.timestamp = timestamp;
|
||||
|
||||
if (decklink && !static_cast<DeckLinkInput*>(decklink)->buffering) {
|
||||
if (decklink && !static_cast<DeckLinkInput *>(decklink)->buffering) {
|
||||
currentPacket.timestamp = os_gettime_ns();
|
||||
currentPacket.timestamp -=
|
||||
(uint64_t)frameCount * 1000000000ULL /
|
||||
|
|
@ -99,8 +99,9 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
|
|||
if (channelFormat != SPEAKERS_UNKNOWN &&
|
||||
channelFormat != SPEAKERS_MONO &&
|
||||
channelFormat != SPEAKERS_STEREO &&
|
||||
(channelFormat != SPEAKERS_7POINT1 || static_cast<DeckLinkInput*>(decklink)->swap)
|
||||
&& maxdevicechannel >= 8) {
|
||||
(channelFormat != SPEAKERS_7POINT1 ||
|
||||
static_cast<DeckLinkInput *>(decklink)->swap) &&
|
||||
maxdevicechannel >= 8) {
|
||||
|
||||
if (audioRepacker->repack((uint8_t *)bytes, frameCount) < 0) {
|
||||
LOG(LOG_ERROR, "Failed to convert audio packet data");
|
||||
|
|
@ -112,13 +113,15 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
|
|||
}
|
||||
|
||||
nextAudioTS = timestamp +
|
||||
((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1;
|
||||
((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1;
|
||||
|
||||
obs_source_output_audio(static_cast<DeckLinkInput*>(decklink)->GetSource(), ¤tPacket);
|
||||
obs_source_output_audio(
|
||||
static_cast<DeckLinkInput *>(decklink)->GetSource(),
|
||||
¤tPacket);
|
||||
}
|
||||
|
||||
void DeckLinkDeviceInstance::HandleVideoFrame(
|
||||
IDeckLinkVideoInputFrame *videoFrame, const uint64_t timestamp)
|
||||
IDeckLinkVideoInputFrame *videoFrame, const uint64_t timestamp)
|
||||
{
|
||||
if (videoFrame == nullptr)
|
||||
return;
|
||||
|
|
@ -129,13 +132,15 @@ void DeckLinkDeviceInstance::HandleVideoFrame(
|
|||
return;
|
||||
}
|
||||
|
||||
currentFrame.data[0] = (uint8_t *)bytes;
|
||||
currentFrame.data[0] = (uint8_t *)bytes;
|
||||
currentFrame.linesize[0] = (uint32_t)videoFrame->GetRowBytes();
|
||||
currentFrame.width = (uint32_t)videoFrame->GetWidth();
|
||||
currentFrame.height = (uint32_t)videoFrame->GetHeight();
|
||||
currentFrame.timestamp = timestamp;
|
||||
currentFrame.width = (uint32_t)videoFrame->GetWidth();
|
||||
currentFrame.height = (uint32_t)videoFrame->GetHeight();
|
||||
currentFrame.timestamp = timestamp;
|
||||
|
||||
obs_source_output_video2(static_cast<DeckLinkInput*>(decklink)->GetSource(), ¤tFrame);
|
||||
obs_source_output_video2(
|
||||
static_cast<DeckLinkInput *>(decklink)->GetSource(),
|
||||
¤tFrame);
|
||||
}
|
||||
|
||||
void DeckLinkDeviceInstance::FinalizeStream()
|
||||
|
|
@ -145,8 +150,7 @@ void DeckLinkDeviceInstance::FinalizeStream()
|
|||
if (channelFormat != SPEAKERS_UNKNOWN)
|
||||
input->DisableAudioInput();
|
||||
|
||||
if (audioRepacker != nullptr)
|
||||
{
|
||||
if (audioRepacker != nullptr) {
|
||||
delete audioRepacker;
|
||||
audioRepacker = nullptr;
|
||||
}
|
||||
|
|
@ -163,7 +167,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_)
|
|||
|
||||
currentFrame.format = ConvertPixelFormat(pixelFormat);
|
||||
|
||||
colorSpace = static_cast<DeckLinkInput*>(decklink)->GetColorSpace();
|
||||
colorSpace = static_cast<DeckLinkInput *>(decklink)->GetColorSpace();
|
||||
if (colorSpace == VIDEO_CS_DEFAULT) {
|
||||
const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags();
|
||||
if (flags & bmdDisplayModeColorspaceRec709)
|
||||
|
|
@ -176,24 +180,25 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_)
|
|||
activeColorSpace = colorSpace;
|
||||
}
|
||||
|
||||
colorRange = static_cast<DeckLinkInput*>(decklink)->GetColorRange();
|
||||
colorRange = static_cast<DeckLinkInput *>(decklink)->GetColorRange();
|
||||
currentFrame.range = colorRange;
|
||||
|
||||
video_format_get_parameters(activeColorSpace, colorRange,
|
||||
currentFrame.color_matrix, currentFrame.color_range_min,
|
||||
currentFrame.color_range_max);
|
||||
currentFrame.color_matrix,
|
||||
currentFrame.color_range_min,
|
||||
currentFrame.color_range_max);
|
||||
|
||||
#ifdef LOG_SETUP_VIDEO_FORMAT
|
||||
LOG(LOG_INFO, "Setup video format: %s, %s, %s",
|
||||
pixelFormat == bmdFormat8BitYUV ? "YUV" : "RGB",
|
||||
activeColorSpace == VIDEO_CS_709 ? "BT.709" : "BT.601",
|
||||
colorRange == VIDEO_RANGE_FULL ? "full" : "limited");
|
||||
pixelFormat == bmdFormat8BitYUV ? "YUV" : "RGB",
|
||||
activeColorSpace == VIDEO_CS_709 ? "BT.709" : "BT.601",
|
||||
colorRange == VIDEO_RANGE_FULL ? "full" : "limited");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_,
|
||||
BMDVideoConnection bmdVideoConnection,
|
||||
BMDAudioConnection bmdAudioConnection)
|
||||
BMDVideoConnection bmdVideoConnection,
|
||||
BMDAudioConnection bmdAudioConnection)
|
||||
{
|
||||
if (mode != nullptr)
|
||||
return false;
|
||||
|
|
@ -205,33 +210,33 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_,
|
|||
if (!device->GetInput(&input))
|
||||
return false;
|
||||
|
||||
|
||||
IDeckLinkConfiguration *deckLinkConfiguration = NULL;
|
||||
HRESULT result = input->QueryInterface(IID_IDeckLinkConfiguration,
|
||||
(void**)&deckLinkConfiguration);
|
||||
if (result != S_OK)
|
||||
{
|
||||
(void **)&deckLinkConfiguration);
|
||||
if (result != S_OK) {
|
||||
LOG(LOG_ERROR,
|
||||
"Could not obtain the IDeckLinkConfiguration interface: %08x\n",
|
||||
result);
|
||||
"Could not obtain the IDeckLinkConfiguration interface: %08x\n",
|
||||
result);
|
||||
} else {
|
||||
if (bmdVideoConnection > 0) {
|
||||
result = deckLinkConfiguration->SetInt(
|
||||
bmdDeckLinkConfigVideoInputConnection, bmdVideoConnection);
|
||||
bmdDeckLinkConfigVideoInputConnection,
|
||||
bmdVideoConnection);
|
||||
if (result != S_OK) {
|
||||
LOG(LOG_ERROR,
|
||||
"Couldn't set input video port to %d\n\n",
|
||||
bmdVideoConnection);
|
||||
"Couldn't set input video port to %d\n\n",
|
||||
bmdVideoConnection);
|
||||
}
|
||||
}
|
||||
|
||||
if (bmdAudioConnection > 0) {
|
||||
result = deckLinkConfiguration->SetInt(
|
||||
bmdDeckLinkConfigAudioInputConnection, bmdAudioConnection);
|
||||
bmdDeckLinkConfigAudioInputConnection,
|
||||
bmdAudioConnection);
|
||||
if (result != S_OK) {
|
||||
LOG(LOG_ERROR,
|
||||
"Couldn't set input audio port to %d\n\n",
|
||||
bmdVideoConnection);
|
||||
"Couldn't set input audio port to %d\n\n",
|
||||
bmdVideoConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -248,12 +253,13 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_,
|
|||
flags = bmdVideoInputEnableFormatDetection;
|
||||
} else {
|
||||
displayMode = mode_->GetDisplayMode();
|
||||
pixelFormat = static_cast<DeckLinkInput*>(decklink)->GetPixelFormat();
|
||||
pixelFormat =
|
||||
static_cast<DeckLinkInput *>(decklink)->GetPixelFormat();
|
||||
flags = bmdVideoInputFlagDefault;
|
||||
}
|
||||
|
||||
const HRESULT videoResult = input->EnableVideoInput(displayMode,
|
||||
pixelFormat, flags);
|
||||
const HRESULT videoResult =
|
||||
input->EnableVideoInput(displayMode, pixelFormat, flags);
|
||||
if (videoResult != S_OK) {
|
||||
LOG(LOG_ERROR, "Failed to enable video input");
|
||||
return false;
|
||||
|
|
@ -261,28 +267,30 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_,
|
|||
|
||||
SetupVideoFormat(mode_);
|
||||
|
||||
channelFormat = static_cast<DeckLinkInput*>(decklink)->GetChannelFormat();
|
||||
channelFormat =
|
||||
static_cast<DeckLinkInput *>(decklink)->GetChannelFormat();
|
||||
currentPacket.speakers = channelFormat;
|
||||
swap = static_cast<DeckLinkInput*>(decklink)->swap;
|
||||
swap = static_cast<DeckLinkInput *>(decklink)->swap;
|
||||
|
||||
int maxdevicechannel = device->GetMaxChannel();
|
||||
|
||||
if (channelFormat != SPEAKERS_UNKNOWN) {
|
||||
const int channel = ConvertChannelFormat(channelFormat);
|
||||
const HRESULT audioResult = input->EnableAudioInput(
|
||||
bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
|
||||
channel);
|
||||
bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
|
||||
channel);
|
||||
if (audioResult != S_OK)
|
||||
LOG(LOG_WARNING, "Failed to enable audio input; continuing...");
|
||||
LOG(LOG_WARNING,
|
||||
"Failed to enable audio input; continuing...");
|
||||
|
||||
if (channelFormat != SPEAKERS_UNKNOWN &&
|
||||
channelFormat != SPEAKERS_MONO &&
|
||||
channelFormat != SPEAKERS_STEREO &&
|
||||
(channelFormat != SPEAKERS_7POINT1 || swap)
|
||||
&& maxdevicechannel >= 8) {
|
||||
(channelFormat != SPEAKERS_7POINT1 || swap) &&
|
||||
maxdevicechannel >= 8) {
|
||||
|
||||
const audio_repack_mode_t repack_mode = ConvertRepackFormat
|
||||
(channelFormat, swap);
|
||||
const audio_repack_mode_t repack_mode =
|
||||
ConvertRepackFormat(channelFormat, swap);
|
||||
audioRepacker = new AudioRepacker(repack_mode);
|
||||
}
|
||||
}
|
||||
|
|
@ -310,7 +318,7 @@ bool DeckLinkDeviceInstance::StopCapture(void)
|
|||
return false;
|
||||
|
||||
LOG(LOG_INFO, "Stopping capture of '%s'...",
|
||||
GetDevice()->GetDisplayName().c_str());
|
||||
GetDevice()->GetDisplayName().c_str());
|
||||
|
||||
input->StopStreams();
|
||||
FinalizeStream();
|
||||
|
|
@ -331,18 +339,15 @@ bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_)
|
|||
return false;
|
||||
|
||||
const HRESULT videoResult = output->EnableVideoOutput(
|
||||
mode_->GetDisplayMode(),
|
||||
bmdVideoOutputFlagDefault);
|
||||
mode_->GetDisplayMode(), bmdVideoOutputFlagDefault);
|
||||
if (videoResult != S_OK) {
|
||||
LOG(LOG_ERROR, "Failed to enable video output");
|
||||
return false;
|
||||
}
|
||||
|
||||
const HRESULT audioResult = output->EnableAudioOutput(
|
||||
bmdAudioSampleRate48kHz,
|
||||
bmdAudioSampleType16bitInteger,
|
||||
2,
|
||||
bmdAudioOutputStreamTimestamped);
|
||||
bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2,
|
||||
bmdAudioOutputStreamTimestamped);
|
||||
if (audioResult != S_OK) {
|
||||
LOG(LOG_ERROR, "Failed to enable audio output");
|
||||
return false;
|
||||
|
|
@ -362,7 +367,7 @@ bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_)
|
|||
}
|
||||
}
|
||||
|
||||
auto decklinkOutput = dynamic_cast<DeckLinkOutput*>(decklink);
|
||||
auto decklinkOutput = dynamic_cast<DeckLinkOutput *>(decklink);
|
||||
if (decklinkOutput == nullptr)
|
||||
return false;
|
||||
|
||||
|
|
@ -378,13 +383,11 @@ bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_)
|
|||
|
||||
HRESULT result;
|
||||
result = output->CreateVideoFrame(decklinkOutput->GetWidth(),
|
||||
decklinkOutput->GetHeight(),
|
||||
rowBytes,
|
||||
pixelFormat,
|
||||
bmdFrameFlagDefault,
|
||||
&decklinkOutputFrame);
|
||||
decklinkOutput->GetHeight(), rowBytes,
|
||||
pixelFormat, bmdFrameFlagDefault,
|
||||
&decklinkOutputFrame);
|
||||
if (result != S_OK) {
|
||||
blog(LOG_ERROR ,"failed to make frame 0x%X", result);
|
||||
blog(LOG_ERROR, "failed to make frame 0x%X", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -397,7 +400,7 @@ bool DeckLinkDeviceInstance::StopOutput()
|
|||
return false;
|
||||
|
||||
LOG(LOG_INFO, "Stopping output of '%s'...",
|
||||
GetDevice()->GetDisplayName().c_str());
|
||||
GetDevice()->GetDisplayName().c_str());
|
||||
|
||||
output->DisableVideoOutput();
|
||||
output->DisableAudioOutput();
|
||||
|
|
@ -412,12 +415,12 @@ bool DeckLinkDeviceInstance::StopOutput()
|
|||
|
||||
void DeckLinkDeviceInstance::DisplayVideoFrame(video_data *frame)
|
||||
{
|
||||
auto decklinkOutput = dynamic_cast<DeckLinkOutput*>(decklink);
|
||||
auto decklinkOutput = dynamic_cast<DeckLinkOutput *>(decklink);
|
||||
if (decklinkOutput == nullptr)
|
||||
return;
|
||||
|
||||
uint8_t *destData;
|
||||
decklinkOutputFrame->GetBytes((void**)&destData);
|
||||
decklinkOutputFrame->GetBytes((void **)&destData);
|
||||
|
||||
uint8_t *outData = frame->data[0];
|
||||
|
||||
|
|
@ -426,8 +429,8 @@ void DeckLinkDeviceInstance::DisplayVideoFrame(video_data *frame)
|
|||
rowBytes = decklinkOutput->GetWidth() * 4;
|
||||
}
|
||||
|
||||
std::copy(outData, outData + (decklinkOutput->GetHeight() *
|
||||
rowBytes), destData);
|
||||
std::copy(outData, outData + (decklinkOutput->GetHeight() * rowBytes),
|
||||
destData);
|
||||
|
||||
output->DisplayVideoFrameSync(decklinkOutputFrame);
|
||||
}
|
||||
|
|
@ -435,16 +438,15 @@ void DeckLinkDeviceInstance::DisplayVideoFrame(video_data *frame)
|
|||
void DeckLinkDeviceInstance::WriteAudio(audio_data *frames)
|
||||
{
|
||||
uint32_t sampleFramesWritten;
|
||||
output->WriteAudioSamplesSync(frames->data[0],
|
||||
frames->frames,
|
||||
&sampleFramesWritten);
|
||||
output->WriteAudioSamplesSync(frames->data[0], frames->frames,
|
||||
&sampleFramesWritten);
|
||||
}
|
||||
|
||||
#define TIME_BASE 1000000000
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived(
|
||||
IDeckLinkVideoInputFrame *videoFrame,
|
||||
IDeckLinkAudioInputPacket *audioPacket)
|
||||
IDeckLinkVideoInputFrame *videoFrame,
|
||||
IDeckLinkAudioInputPacket *audioPacket)
|
||||
{
|
||||
BMDTimeValue videoTS = 0;
|
||||
BMDTimeValue videoDur = 0;
|
||||
|
|
@ -481,9 +483,8 @@ HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived(
|
|||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFormatChanged(
|
||||
BMDVideoInputFormatChangedEvents events,
|
||||
IDeckLinkDisplayMode *newMode,
|
||||
BMDDetectedVideoInputFormatFlags detectedSignalFlags)
|
||||
BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *newMode,
|
||||
BMDDetectedVideoInputFormatFlags detectedSignalFlags)
|
||||
{
|
||||
input->PauseStreams();
|
||||
|
||||
|
|
@ -506,8 +507,8 @@ HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFormatChanged(
|
|||
}
|
||||
}
|
||||
|
||||
const HRESULT videoResult = input->EnableVideoInput(displayMode,
|
||||
pixelFormat, bmdVideoInputEnableFormatDetection);
|
||||
const HRESULT videoResult = input->EnableVideoInput(
|
||||
displayMode, pixelFormat, bmdVideoInputEnableFormatDetection);
|
||||
if (videoResult != S_OK) {
|
||||
LOG(LOG_ERROR, "Failed to enable video input");
|
||||
input->StopStreams();
|
||||
|
|
@ -530,7 +531,7 @@ ULONG STDMETHODCALLTYPE DeckLinkDeviceInstance::AddRef(void)
|
|||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::QueryInterface(REFIID iid,
|
||||
LPVOID *ppv)
|
||||
LPVOID *ppv)
|
||||
{
|
||||
HRESULT result = E_NOINTERFACE;
|
||||
|
||||
|
|
@ -542,7 +543,7 @@ HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::QueryInterface(REFIID iid,
|
|||
AddRef();
|
||||
result = S_OK;
|
||||
} else if (memcmp(&iid, &IID_IDeckLinkNotificationCallback,
|
||||
sizeof(REFIID)) == 0) {
|
||||
sizeof(REFIID)) == 0) {
|
||||
*ppv = (IDeckLinkNotificationCallback *)this;
|
||||
AddRef();
|
||||
result = S_OK;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#define LOG(level, message, ...) blog(level, "%s: " message, "decklink", ##__VA_ARGS__)
|
||||
#define LOG(level, message, ...) \
|
||||
blog(level, "%s: " message, "decklink", ##__VA_ARGS__)
|
||||
|
||||
#include <obs-module.h>
|
||||
#include "decklink-device.hpp"
|
||||
|
|
@ -13,25 +14,25 @@ class DeckLinkDeviceInstance : public IDeckLinkInputCallback {
|
|||
protected:
|
||||
struct obs_source_frame2 currentFrame;
|
||||
struct obs_source_audio currentPacket;
|
||||
DecklinkBase *decklink = nullptr;
|
||||
DeckLinkDevice *device = nullptr;
|
||||
DeckLinkDeviceMode *mode = nullptr;
|
||||
BMDVideoConnection videoConnection;
|
||||
BMDAudioConnection audioConnection;
|
||||
BMDDisplayMode displayMode = bmdModeNTSC;
|
||||
BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
|
||||
video_colorspace colorSpace = VIDEO_CS_DEFAULT;
|
||||
video_colorspace activeColorSpace = VIDEO_CS_DEFAULT;
|
||||
video_range_type colorRange = VIDEO_RANGE_DEFAULT;
|
||||
ComPtr<IDeckLinkInput> input;
|
||||
DecklinkBase *decklink = nullptr;
|
||||
DeckLinkDevice *device = nullptr;
|
||||
DeckLinkDeviceMode *mode = nullptr;
|
||||
BMDVideoConnection videoConnection;
|
||||
BMDAudioConnection audioConnection;
|
||||
BMDDisplayMode displayMode = bmdModeNTSC;
|
||||
BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
|
||||
video_colorspace colorSpace = VIDEO_CS_DEFAULT;
|
||||
video_colorspace activeColorSpace = VIDEO_CS_DEFAULT;
|
||||
video_range_type colorRange = VIDEO_RANGE_DEFAULT;
|
||||
ComPtr<IDeckLinkInput> input;
|
||||
ComPtr<IDeckLinkOutput> output;
|
||||
volatile long refCount = 1;
|
||||
int64_t audioOffset = 0;
|
||||
uint64_t nextAudioTS = 0;
|
||||
uint64_t lastVideoTS = 0;
|
||||
AudioRepacker *audioRepacker = nullptr;
|
||||
speaker_layout channelFormat = SPEAKERS_STEREO;
|
||||
bool swap;
|
||||
volatile long refCount = 1;
|
||||
int64_t audioOffset = 0;
|
||||
uint64_t nextAudioTS = 0;
|
||||
uint64_t lastVideoTS = 0;
|
||||
AudioRepacker *audioRepacker = nullptr;
|
||||
speaker_layout channelFormat = SPEAKERS_STEREO;
|
||||
bool swap;
|
||||
|
||||
IDeckLinkMutableVideoFrame *decklinkOutputFrame = nullptr;
|
||||
|
||||
|
|
@ -39,45 +40,63 @@ protected:
|
|||
void SetupVideoFormat(DeckLinkDeviceMode *mode_);
|
||||
|
||||
void HandleAudioPacket(IDeckLinkAudioInputPacket *audioPacket,
|
||||
const uint64_t timestamp);
|
||||
const uint64_t timestamp);
|
||||
void HandleVideoFrame(IDeckLinkVideoInputFrame *videoFrame,
|
||||
const uint64_t timestamp);
|
||||
const uint64_t timestamp);
|
||||
|
||||
public:
|
||||
DeckLinkDeviceInstance(DecklinkBase *decklink, DeckLinkDevice *device);
|
||||
virtual ~DeckLinkDeviceInstance();
|
||||
|
||||
inline DeckLinkDevice *GetDevice() const {return device;}
|
||||
inline DeckLinkDevice *GetDevice() const { return device; }
|
||||
inline long long GetActiveModeId() const
|
||||
{
|
||||
return mode ? mode->GetId() : 0;
|
||||
}
|
||||
|
||||
inline BMDPixelFormat GetActivePixelFormat() const {return pixelFormat;}
|
||||
inline video_colorspace GetActiveColorSpace() const {return colorSpace;}
|
||||
inline video_range_type GetActiveColorRange() const {return colorRange;}
|
||||
inline speaker_layout GetActiveChannelFormat() const {return channelFormat;}
|
||||
inline bool GetActiveSwapState() const {return swap;}
|
||||
inline BMDVideoConnection GetVideoConnection() const {return videoConnection;}
|
||||
inline BMDAudioConnection GetAudioConnection() const {return audioConnection;}
|
||||
inline BMDPixelFormat GetActivePixelFormat() const
|
||||
{
|
||||
return pixelFormat;
|
||||
}
|
||||
inline video_colorspace GetActiveColorSpace() const
|
||||
{
|
||||
return colorSpace;
|
||||
}
|
||||
inline video_range_type GetActiveColorRange() const
|
||||
{
|
||||
return colorRange;
|
||||
}
|
||||
inline speaker_layout GetActiveChannelFormat() const
|
||||
{
|
||||
return channelFormat;
|
||||
}
|
||||
inline bool GetActiveSwapState() const { return swap; }
|
||||
inline BMDVideoConnection GetVideoConnection() const
|
||||
{
|
||||
return videoConnection;
|
||||
}
|
||||
inline BMDAudioConnection GetAudioConnection() const
|
||||
{
|
||||
return audioConnection;
|
||||
}
|
||||
|
||||
inline DeckLinkDeviceMode *GetMode() const {return mode;}
|
||||
inline DeckLinkDeviceMode *GetMode() const { return mode; }
|
||||
|
||||
bool StartCapture(DeckLinkDeviceMode *mode,
|
||||
BMDVideoConnection bmdVideoConnection,
|
||||
BMDAudioConnection bmdAudioConnection);
|
||||
BMDVideoConnection bmdVideoConnection,
|
||||
BMDAudioConnection bmdAudioConnection);
|
||||
bool StopCapture(void);
|
||||
|
||||
bool StartOutput(DeckLinkDeviceMode *mode_);
|
||||
bool StopOutput(void);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(
|
||||
IDeckLinkVideoInputFrame *videoFrame,
|
||||
IDeckLinkAudioInputPacket *audioPacket);
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
VideoInputFrameArrived(IDeckLinkVideoInputFrame *videoFrame,
|
||||
IDeckLinkAudioInputPacket *audioPacket);
|
||||
HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(
|
||||
BMDVideoInputFormatChangedEvents events,
|
||||
IDeckLinkDisplayMode *newMode,
|
||||
BMDDetectedVideoInputFormatFlags detectedSignalFlags);
|
||||
BMDVideoInputFormatChangedEvents events,
|
||||
IDeckLinkDisplayMode *newMode,
|
||||
BMDDetectedVideoInputFormatFlags detectedSignalFlags);
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef(void);
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "decklink-device-mode.hpp"
|
||||
|
||||
DeckLinkDeviceMode::DeckLinkDeviceMode(IDeckLinkDisplayMode *mode,
|
||||
long long id) : id(id), mode(mode)
|
||||
DeckLinkDeviceMode::DeckLinkDeviceMode(IDeckLinkDisplayMode *mode, long long id)
|
||||
: id(id), mode(mode)
|
||||
{
|
||||
if (mode == nullptr)
|
||||
return;
|
||||
|
|
@ -13,8 +13,8 @@ DeckLinkDeviceMode::DeckLinkDeviceMode(IDeckLinkDisplayMode *mode,
|
|||
DeckLinkStringToStdString(decklinkStringName, name);
|
||||
}
|
||||
|
||||
DeckLinkDeviceMode::DeckLinkDeviceMode(const std::string& name, long long id) :
|
||||
id(id), mode(nullptr), name(name)
|
||||
DeckLinkDeviceMode::DeckLinkDeviceMode(const std::string &name, long long id)
|
||||
: id(id), mode(nullptr), name(name)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ long long DeckLinkDeviceMode::GetId(void) const
|
|||
return id;
|
||||
}
|
||||
|
||||
const std::string& DeckLinkDeviceMode::GetName(void) const
|
||||
const std::string &DeckLinkDeviceMode::GetName(void) const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,23 +4,23 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#define MODE_ID_AUTO -1
|
||||
#define MODE_ID_AUTO -1
|
||||
|
||||
class DeckLinkDeviceMode {
|
||||
protected:
|
||||
long long id;
|
||||
long long id;
|
||||
IDeckLinkDisplayMode *mode;
|
||||
std::string name;
|
||||
std::string name;
|
||||
|
||||
public:
|
||||
DeckLinkDeviceMode(IDeckLinkDisplayMode *mode, long long id);
|
||||
DeckLinkDeviceMode(const std::string& name, long long id);
|
||||
DeckLinkDeviceMode(const std::string &name, long long id);
|
||||
virtual ~DeckLinkDeviceMode(void);
|
||||
|
||||
BMDDisplayMode GetDisplayMode(void) const;
|
||||
BMDDisplayModeFlags GetDisplayModeFlags(void) const;
|
||||
long long GetId(void) const;
|
||||
const std::string& GetName(void) const;
|
||||
const std::string &GetName(void) const;
|
||||
|
||||
void SetMode(IDeckLinkDisplayMode *mode);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@
|
|||
|
||||
#include <util/threading.h>
|
||||
|
||||
DeckLinkDevice::DeckLinkDevice(IDeckLink *device_) : device(device_)
|
||||
{
|
||||
}
|
||||
DeckLinkDevice::DeckLinkDevice(IDeckLink *device_) : device(device_) {}
|
||||
|
||||
DeckLinkDevice::~DeckLinkDevice(void)
|
||||
{
|
||||
|
|
@ -34,15 +32,15 @@ bool DeckLinkDevice::Init()
|
|||
{
|
||||
ComPtr<IDeckLinkAttributes> attributes;
|
||||
const HRESULT result = device->QueryInterface(IID_IDeckLinkAttributes,
|
||||
(void **)&attributes);
|
||||
(void **)&attributes);
|
||||
|
||||
if (result == S_OK) {
|
||||
decklink_bool_t detectable = false;
|
||||
if (attributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection,
|
||||
&detectable) == S_OK && !!detectable) {
|
||||
DeckLinkDeviceMode *mode = new DeckLinkDeviceMode(
|
||||
"Auto",
|
||||
MODE_ID_AUTO);
|
||||
&detectable) == S_OK &&
|
||||
!!detectable) {
|
||||
DeckLinkDeviceMode *mode =
|
||||
new DeckLinkDeviceMode("Auto", MODE_ID_AUTO);
|
||||
inputModes.push_back(mode);
|
||||
inputModeIdMap[MODE_ID_AUTO] = mode;
|
||||
}
|
||||
|
|
@ -50,7 +48,8 @@ bool DeckLinkDevice::Init()
|
|||
|
||||
// Find input modes
|
||||
ComPtr<IDeckLinkInput> input;
|
||||
if (device->QueryInterface(IID_IDeckLinkInput, (void **) &input) == S_OK) {
|
||||
if (device->QueryInterface(IID_IDeckLinkInput, (void **)&input) ==
|
||||
S_OK) {
|
||||
IDeckLinkDisplayModeIterator *modeIterator;
|
||||
if (input->GetDisplayModeIterator(&modeIterator) == S_OK) {
|
||||
IDeckLinkDisplayMode *displayMode;
|
||||
|
|
@ -61,7 +60,8 @@ bool DeckLinkDevice::Init()
|
|||
continue;
|
||||
|
||||
DeckLinkDeviceMode *mode =
|
||||
new DeckLinkDeviceMode(displayMode, modeId);
|
||||
new DeckLinkDeviceMode(displayMode,
|
||||
modeId);
|
||||
inputModes.push_back(mode);
|
||||
inputModeIdMap[modeId] = mode;
|
||||
displayMode->Release();
|
||||
|
|
@ -74,19 +74,20 @@ bool DeckLinkDevice::Init()
|
|||
|
||||
// Get supported video connections
|
||||
attributes->GetInt(BMDDeckLinkVideoInputConnections,
|
||||
&supportedVideoInputConnections);
|
||||
&supportedVideoInputConnections);
|
||||
attributes->GetInt(BMDDeckLinkVideoOutputConnections,
|
||||
&supportedVideoOutputConnections);
|
||||
&supportedVideoOutputConnections);
|
||||
|
||||
// Get supported audio connections
|
||||
attributes->GetInt(BMDDeckLinkAudioInputConnections,
|
||||
&supportedAudioInputConnections);
|
||||
&supportedAudioInputConnections);
|
||||
attributes->GetInt(BMDDeckLinkAudioOutputConnections,
|
||||
&supportedAudioOutputConnections);
|
||||
&supportedAudioOutputConnections);
|
||||
|
||||
// find output modes
|
||||
ComPtr<IDeckLinkOutput> output;
|
||||
if (device->QueryInterface(IID_IDeckLinkOutput, (void **) &output) == S_OK) {
|
||||
if (device->QueryInterface(IID_IDeckLinkOutput, (void **)&output) ==
|
||||
S_OK) {
|
||||
|
||||
IDeckLinkDisplayModeIterator *modeIterator;
|
||||
if (output->GetDisplayModeIterator(&modeIterator) == S_OK) {
|
||||
|
|
@ -98,7 +99,8 @@ bool DeckLinkDevice::Init()
|
|||
continue;
|
||||
|
||||
DeckLinkDeviceMode *mode =
|
||||
new DeckLinkDeviceMode(displayMode, modeId);
|
||||
new DeckLinkDeviceMode(displayMode,
|
||||
modeId);
|
||||
outputModes.push_back(mode);
|
||||
outputModeIdMap[modeId] = mode;
|
||||
displayMode->Release();
|
||||
|
|
@ -111,9 +113,9 @@ bool DeckLinkDevice::Init()
|
|||
|
||||
// get keyer support
|
||||
attributes->GetFlag(BMDDeckLinkSupportsExternalKeying,
|
||||
&supportsExternalKeyer);
|
||||
&supportsExternalKeyer);
|
||||
attributes->GetFlag(BMDDeckLinkSupportsInternalKeying,
|
||||
&supportsInternalKeyer);
|
||||
&supportsInternalKeyer);
|
||||
|
||||
// Sub Device Counts
|
||||
attributes->GetInt(BMDDeckLinkSubDeviceIndex, &subDeviceIndex);
|
||||
|
|
@ -139,7 +141,8 @@ bool DeckLinkDevice::Init()
|
|||
/* Intensity Shuttle for Thunderbolt return 2; however, it supports 8 channels */
|
||||
if (name == "Intensity Shuttle Thunderbolt")
|
||||
maxChannel = 8;
|
||||
else if (attributes->GetInt(BMDDeckLinkMaximumAudioChannels, &channels) == S_OK)
|
||||
else if (attributes->GetInt(BMDDeckLinkMaximumAudioChannels,
|
||||
&channels) == S_OK)
|
||||
maxChannel = (int32_t)channels;
|
||||
else
|
||||
maxChannel = 2;
|
||||
|
|
@ -149,7 +152,7 @@ bool DeckLinkDevice::Init()
|
|||
* BMDDeckLinkPersistentID for newer ones */
|
||||
|
||||
int64_t value;
|
||||
if (attributes->GetInt(BMDDeckLinkPersistentID, &value) != S_OK &&
|
||||
if (attributes->GetInt(BMDDeckLinkPersistentID, &value) != S_OK &&
|
||||
attributes->GetInt(BMDDeckLinkTopologicalID, &value) != S_OK)
|
||||
return true;
|
||||
|
||||
|
|
@ -161,14 +164,15 @@ bool DeckLinkDevice::Init()
|
|||
|
||||
bool DeckLinkDevice::GetInput(IDeckLinkInput **input)
|
||||
{
|
||||
if (device->QueryInterface(IID_IDeckLinkInput, (void**)input) != S_OK)
|
||||
if (device->QueryInterface(IID_IDeckLinkInput, (void **)input) != S_OK)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeckLinkDevice::GetOutput(IDeckLinkOutput **output)
|
||||
{
|
||||
if (device->QueryInterface(IID_IDeckLinkOutput, (void**)output) != S_OK)
|
||||
if (device->QueryInterface(IID_IDeckLinkOutput, (void **)output) !=
|
||||
S_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -176,9 +180,10 @@ bool DeckLinkDevice::GetOutput(IDeckLinkOutput **output)
|
|||
|
||||
bool DeckLinkDevice::GetKeyer(IDeckLinkKeyer **deckLinkKeyer)
|
||||
{
|
||||
if (device->QueryInterface(IID_IDeckLinkKeyer, (void**)deckLinkKeyer) != S_OK)
|
||||
{
|
||||
fprintf(stderr, "Could not obtain the IDeckLinkKeyer interface\n");
|
||||
if (device->QueryInterface(IID_IDeckLinkKeyer,
|
||||
(void **)deckLinkKeyer) != S_OK) {
|
||||
fprintf(stderr,
|
||||
"Could not obtain the IDeckLinkKeyer interface\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -205,22 +210,24 @@ DeckLinkDeviceMode *DeckLinkDevice::FindOutputMode(long long id)
|
|||
return outputModeIdMap[id];
|
||||
}
|
||||
|
||||
const std::string& DeckLinkDevice::GetDisplayName(void)
|
||||
const std::string &DeckLinkDevice::GetDisplayName(void)
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
|
||||
const std::string& DeckLinkDevice::GetHash(void) const
|
||||
const std::string &DeckLinkDevice::GetHash(void) const
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
|
||||
const std::vector<DeckLinkDeviceMode *>& DeckLinkDevice::GetInputModes(void) const
|
||||
const std::vector<DeckLinkDeviceMode *> &
|
||||
DeckLinkDevice::GetInputModes(void) const
|
||||
{
|
||||
return inputModes;
|
||||
}
|
||||
|
||||
const std::vector<DeckLinkDeviceMode *>& DeckLinkDevice::GetOutputModes(void) const
|
||||
const std::vector<DeckLinkDeviceMode *> &
|
||||
DeckLinkDevice::GetOutputModes(void) const
|
||||
{
|
||||
return outputModes;
|
||||
}
|
||||
|
|
@ -235,7 +242,6 @@ int64_t DeckLinkDevice::GetAudioInputConnections()
|
|||
return supportedAudioInputConnections;
|
||||
}
|
||||
|
||||
|
||||
bool DeckLinkDevice::GetSupportsExternalKeyer(void) const
|
||||
{
|
||||
return supportsExternalKeyer;
|
||||
|
|
@ -256,7 +262,7 @@ int64_t DeckLinkDevice::GetSubDeviceIndex()
|
|||
return subDeviceIndex;
|
||||
}
|
||||
|
||||
const std::string& DeckLinkDevice::GetName(void) const
|
||||
const std::string &DeckLinkDevice::GetName(void) const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,28 +7,26 @@
|
|||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
class DeckLinkDevice {
|
||||
ComPtr<IDeckLink> device;
|
||||
ComPtr<IDeckLink> device;
|
||||
std::map<long long, DeckLinkDeviceMode *> inputModeIdMap;
|
||||
std::vector<DeckLinkDeviceMode *> inputModes;
|
||||
std::vector<DeckLinkDeviceMode *> inputModes;
|
||||
std::map<long long, DeckLinkDeviceMode *> outputModeIdMap;
|
||||
std::vector<DeckLinkDeviceMode *> outputModes;
|
||||
std::string name;
|
||||
std::string displayName;
|
||||
std::string hash;
|
||||
int32_t maxChannel = 0;
|
||||
decklink_bool_t supportsExternalKeyer = false;
|
||||
decklink_bool_t supportsInternalKeyer = false;
|
||||
int64_t subDeviceIndex = 0;
|
||||
int64_t numSubDevices = 0;
|
||||
int64_t supportedVideoInputConnections = -1;
|
||||
int64_t supportedVideoOutputConnections = -1;
|
||||
int64_t supportedAudioInputConnections = -1;
|
||||
int64_t supportedAudioOutputConnections = -1;
|
||||
int keyerMode = 0;
|
||||
volatile long refCount = 1;
|
||||
std::vector<DeckLinkDeviceMode *> outputModes;
|
||||
std::string name;
|
||||
std::string displayName;
|
||||
std::string hash;
|
||||
int32_t maxChannel = 0;
|
||||
decklink_bool_t supportsExternalKeyer = false;
|
||||
decklink_bool_t supportsInternalKeyer = false;
|
||||
int64_t subDeviceIndex = 0;
|
||||
int64_t numSubDevices = 0;
|
||||
int64_t supportedVideoInputConnections = -1;
|
||||
int64_t supportedVideoOutputConnections = -1;
|
||||
int64_t supportedAudioInputConnections = -1;
|
||||
int64_t supportedAudioOutputConnections = -1;
|
||||
int keyerMode = 0;
|
||||
volatile long refCount = 1;
|
||||
|
||||
public:
|
||||
DeckLinkDevice(IDeckLink *device);
|
||||
|
|
@ -41,10 +39,10 @@ public:
|
|||
|
||||
DeckLinkDeviceMode *FindInputMode(long long id);
|
||||
DeckLinkDeviceMode *FindOutputMode(long long id);
|
||||
const std::string& GetDisplayName(void);
|
||||
const std::string& GetHash(void) const;
|
||||
const std::vector<DeckLinkDeviceMode *>& GetInputModes(void) const;
|
||||
const std::vector<DeckLinkDeviceMode *>& GetOutputModes(void) const;
|
||||
const std::string &GetDisplayName(void);
|
||||
const std::string &GetHash(void) const;
|
||||
const std::vector<DeckLinkDeviceMode *> &GetInputModes(void) const;
|
||||
const std::vector<DeckLinkDeviceMode *> &GetOutputModes(void) const;
|
||||
int64_t GetVideoInputConnections();
|
||||
int64_t GetAudioInputConnections();
|
||||
bool GetSupportsExternalKeyer(void) const;
|
||||
|
|
@ -53,15 +51,12 @@ public:
|
|||
int64_t GetSubDeviceIndex();
|
||||
int GetKeyerMode(void);
|
||||
void SetKeyerMode(int newKeyerMode);
|
||||
const std::string& GetName(void) const;
|
||||
const std::string &GetName(void) const;
|
||||
int32_t GetMaxChannel(void) const;
|
||||
|
||||
bool GetInput(IDeckLinkInput **input);
|
||||
bool GetOutput(IDeckLinkOutput **output);
|
||||
bool GetKeyer(IDeckLinkKeyer **keyer);
|
||||
|
||||
inline bool IsDevice(IDeckLink *device_)
|
||||
{
|
||||
return device_ == device;
|
||||
}
|
||||
inline bool IsDevice(IDeckLink *device_) { return device_ == device; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ void fill_out_devices(obs_property_t *list)
|
|||
{
|
||||
deviceEnum->Lock();
|
||||
|
||||
const std::vector<DeckLinkDevice*> &devices = deviceEnum->GetDevices();
|
||||
const std::vector<DeckLinkDevice *> &devices = deviceEnum->GetDevices();
|
||||
for (DeckLinkDevice *device : devices) {
|
||||
obs_property_list_add_string(list,
|
||||
device->GetDisplayName().c_str(),
|
||||
device->GetHash().c_str());
|
||||
device->GetDisplayName().c_str(),
|
||||
device->GetHash().c_str());
|
||||
}
|
||||
|
||||
deviceEnum->Unlock();
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ static bool decklink_output_start(void *data)
|
|||
|
||||
decklink->audio_samplerate = aoi.samples_per_sec;
|
||||
decklink->audio_planes = 2;
|
||||
decklink->audio_size = get_audio_size(AUDIO_FORMAT_16BIT, aoi.speakers, 1);
|
||||
decklink->audio_size =
|
||||
get_audio_size(AUDIO_FORMAT_16BIT, aoi.speakers, 1);
|
||||
|
||||
decklink->start_timestamp = 0;
|
||||
|
||||
|
|
@ -68,7 +69,7 @@ static bool decklink_output_start(void *data)
|
|||
to.format = VIDEO_FORMAT_UYVY;
|
||||
}
|
||||
to.width = mode->GetWidth();
|
||||
to.height = mode->GetHeight();
|
||||
to.height = mode->GetHeight();
|
||||
|
||||
obs_output_set_video_conversion(decklink->GetOutput(), &to);
|
||||
|
||||
|
|
@ -78,7 +79,7 @@ static bool decklink_output_start(void *data)
|
|||
struct audio_convert_info conversion = {};
|
||||
conversion.format = AUDIO_FORMAT_16BIT;
|
||||
conversion.speakers = SPEAKERS_STEREO;
|
||||
conversion.samples_per_sec = 48000; // Only format the decklink supports
|
||||
conversion.samples_per_sec = 48000; // Only format the decklink supports
|
||||
|
||||
obs_output_set_audio_conversion(decklink->GetOutput(), &conversion);
|
||||
|
||||
|
|
@ -112,14 +113,14 @@ static void decklink_output_raw_video(void *data, struct video_data *frame)
|
|||
}
|
||||
|
||||
static bool prepare_audio(DeckLinkOutput *decklink,
|
||||
const struct audio_data *frame,
|
||||
struct audio_data *output)
|
||||
const struct audio_data *frame,
|
||||
struct audio_data *output)
|
||||
{
|
||||
*output = *frame;
|
||||
|
||||
if (frame->timestamp < decklink->start_timestamp) {
|
||||
uint64_t duration = (uint64_t)frame->frames * 1000000000 /
|
||||
(uint64_t)decklink->audio_samplerate;
|
||||
(uint64_t)decklink->audio_samplerate;
|
||||
uint64_t end_ts = frame->timestamp + duration;
|
||||
uint64_t cutoff;
|
||||
|
||||
|
|
@ -132,8 +133,8 @@ static bool prepare_audio(DeckLinkOutput *decklink,
|
|||
cutoff *= (uint64_t)decklink->audio_samplerate / 1000000000;
|
||||
|
||||
for (size_t i = 0; i < decklink->audio_planes; i++)
|
||||
output->data[i] += decklink->audio_size *
|
||||
(uint32_t)cutoff;
|
||||
output->data[i] +=
|
||||
decklink->audio_size * (uint32_t)cutoff;
|
||||
|
||||
output->frames -= (uint32_t)cutoff;
|
||||
}
|
||||
|
|
@ -156,7 +157,8 @@ static void decklink_output_raw_audio(void *data, struct audio_data *frames)
|
|||
}
|
||||
|
||||
static bool decklink_output_device_changed(obs_properties_t *props,
|
||||
obs_property_t *list, obs_data_t *settings)
|
||||
obs_property_t *list,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
const char *name = obs_data_get_string(settings, DEVICE_NAME);
|
||||
const char *hash = obs_data_get_string(settings, DEVICE_HASH);
|
||||
|
|
@ -193,13 +195,13 @@ static bool decklink_output_device_changed(obs_properties_t *props,
|
|||
obs_property_list_item_disable(modeList, 0, true);
|
||||
obs_property_list_item_disable(keyerList, 0, true);
|
||||
} else {
|
||||
const std::vector<DeckLinkDeviceMode*> &modes =
|
||||
device->GetOutputModes();
|
||||
const std::vector<DeckLinkDeviceMode *> &modes =
|
||||
device->GetOutputModes();
|
||||
|
||||
for (DeckLinkDeviceMode *mode : modes) {
|
||||
obs_property_list_add_int(modeList,
|
||||
mode->GetName().c_str(),
|
||||
mode->GetId());
|
||||
mode->GetName().c_str(),
|
||||
mode->GetId());
|
||||
}
|
||||
|
||||
obs_property_list_add_int(keyerList, "Disabled", 0);
|
||||
|
|
@ -222,22 +224,26 @@ static obs_properties_t *decklink_output_properties(void *unused)
|
|||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH,
|
||||
TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_set_modified_callback(list, decklink_output_device_changed);
|
||||
TEXT_DEVICE,
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_set_modified_callback(list,
|
||||
decklink_output_device_changed);
|
||||
|
||||
fill_out_devices(list);
|
||||
|
||||
obs_properties_add_list(props,
|
||||
MODE_ID, TEXT_MODE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_properties_add_list(props, MODE_ID, TEXT_MODE, OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
|
||||
obs_properties_add_bool(props, AUTO_START, TEXT_AUTO_START);
|
||||
|
||||
obs_properties_add_list(props, KEYER, TEXT_ENABLE_KEYER, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_properties_add_list(props, KEYER, TEXT_ENABLE_KEYER,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static const char *decklink_output_get_name(void*)
|
||||
static const char *decklink_output_get_name(void *)
|
||||
{
|
||||
return obs_module_text("BlackmagicDevice");
|
||||
}
|
||||
|
|
@ -246,17 +252,17 @@ struct obs_output_info create_decklink_output_info()
|
|||
{
|
||||
struct obs_output_info decklink_output_info = {};
|
||||
|
||||
decklink_output_info.id = "decklink_output";
|
||||
decklink_output_info.flags = OBS_OUTPUT_AV;
|
||||
decklink_output_info.get_name = decklink_output_get_name;
|
||||
decklink_output_info.create = decklink_output_create;
|
||||
decklink_output_info.destroy = decklink_output_destroy;
|
||||
decklink_output_info.start = decklink_output_start;
|
||||
decklink_output_info.stop = decklink_output_stop;
|
||||
decklink_output_info.id = "decklink_output";
|
||||
decklink_output_info.flags = OBS_OUTPUT_AV;
|
||||
decklink_output_info.get_name = decklink_output_get_name;
|
||||
decklink_output_info.create = decklink_output_create;
|
||||
decklink_output_info.destroy = decklink_output_destroy;
|
||||
decklink_output_info.start = decklink_output_start;
|
||||
decklink_output_info.stop = decklink_output_stop;
|
||||
decklink_output_info.get_properties = decklink_output_properties;
|
||||
decklink_output_info.raw_video = decklink_output_raw_video;
|
||||
decklink_output_info.raw_audio = decklink_output_raw_audio;
|
||||
decklink_output_info.update = decklink_output_update;
|
||||
decklink_output_info.raw_video = decklink_output_raw_video;
|
||||
decklink_output_info.raw_audio = decklink_output_raw_audio;
|
||||
decklink_output_info.update = decklink_output_update;
|
||||
|
||||
return decklink_output_info;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ static void decklink_enable_buffering(DeckLinkInput *decklink, bool enabled)
|
|||
decklink->buffering = enabled;
|
||||
}
|
||||
|
||||
static void decklink_deactivate_when_not_showing(DeckLinkInput *decklink, bool dwns)
|
||||
static void decklink_deactivate_when_not_showing(DeckLinkInput *decklink,
|
||||
bool dwns)
|
||||
{
|
||||
decklink->dwns = dwns;
|
||||
}
|
||||
|
|
@ -26,7 +27,7 @@ static void *decklink_create(obs_data_t *settings, obs_source_t *source)
|
|||
|
||||
obs_source_set_async_decoupled(source, true);
|
||||
decklink_enable_buffering(decklink,
|
||||
obs_data_get_bool(settings, BUFFERING));
|
||||
obs_data_get_bool(settings, BUFFERING));
|
||||
|
||||
obs_source_update(source, settings);
|
||||
return decklink;
|
||||
|
|
@ -43,16 +44,18 @@ static void decklink_update(void *data, obs_data_t *settings)
|
|||
DeckLinkInput *decklink = (DeckLinkInput *)data;
|
||||
const char *hash = obs_data_get_string(settings, DEVICE_HASH);
|
||||
long long id = obs_data_get_int(settings, MODE_ID);
|
||||
BMDVideoConnection videoConnection = (BMDVideoConnection) obs_data_get_int(settings,
|
||||
VIDEO_CONNECTION);
|
||||
BMDAudioConnection audioConnection = (BMDAudioConnection) obs_data_get_int(settings,
|
||||
AUDIO_CONNECTION);
|
||||
BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings,
|
||||
PIXEL_FORMAT);
|
||||
video_colorspace colorSpace = (video_colorspace)obs_data_get_int(settings,
|
||||
COLOR_SPACE);
|
||||
video_range_type colorRange = (video_range_type)obs_data_get_int(settings,
|
||||
COLOR_RANGE);
|
||||
BMDVideoConnection videoConnection =
|
||||
(BMDVideoConnection)obs_data_get_int(settings,
|
||||
VIDEO_CONNECTION);
|
||||
BMDAudioConnection audioConnection =
|
||||
(BMDAudioConnection)obs_data_get_int(settings,
|
||||
AUDIO_CONNECTION);
|
||||
BMDPixelFormat pixelFormat =
|
||||
(BMDPixelFormat)obs_data_get_int(settings, PIXEL_FORMAT);
|
||||
video_colorspace colorSpace =
|
||||
(video_colorspace)obs_data_get_int(settings, COLOR_SPACE);
|
||||
video_range_type colorRange =
|
||||
(video_range_type)obs_data_get_int(settings, COLOR_RANGE);
|
||||
int chFmtInt = (int)obs_data_get_int(settings, CHANNEL_FORMAT);
|
||||
|
||||
if (chFmtInt == 7)
|
||||
|
|
@ -63,10 +66,10 @@ static void decklink_update(void *data, obs_data_t *settings)
|
|||
speaker_layout channelFormat = (speaker_layout)chFmtInt;
|
||||
|
||||
decklink_enable_buffering(decklink,
|
||||
obs_data_get_bool(settings, BUFFERING));
|
||||
obs_data_get_bool(settings, BUFFERING));
|
||||
|
||||
decklink_deactivate_when_not_showing(decklink,
|
||||
obs_data_get_bool(settings, DEACTIVATE_WNS));
|
||||
decklink_deactivate_when_not_showing(
|
||||
decklink, obs_data_get_bool(settings, DEACTIVATE_WNS));
|
||||
|
||||
ComPtr<DeckLinkDevice> device;
|
||||
device.Set(deviceEnum->FindByHash(hash));
|
||||
|
|
@ -88,8 +91,9 @@ static void decklink_show(void *data)
|
|||
if (decklink->dwns && showing && !decklink->Capturing()) {
|
||||
ComPtr<DeckLinkDevice> device;
|
||||
device.Set(deviceEnum->FindByHash(decklink->hash.c_str()));
|
||||
decklink->Activate(device, decklink->id, decklink->videoConnection,
|
||||
decklink->audioConnection);
|
||||
decklink->Activate(device, decklink->id,
|
||||
decklink->videoConnection,
|
||||
decklink->audioConnection);
|
||||
}
|
||||
}
|
||||
static void decklink_hide(void *data)
|
||||
|
|
@ -111,13 +115,13 @@ static void decklink_get_defaults(obs_data_t *settings)
|
|||
obs_data_set_default_bool(settings, SWAP, false);
|
||||
}
|
||||
|
||||
static const char *decklink_get_name(void*)
|
||||
static const char *decklink_get_name(void *)
|
||||
{
|
||||
return obs_module_text("BlackmagicDevice");
|
||||
}
|
||||
|
||||
static bool decklink_device_changed(obs_properties_t *props,
|
||||
obs_property_t *list, obs_data_t *settings)
|
||||
obs_property_t *list, obs_data_t *settings)
|
||||
{
|
||||
const char *name = obs_data_get_string(settings, DEVICE_NAME);
|
||||
const char *hash = obs_data_get_string(settings, DEVICE_HASH);
|
||||
|
|
@ -140,10 +144,10 @@ static bool decklink_device_changed(obs_properties_t *props,
|
|||
obs_property_list_item_disable(list, 0, true);
|
||||
}
|
||||
|
||||
obs_property_t *videoConnectionList = obs_properties_get(props,
|
||||
VIDEO_CONNECTION);
|
||||
obs_property_t *audioConnectionList = obs_properties_get(props,
|
||||
AUDIO_CONNECTION);
|
||||
obs_property_t *videoConnectionList =
|
||||
obs_properties_get(props, VIDEO_CONNECTION);
|
||||
obs_property_t *audioConnectionList =
|
||||
obs_properties_get(props, AUDIO_CONNECTION);
|
||||
obs_property_t *modeList = obs_properties_get(props, MODE_ID);
|
||||
obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT);
|
||||
|
||||
|
|
@ -154,9 +158,9 @@ static bool decklink_device_changed(obs_properties_t *props,
|
|||
|
||||
obs_property_list_clear(channelList);
|
||||
obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_NONE,
|
||||
SPEAKERS_UNKNOWN);
|
||||
SPEAKERS_UNKNOWN);
|
||||
obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_0CH,
|
||||
SPEAKERS_STEREO);
|
||||
SPEAKERS_STEREO);
|
||||
|
||||
ComPtr<DeckLinkDevice> device;
|
||||
device.Set(deviceEnum->FindByHash(hash));
|
||||
|
|
@ -168,60 +172,75 @@ static bool decklink_device_changed(obs_properties_t *props,
|
|||
obs_property_list_item_disable(modeList, 0, true);
|
||||
} else {
|
||||
const BMDVideoConnection BMDVideoConnections[] = {
|
||||
bmdVideoConnectionSDI, bmdVideoConnectionHDMI,
|
||||
bmdVideoConnectionOpticalSDI, bmdVideoConnectionComponent,
|
||||
bmdVideoConnectionComposite, bmdVideoConnectionSVideo
|
||||
};
|
||||
bmdVideoConnectionSDI,
|
||||
bmdVideoConnectionHDMI,
|
||||
bmdVideoConnectionOpticalSDI,
|
||||
bmdVideoConnectionComponent,
|
||||
bmdVideoConnectionComposite,
|
||||
bmdVideoConnectionSVideo};
|
||||
|
||||
for (BMDVideoConnection conn : BMDVideoConnections) {
|
||||
if ((device->GetVideoInputConnections() & conn) == conn) {
|
||||
obs_property_list_add_int(videoConnectionList,
|
||||
bmd_video_connection_to_name(conn), conn);
|
||||
if ((device->GetVideoInputConnections() & conn) ==
|
||||
conn) {
|
||||
obs_property_list_add_int(
|
||||
videoConnectionList,
|
||||
bmd_video_connection_to_name(conn),
|
||||
conn);
|
||||
}
|
||||
}
|
||||
|
||||
const BMDAudioConnection BMDAudioConnections[] = {
|
||||
bmdAudioConnectionEmbedded, bmdAudioConnectionAESEBU,
|
||||
bmdAudioConnectionAnalog, bmdAudioConnectionAnalogXLR,
|
||||
bmdAudioConnectionAnalogRCA, bmdAudioConnectionMicrophone,
|
||||
bmdAudioConnectionHeadphones
|
||||
};
|
||||
bmdAudioConnectionEmbedded,
|
||||
bmdAudioConnectionAESEBU,
|
||||
bmdAudioConnectionAnalog,
|
||||
bmdAudioConnectionAnalogXLR,
|
||||
bmdAudioConnectionAnalogRCA,
|
||||
bmdAudioConnectionMicrophone,
|
||||
bmdAudioConnectionHeadphones};
|
||||
|
||||
for (BMDAudioConnection conn : BMDAudioConnections) {
|
||||
if ((device->GetAudioInputConnections() & conn) == conn) {
|
||||
obs_property_list_add_int(audioConnectionList,
|
||||
bmd_audio_connection_to_name(conn), conn);
|
||||
if ((device->GetAudioInputConnections() & conn) ==
|
||||
conn) {
|
||||
obs_property_list_add_int(
|
||||
audioConnectionList,
|
||||
bmd_audio_connection_to_name(conn),
|
||||
conn);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<DeckLinkDeviceMode*> &modes =
|
||||
device->GetInputModes();
|
||||
const std::vector<DeckLinkDeviceMode *> &modes =
|
||||
device->GetInputModes();
|
||||
|
||||
for (DeckLinkDeviceMode *mode : modes) {
|
||||
obs_property_list_add_int(modeList,
|
||||
mode->GetName().c_str(),
|
||||
mode->GetId());
|
||||
mode->GetName().c_str(),
|
||||
mode->GetId());
|
||||
}
|
||||
|
||||
if (device->GetMaxChannel() >= 8) {
|
||||
obs_property_list_add_int(channelList,
|
||||
TEXT_CHANNEL_FORMAT_2_1CH, SPEAKERS_2POINT1);
|
||||
TEXT_CHANNEL_FORMAT_2_1CH,
|
||||
SPEAKERS_2POINT1);
|
||||
obs_property_list_add_int(channelList,
|
||||
TEXT_CHANNEL_FORMAT_4_0CH, SPEAKERS_4POINT0);
|
||||
TEXT_CHANNEL_FORMAT_4_0CH,
|
||||
SPEAKERS_4POINT0);
|
||||
obs_property_list_add_int(channelList,
|
||||
TEXT_CHANNEL_FORMAT_4_1CH, SPEAKERS_4POINT1);
|
||||
TEXT_CHANNEL_FORMAT_4_1CH,
|
||||
SPEAKERS_4POINT1);
|
||||
obs_property_list_add_int(channelList,
|
||||
TEXT_CHANNEL_FORMAT_5_1CH, SPEAKERS_5POINT1);
|
||||
TEXT_CHANNEL_FORMAT_5_1CH,
|
||||
SPEAKERS_5POINT1);
|
||||
obs_property_list_add_int(channelList,
|
||||
TEXT_CHANNEL_FORMAT_7_1CH, SPEAKERS_7POINT1);
|
||||
TEXT_CHANNEL_FORMAT_7_1CH,
|
||||
SPEAKERS_7POINT1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mode_id_changed(obs_properties_t *props,
|
||||
obs_property_t *list, obs_data_t *settings)
|
||||
static bool mode_id_changed(obs_properties_t *props, obs_property_t *list,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
long long id = obs_data_get_int(settings, MODE_ID);
|
||||
|
||||
|
|
@ -236,56 +255,65 @@ static obs_properties_t *decklink_get_properties(void *data)
|
|||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH,
|
||||
TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
TEXT_DEVICE,
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_set_modified_callback(list, decklink_device_changed);
|
||||
|
||||
fill_out_devices(list);
|
||||
|
||||
obs_properties_add_list(props, VIDEO_CONNECTION, TEXT_VIDEO_CONNECTION,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_properties_add_list(props, AUDIO_CONNECTION, TEXT_AUDIO_CONNECTION,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
|
||||
list = obs_properties_add_list(props, MODE_ID, TEXT_MODE,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_modified_callback(list, mode_id_changed);
|
||||
|
||||
list = obs_properties_add_list(props, PIXEL_FORMAT,
|
||||
TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
list = obs_properties_add_list(props, PIXEL_FORMAT, TEXT_PIXEL_FORMAT,
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
|
||||
obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV);
|
||||
obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA);
|
||||
|
||||
list = obs_properties_add_list(props, COLOR_SPACE, TEXT_COLOR_SPACE,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_list_add_int(list, TEXT_COLOR_SPACE_DEFAULT, VIDEO_CS_DEFAULT);
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_list_add_int(list, TEXT_COLOR_SPACE_DEFAULT,
|
||||
VIDEO_CS_DEFAULT);
|
||||
obs_property_list_add_int(list, "BT.601", VIDEO_CS_601);
|
||||
obs_property_list_add_int(list, "BT.709", VIDEO_CS_709);
|
||||
|
||||
list = obs_properties_add_list(props, COLOR_RANGE, TEXT_COLOR_RANGE,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_list_add_int(list, TEXT_COLOR_RANGE_DEFAULT, VIDEO_RANGE_DEFAULT);
|
||||
obs_property_list_add_int(list, TEXT_COLOR_RANGE_PARTIAL, VIDEO_RANGE_PARTIAL);
|
||||
obs_property_list_add_int(list, TEXT_COLOR_RANGE_FULL, VIDEO_RANGE_FULL);
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_list_add_int(list, TEXT_COLOR_RANGE_DEFAULT,
|
||||
VIDEO_RANGE_DEFAULT);
|
||||
obs_property_list_add_int(list, TEXT_COLOR_RANGE_PARTIAL,
|
||||
VIDEO_RANGE_PARTIAL);
|
||||
obs_property_list_add_int(list, TEXT_COLOR_RANGE_FULL,
|
||||
VIDEO_RANGE_FULL);
|
||||
|
||||
list = obs_properties_add_list(props, CHANNEL_FORMAT,
|
||||
TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_NONE,
|
||||
SPEAKERS_UNKNOWN);
|
||||
SPEAKERS_UNKNOWN);
|
||||
obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_0CH,
|
||||
SPEAKERS_STEREO);
|
||||
SPEAKERS_STEREO);
|
||||
obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_1CH,
|
||||
SPEAKERS_2POINT1);
|
||||
SPEAKERS_2POINT1);
|
||||
obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_0CH,
|
||||
SPEAKERS_4POINT0);
|
||||
SPEAKERS_4POINT0);
|
||||
obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_1CH,
|
||||
SPEAKERS_4POINT1);
|
||||
SPEAKERS_4POINT1);
|
||||
obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_5_1CH,
|
||||
SPEAKERS_5POINT1);
|
||||
SPEAKERS_5POINT1);
|
||||
obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_7_1CH,
|
||||
SPEAKERS_7POINT1);
|
||||
SPEAKERS_7POINT1);
|
||||
|
||||
obs_property_t *swap = obs_properties_add_bool(props, SWAP, TEXT_SWAP);
|
||||
obs_property_set_long_description(swap, TEXT_SWAP_TOOLTIP);
|
||||
|
|
@ -298,21 +326,22 @@ static obs_properties_t *decklink_get_properties(void *data)
|
|||
return props;
|
||||
}
|
||||
|
||||
|
||||
struct obs_source_info create_decklink_source_info()
|
||||
{
|
||||
struct obs_source_info decklink_source_info = {};
|
||||
decklink_source_info.id = "decklink-input";
|
||||
decklink_source_info.type = OBS_SOURCE_TYPE_INPUT;
|
||||
decklink_source_info.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE;
|
||||
decklink_source_info.create = decklink_create;
|
||||
decklink_source_info.destroy = decklink_destroy;
|
||||
decklink_source_info.get_defaults = decklink_get_defaults;
|
||||
decklink_source_info.get_name = decklink_get_name;
|
||||
decklink_source_info.id = "decklink-input";
|
||||
decklink_source_info.type = OBS_SOURCE_TYPE_INPUT;
|
||||
decklink_source_info.output_flags = OBS_SOURCE_ASYNC_VIDEO |
|
||||
OBS_SOURCE_AUDIO |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE;
|
||||
decklink_source_info.create = decklink_create;
|
||||
decklink_source_info.destroy = decklink_destroy;
|
||||
decklink_source_info.get_defaults = decklink_get_defaults;
|
||||
decklink_source_info.get_name = decklink_get_name;
|
||||
decklink_source_info.get_properties = decklink_get_properties;
|
||||
decklink_source_info.update = decklink_update;
|
||||
decklink_source_info.show = decklink_show;
|
||||
decklink_source_info.hide = decklink_hide;
|
||||
decklink_source_info.update = decklink_update;
|
||||
decklink_source_info.show = decklink_show;
|
||||
decklink_source_info.hide = decklink_hide;
|
||||
|
||||
return decklink_source_info;
|
||||
}
|
||||
|
|
|
|||
3
plugins/decklink/linux/decklink-sdk/.clang-format
Normal file
3
plugins/decklink/linux/decklink-sdk/.clang-format
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Language: Cpp
|
||||
SortIncludes: false
|
||||
DisableFormat: true
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#include "../platform.hpp"
|
||||
|
||||
bool DeckLinkStringToStdString(decklink_string_t input, std::string& output)
|
||||
bool DeckLinkStringToStdString(decklink_string_t input, std::string &output)
|
||||
{
|
||||
if (input == nullptr)
|
||||
return false;
|
||||
|
|
|
|||
3
plugins/decklink/mac/decklink-sdk/.clang-format
Normal file
3
plugins/decklink/mac/decklink-sdk/.clang-format
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Language: Cpp
|
||||
SortIncludes: false
|
||||
DisableFormat: true
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include "../platform.hpp"
|
||||
#include <util/apple/cfstring-utils.h>
|
||||
|
||||
bool DeckLinkStringToStdString(decklink_string_t input, std::string& output)
|
||||
bool DeckLinkStringToStdString(decklink_string_t input, std::string &output)
|
||||
{
|
||||
const CFStringRef string = static_cast<CFStringRef>(input);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,4 +24,4 @@ typedef const char *decklink_string_t;
|
|||
|
||||
#include <string>
|
||||
|
||||
bool DeckLinkStringToStdString(decklink_string_t input, std::string& output);
|
||||
bool DeckLinkStringToStdString(decklink_string_t input, std::string &output);
|
||||
|
|
|
|||
|
|
@ -3,41 +3,41 @@
|
|||
const char *bmd_video_connection_to_name(BMDVideoConnection connection)
|
||||
{
|
||||
switch (connection) {
|
||||
case bmdVideoConnectionSDI:
|
||||
return "SDI";
|
||||
case bmdVideoConnectionHDMI:
|
||||
return "HDMI";
|
||||
case bmdVideoConnectionOpticalSDI:
|
||||
return "Optical SDI";
|
||||
case bmdVideoConnectionComponent:
|
||||
return "Component";
|
||||
case bmdVideoConnectionComposite:
|
||||
return "Composite";
|
||||
case bmdVideoConnectionSVideo:
|
||||
return "S-Video";
|
||||
default:
|
||||
return "Unknown";
|
||||
case bmdVideoConnectionSDI:
|
||||
return "SDI";
|
||||
case bmdVideoConnectionHDMI:
|
||||
return "HDMI";
|
||||
case bmdVideoConnectionOpticalSDI:
|
||||
return "Optical SDI";
|
||||
case bmdVideoConnectionComponent:
|
||||
return "Component";
|
||||
case bmdVideoConnectionComposite:
|
||||
return "Composite";
|
||||
case bmdVideoConnectionSVideo:
|
||||
return "S-Video";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
const char *bmd_audio_connection_to_name(BMDAudioConnection connection)
|
||||
{
|
||||
switch (connection) {
|
||||
case bmdAudioConnectionEmbedded:
|
||||
return "Embedded";
|
||||
case bmdAudioConnectionAESEBU:
|
||||
return "AES/EBU";
|
||||
case bmdAudioConnectionAnalog:
|
||||
return "Analog";
|
||||
case bmdAudioConnectionAnalogXLR:
|
||||
return "Analog XLR";
|
||||
case bmdAudioConnectionAnalogRCA:
|
||||
return "Analog RCA";
|
||||
case bmdAudioConnectionMicrophone:
|
||||
return "Microphone";
|
||||
case bmdAudioConnectionHeadphones:
|
||||
return "Headphones";
|
||||
default:
|
||||
return "Unknown";
|
||||
case bmdAudioConnectionEmbedded:
|
||||
return "Embedded";
|
||||
case bmdAudioConnectionAESEBU:
|
||||
return "AES/EBU";
|
||||
case bmdAudioConnectionAnalog:
|
||||
return "Analog";
|
||||
case bmdAudioConnectionAnalogXLR:
|
||||
return "Analog XLR";
|
||||
case bmdAudioConnectionAnalogRCA:
|
||||
return "Analog RCA";
|
||||
case bmdAudioConnectionMicrophone:
|
||||
return "Microphone";
|
||||
case bmdAudioConnectionHeadphones:
|
||||
return "Headphones";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,13 @@
|
|||
IDeckLinkDiscovery *CreateDeckLinkDiscoveryInstance(void)
|
||||
{
|
||||
IDeckLinkDiscovery *instance;
|
||||
const HRESULT result = CoCreateInstance(CLSID_CDeckLinkDiscovery,
|
||||
nullptr, CLSCTX_ALL, IID_IDeckLinkDiscovery,
|
||||
(void **)&instance);
|
||||
const HRESULT result =
|
||||
CoCreateInstance(CLSID_CDeckLinkDiscovery, nullptr, CLSCTX_ALL,
|
||||
IID_IDeckLinkDiscovery, (void **)&instance);
|
||||
return result == S_OK ? instance : nullptr;
|
||||
}
|
||||
|
||||
bool DeckLinkStringToStdString(decklink_string_t input, std::string& output)
|
||||
bool DeckLinkStringToStdString(decklink_string_t input, std::string &output)
|
||||
{
|
||||
if (input == nullptr)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -51,13 +51,15 @@ static obs_properties_t *color_source_properties(void *unused)
|
|||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_properties_add_color(props, "color",
|
||||
obs_module_text("ColorSource.Color"));
|
||||
obs_module_text("ColorSource.Color"));
|
||||
|
||||
obs_properties_add_int(props, "width",
|
||||
obs_module_text("ColorSource.Width"), 0, 4096, 1);
|
||||
obs_module_text("ColorSource.Width"), 0, 4096,
|
||||
1);
|
||||
|
||||
obs_properties_add_int(props, "height",
|
||||
obs_module_text("ColorSource.Height"), 0, 4096, 1);
|
||||
obs_module_text("ColorSource.Height"), 0, 4096,
|
||||
1);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
|
@ -68,9 +70,9 @@ static void color_source_render(void *data, gs_effect_t *effect)
|
|||
|
||||
struct color_source *context = data;
|
||||
|
||||
gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
|
||||
gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
|
||||
gs_technique_t *tech = gs_effect_get_technique(solid, "Solid");
|
||||
gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
|
||||
gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
|
||||
gs_technique_t *tech = gs_effect_get_technique(solid, "Solid");
|
||||
|
||||
struct vec4 colorVal;
|
||||
vec4_from_rgba(&colorVal, context->color);
|
||||
|
|
@ -99,25 +101,22 @@ static uint32_t color_source_getheight(void *data)
|
|||
|
||||
static void color_source_defaults(obs_data_t *settings)
|
||||
{
|
||||
struct obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
|
||||
obs_data_set_default_int(settings, "color", 0xFFFFFFFF);
|
||||
obs_data_set_default_int(settings, "width", ovi.base_width);
|
||||
obs_data_set_default_int(settings, "height", ovi.base_height);
|
||||
obs_data_set_default_int(settings, "width", 400);
|
||||
obs_data_set_default_int(settings, "height", 400);
|
||||
}
|
||||
|
||||
struct obs_source_info color_source_info = {
|
||||
.id = "color_source",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
|
||||
.create = color_source_create,
|
||||
.destroy = color_source_destroy,
|
||||
.update = color_source_update,
|
||||
.get_name = color_source_get_name,
|
||||
.get_defaults = color_source_defaults,
|
||||
.get_width = color_source_getwidth,
|
||||
.get_height = color_source_getheight,
|
||||
.video_render = color_source_render,
|
||||
.get_properties = color_source_properties
|
||||
.id = "color_source",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
|
||||
.create = color_source_create,
|
||||
.destroy = color_source_destroy,
|
||||
.update = color_source_update,
|
||||
.get_name = color_source_get_name,
|
||||
.get_defaults = color_source_defaults,
|
||||
.get_width = color_source_getwidth,
|
||||
.get_height = color_source_getheight,
|
||||
.video_render = color_source_render,
|
||||
.get_properties = color_source_properties,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ SlideShow.Loop="Bucle"
|
|||
SlideShow.Transition="Transición"
|
||||
SlideShow.Transition.Cut="Corte"
|
||||
SlideShow.Transition.Fade="Desvanecimiento"
|
||||
SlideShow.Transition.Swipe="Deslizar Rapido"
|
||||
SlideShow.Transition.Slide="Deslizar"
|
||||
SlideShow.Transition.Swipe="Deslizar"
|
||||
SlideShow.Transition.Slide="Diapositiva"
|
||||
SlideShow.PlaybackBehavior="Comportamiento de visibilidad"
|
||||
SlideShow.PlaybackBehavior.StopRestart="Detener cuando no sea visible, reiniciar cuando sea visible"
|
||||
SlideShow.PlaybackBehavior.PauseUnpause="Pausar cuando no sea visible, reanudar cuando sea visible"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,36 @@
|
|||
ImageInput="Imaxe"
|
||||
File="Ficheiro de imaxe"
|
||||
UnloadWhenNotShowing="Non descargar a imaxe cando non se mostre"
|
||||
UnloadWhenNotShowing="Descargar a imaxe cando non se amosa"
|
||||
|
||||
SlideShow="Diaporama"
|
||||
SlideShow.TransitionSpeed="Velocidade da transición (milisegundos)"
|
||||
SlideShow.SlideTime="Tempo entre diapositivas (milisegundos)"
|
||||
SlideShow.Files="Ficheiros de imaxe"
|
||||
SlideShow.CustomSize="Límite do tamaño/relación de aspecto"
|
||||
SlideShow.CustomSize.Auto="Automático"
|
||||
SlideShow.Randomize="Reproducir ao chou"
|
||||
SlideShow.Loop="Bucle"
|
||||
SlideShow.Transition="Transición"
|
||||
SlideShow.Transition.Cut="Cortar"
|
||||
SlideShow.Transition.Fade="Esvaecer"
|
||||
SlideShow.Transition.Swipe="Esvarar"
|
||||
SlideShow.Transition.Slide="Diapositiva"
|
||||
SlideShow.PlaybackBehavior="Comportamento da visibilidade"
|
||||
SlideShow.PlaybackBehavior.StopRestart="Parar cando non é visíbel, reiniciar cando sexa visíbel"
|
||||
SlideShow.PlaybackBehavior.PauseUnpause="Pór en pausa cando non é visíbel, reiniciar cando sexa visíbel"
|
||||
SlideShow.PlaybackBehavior.AlwaysPlay="Reproducir sempre cando non estea visíbel"
|
||||
SlideShow.SlideMode="Modo de diapositiva"
|
||||
SlideShow.SlideMode.Auto="Automático"
|
||||
SlideShow.SlideMode.Manual="Manual (use as teclas rápidas para controlar o disporama)"
|
||||
SlideShow.PlayPause="Reproducir/Pór en pausa"
|
||||
SlideShow.Restart="Reiniciar"
|
||||
SlideShow.Stop="Parar"
|
||||
SlideShow.NextSlide="Seguinte diapositiva"
|
||||
SlideShow.PreviousSlide="Diapositiva anterior"
|
||||
SlideShow.HideWhenDone="Agochar cando se reproduce o diaporama"
|
||||
|
||||
ColorSource="Orixe da cor"
|
||||
ColorSource.Color="Cor"
|
||||
ColorSource.Width="Largo"
|
||||
ColorSource.Height="Alto"
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ SlideShow.Transition.Cut="Decupare"
|
|||
SlideShow.Transition.Fade="Estompare"
|
||||
SlideShow.Transition.Swipe="Glisare"
|
||||
SlideShow.Transition.Slide="Culisare"
|
||||
SlideShow.SlideMode="Mod de culisare"
|
||||
SlideShow.SlideMode.Auto="Automat"
|
||||
SlideShow.PlayPause="Redă/Pune pe pauză"
|
||||
SlideShow.Restart="Repornește"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,36 @@
|
|||
ImageInput="Slika"
|
||||
File="Slikovna datoteka"
|
||||
UnloadWhenNotShowing="Ne naloži slike, ko ni prikazana"
|
||||
UnloadWhenNotShowing="Razloži sliko, ko ni prikazana"
|
||||
|
||||
SlideShow="Slikovna predstavitev"
|
||||
SlideShow.TransitionSpeed="Hitrost prehodov (ms)"
|
||||
SlideShow.SlideTime="Čas med slikami (ms)"
|
||||
SlideShow.Files="Slikovne datoteke"
|
||||
SlideShow.CustomSize="Omejitev velikost/Razmerje"
|
||||
SlideShow.CustomSize.Auto="Samodejno"
|
||||
SlideShow.Randomize="Naključno predvajanje"
|
||||
SlideShow.Loop="Ponavljaj"
|
||||
SlideShow.Transition="Prehod"
|
||||
SlideShow.Transition.Cut="Izreži"
|
||||
SlideShow.Transition.Fade="Pojemaj"
|
||||
SlideShow.Transition.Swipe="Potegni"
|
||||
SlideShow.Transition.Slide="Podrsaj"
|
||||
SlideShow.PlaybackBehavior="Vedenje vidnosti"
|
||||
SlideShow.PlaybackBehavior.StopRestart="Ustavi, ko ni vidno; ponovno zaženi, ko je vidno"
|
||||
SlideShow.PlaybackBehavior.PauseUnpause="Premor, ko ni vidno; nadaljuj, ko je vidno"
|
||||
SlideShow.PlaybackBehavior.AlwaysPlay="Vedno predvajaj, ko ni vidno"
|
||||
SlideShow.SlideMode="Način predstavitve"
|
||||
SlideShow.SlideMode.Auto="Samodejno"
|
||||
SlideShow.SlideMode.Manual="Ročno (uporabi hitre tipke na nadzor predstavitve)"
|
||||
SlideShow.PlayPause="Predvajaj/Premor"
|
||||
SlideShow.Restart="Ponovno zaženi"
|
||||
SlideShow.Stop="Ustavi"
|
||||
SlideShow.NextSlide="Naslednja slika"
|
||||
SlideShow.PreviousSlide="Prejšnja slika"
|
||||
SlideShow.HideWhenDone="Skrij, ko je predstavitev končana"
|
||||
|
||||
ColorSource="Barvni vir"
|
||||
ColorSource.Color="Barva"
|
||||
ColorSource.Width="Širina"
|
||||
ColorSource.Height="Višina"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,31 +4,27 @@
|
|||
#include <util/dstr.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define blog(log_level, format, ...) \
|
||||
#define blog(log_level, format, ...) \
|
||||
blog(log_level, "[image_source: '%s'] " format, \
|
||||
obs_source_get_name(context->source), ##__VA_ARGS__)
|
||||
obs_source_get_name(context->source), ##__VA_ARGS__)
|
||||
|
||||
#define debug(format, ...) \
|
||||
blog(LOG_DEBUG, format, ##__VA_ARGS__)
|
||||
#define info(format, ...) \
|
||||
blog(LOG_INFO, format, ##__VA_ARGS__)
|
||||
#define warn(format, ...) \
|
||||
blog(LOG_WARNING, format, ##__VA_ARGS__)
|
||||
#define debug(format, ...) blog(LOG_DEBUG, format, ##__VA_ARGS__)
|
||||
#define info(format, ...) blog(LOG_INFO, format, ##__VA_ARGS__)
|
||||
#define warn(format, ...) blog(LOG_WARNING, format, ##__VA_ARGS__)
|
||||
|
||||
struct image_source {
|
||||
obs_source_t *source;
|
||||
|
||||
char *file;
|
||||
bool persistent;
|
||||
time_t file_timestamp;
|
||||
float update_time_elapsed;
|
||||
uint64_t last_time;
|
||||
bool active;
|
||||
char *file;
|
||||
bool persistent;
|
||||
time_t file_timestamp;
|
||||
float update_time_elapsed;
|
||||
uint64_t last_time;
|
||||
bool active;
|
||||
|
||||
gs_image_file2_t if2;
|
||||
};
|
||||
|
||||
|
||||
static time_t get_modified_timestamp(const char *filename)
|
||||
{
|
||||
struct stat stats;
|
||||
|
|
@ -152,9 +148,9 @@ static void image_source_render(void *data, gs_effect_t *effect)
|
|||
return;
|
||||
|
||||
gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"),
|
||||
context->if2.image.texture);
|
||||
gs_draw_sprite(context->if2.image.texture, 0,
|
||||
context->if2.image.cx, context->if2.image.cy);
|
||||
context->if2.image.texture);
|
||||
gs_draw_sprite(context->if2.image.texture, 0, context->if2.image.cx,
|
||||
context->if2.image.cy);
|
||||
}
|
||||
|
||||
static void image_source_tick(void *data, float seconds)
|
||||
|
|
@ -212,7 +208,6 @@ static void image_source_tick(void *data, float seconds)
|
|||
context->last_time = frame_time;
|
||||
}
|
||||
|
||||
|
||||
static const char *image_filter =
|
||||
"All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif *.psd);;"
|
||||
"BMP Files (*.bmp);;"
|
||||
|
|
@ -240,11 +235,10 @@ static obs_properties_t *image_source_properties(void *data)
|
|||
dstr_resize(&path, slash - path.array + 1);
|
||||
}
|
||||
|
||||
obs_properties_add_path(props,
|
||||
"file", obs_module_text("File"),
|
||||
OBS_PATH_FILE, image_filter, path.array);
|
||||
obs_properties_add_bool(props,
|
||||
"unload", obs_module_text("UnloadWhenNotShowing"));
|
||||
obs_properties_add_path(props, "file", obs_module_text("File"),
|
||||
OBS_PATH_FILE, image_filter, path.array);
|
||||
obs_properties_add_bool(props, "unload",
|
||||
obs_module_text("UnloadWhenNotShowing"));
|
||||
dstr_free(&path);
|
||||
|
||||
return props;
|
||||
|
|
@ -257,22 +251,21 @@ uint64_t image_source_get_memory_usage(void *data)
|
|||
}
|
||||
|
||||
static struct obs_source_info image_source_info = {
|
||||
.id = "image_source",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO,
|
||||
.get_name = image_source_get_name,
|
||||
.create = image_source_create,
|
||||
.destroy = image_source_destroy,
|
||||
.update = image_source_update,
|
||||
.get_defaults = image_source_defaults,
|
||||
.show = image_source_show,
|
||||
.hide = image_source_hide,
|
||||
.get_width = image_source_getwidth,
|
||||
.get_height = image_source_getheight,
|
||||
.video_render = image_source_render,
|
||||
.video_tick = image_source_tick,
|
||||
.get_properties = image_source_properties
|
||||
};
|
||||
.id = "image_source",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO,
|
||||
.get_name = image_source_get_name,
|
||||
.create = image_source_create,
|
||||
.destroy = image_source_destroy,
|
||||
.update = image_source_update,
|
||||
.get_defaults = image_source_defaults,
|
||||
.show = image_source_show,
|
||||
.hide = image_source_hide,
|
||||
.get_width = image_source_getwidth,
|
||||
.get_height = image_source_getheight,
|
||||
.video_render = image_source_render,
|
||||
.video_tick = image_source_tick,
|
||||
.get_properties = image_source_properties};
|
||||
|
||||
OBS_DECLARE_MODULE()
|
||||
OBS_MODULE_USE_DEFAULT_LOCALE("image-source", "en-US")
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@
|
|||
#include <util/darray.h>
|
||||
#include <util/dstr.h>
|
||||
|
||||
#define do_log(level, format, ...) \
|
||||
#define do_log(level, format, ...) \
|
||||
blog(level, "[slideshow: '%s'] " format, \
|
||||
obs_source_get_name(ss->source), ##__VA_ARGS__)
|
||||
obs_source_get_name(ss->source), ##__VA_ARGS__)
|
||||
|
||||
#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
|
||||
#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define S_TR_SPEED "transition_speed"
|
||||
#define S_CUSTOM_SIZE "use_custom_size"
|
||||
|
|
@ -55,12 +57,14 @@
|
|||
#define T_TR_SWIPE T_TR_("Swipe")
|
||||
#define T_TR_SLIDE T_TR_("Slide")
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
extern uint64_t image_source_get_memory_usage(void *data);
|
||||
|
||||
#define BYTES_TO_MBYTES (1024 * 1024)
|
||||
#define MAX_MEM_USAGE (250 * BYTES_TO_MBYTES)
|
||||
#define MAX_MEM_USAGE (250 * BYTES_TO_MBYTES)
|
||||
|
||||
struct image_file_data {
|
||||
char *path;
|
||||
|
|
@ -182,7 +186,7 @@ static const char *ss_getname(void *unused)
|
|||
}
|
||||
|
||||
static void add_file(struct slideshow *ss, struct darray *array,
|
||||
const char *path, uint32_t *cx, uint32_t *cy)
|
||||
const char *path, uint32_t *cx, uint32_t *cy)
|
||||
{
|
||||
DARRAY(struct image_file_data) new_files;
|
||||
struct image_file_data data;
|
||||
|
|
@ -207,8 +211,10 @@ static void add_file(struct slideshow *ss, struct darray *array,
|
|||
data.source = new_source;
|
||||
da_push_back(new_files, &data);
|
||||
|
||||
if (new_cx > *cx) *cx = new_cx;
|
||||
if (new_cy > *cy) *cy = new_cy;
|
||||
if (new_cx > *cx)
|
||||
*cx = new_cx;
|
||||
if (new_cy > *cy)
|
||||
*cy = new_cy;
|
||||
|
||||
void *source_data = obs_obj_get_data(new_source);
|
||||
ss->mem_usage += image_source_get_memory_usage(source_data);
|
||||
|
|
@ -221,12 +227,9 @@ static bool valid_extension(const char *ext)
|
|||
{
|
||||
if (!ext)
|
||||
return false;
|
||||
return astrcmpi(ext, ".bmp") == 0 ||
|
||||
astrcmpi(ext, ".tga") == 0 ||
|
||||
astrcmpi(ext, ".png") == 0 ||
|
||||
astrcmpi(ext, ".jpeg") == 0 ||
|
||||
astrcmpi(ext, ".jpg") == 0 ||
|
||||
astrcmpi(ext, ".gif") == 0;
|
||||
return astrcmpi(ext, ".bmp") == 0 || astrcmpi(ext, ".tga") == 0 ||
|
||||
astrcmpi(ext, ".png") == 0 || astrcmpi(ext, ".jpeg") == 0 ||
|
||||
astrcmpi(ext, ".jpg") == 0 || astrcmpi(ext, ".gif") == 0;
|
||||
}
|
||||
|
||||
static inline bool item_valid(struct slideshow *ss)
|
||||
|
|
@ -241,19 +244,16 @@ static void do_transition(void *data, bool to_null)
|
|||
|
||||
if (valid && ss->use_cut)
|
||||
obs_transition_set(ss->transition,
|
||||
ss->files.array[ss->cur_item].source);
|
||||
ss->files.array[ss->cur_item].source);
|
||||
|
||||
else if (valid && !to_null)
|
||||
obs_transition_start(ss->transition,
|
||||
OBS_TRANSITION_MODE_AUTO,
|
||||
ss->tr_speed,
|
||||
ss->files.array[ss->cur_item].source);
|
||||
obs_transition_start(ss->transition, OBS_TRANSITION_MODE_AUTO,
|
||||
ss->tr_speed,
|
||||
ss->files.array[ss->cur_item].source);
|
||||
|
||||
else
|
||||
obs_transition_start(ss->transition,
|
||||
OBS_TRANSITION_MODE_AUTO,
|
||||
ss->tr_speed,
|
||||
NULL);
|
||||
obs_transition_start(ss->transition, OBS_TRANSITION_MODE_AUTO,
|
||||
ss->tr_speed, NULL);
|
||||
}
|
||||
|
||||
static void ss_update(void *data, obs_data_t *settings)
|
||||
|
|
@ -344,8 +344,8 @@ static void ss_update(void *data, obs_data_t *settings)
|
|||
dstr_copy(&dir_path, path);
|
||||
dstr_cat_ch(&dir_path, '/');
|
||||
dstr_cat(&dir_path, ent->d_name);
|
||||
add_file(ss, &new_files.da, dir_path.array,
|
||||
&cx, &cy);
|
||||
add_file(ss, &new_files.da, dir_path.array, &cx,
|
||||
&cy);
|
||||
|
||||
if (ss->mem_usage >= MAX_MEM_USAGE)
|
||||
break;
|
||||
|
|
@ -447,7 +447,7 @@ static void ss_update(void *data, obs_data_t *settings)
|
|||
obs_transition_set_size(ss->transition, cx, cy);
|
||||
obs_transition_set_alignment(ss->transition, OBS_ALIGN_CENTER);
|
||||
obs_transition_set_scale_type(ss->transition,
|
||||
OBS_TRANSITION_SCALE_ASPECT);
|
||||
OBS_TRANSITION_SCALE_ASPECT);
|
||||
|
||||
if (ss->randomize && ss->files.num)
|
||||
ss->cur_item = random_file(ss);
|
||||
|
|
@ -475,7 +475,7 @@ static void ss_restart(void *data)
|
|||
ss->cur_item = 0;
|
||||
|
||||
obs_transition_set(ss->transition,
|
||||
ss->files.array[ss->cur_item].source);
|
||||
ss->files.array[ss->cur_item].source);
|
||||
|
||||
ss->stop = false;
|
||||
ss->paused = false;
|
||||
|
|
@ -522,7 +522,7 @@ static void ss_previous_slide(void *data)
|
|||
}
|
||||
|
||||
static void play_pause_hotkey(void *data, obs_hotkey_id id,
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
|
|
@ -533,8 +533,8 @@ static void play_pause_hotkey(void *data, obs_hotkey_id id,
|
|||
ss_play_pause(ss);
|
||||
}
|
||||
|
||||
static void restart_hotkey(void *data, obs_hotkey_id id,
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
static void restart_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
|
|
@ -545,8 +545,8 @@ static void restart_hotkey(void *data, obs_hotkey_id id,
|
|||
ss_restart(ss);
|
||||
}
|
||||
|
||||
static void stop_hotkey(void *data, obs_hotkey_id id,
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
static void stop_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
|
|
@ -558,7 +558,7 @@ static void stop_hotkey(void *data, obs_hotkey_id id,
|
|||
}
|
||||
|
||||
static void next_slide_hotkey(void *data, obs_hotkey_id id,
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
|
|
@ -573,7 +573,7 @@ static void next_slide_hotkey(void *data, obs_hotkey_id id,
|
|||
}
|
||||
|
||||
static void previous_slide_hotkey(void *data, obs_hotkey_id id,
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
|
|
@ -607,30 +607,26 @@ static void *ss_create(obs_data_t *settings, obs_source_t *source)
|
|||
ss->paused = false;
|
||||
ss->stop = false;
|
||||
|
||||
ss->play_pause_hotkey = obs_hotkey_register_source(source,
|
||||
"SlideShow.PlayPause",
|
||||
obs_module_text("SlideShow.PlayPause"),
|
||||
play_pause_hotkey, ss);
|
||||
ss->play_pause_hotkey = obs_hotkey_register_source(
|
||||
source, "SlideShow.PlayPause",
|
||||
obs_module_text("SlideShow.PlayPause"), play_pause_hotkey, ss);
|
||||
|
||||
ss->restart_hotkey = obs_hotkey_register_source(source,
|
||||
"SlideShow.Restart",
|
||||
obs_module_text("SlideShow.Restart"),
|
||||
restart_hotkey, ss);
|
||||
ss->restart_hotkey = obs_hotkey_register_source(
|
||||
source, "SlideShow.Restart",
|
||||
obs_module_text("SlideShow.Restart"), restart_hotkey, ss);
|
||||
|
||||
ss->stop_hotkey = obs_hotkey_register_source(source,
|
||||
"SlideShow.Stop",
|
||||
obs_module_text("SlideShow.Stop"),
|
||||
stop_hotkey, ss);
|
||||
ss->stop_hotkey = obs_hotkey_register_source(
|
||||
source, "SlideShow.Stop", obs_module_text("SlideShow.Stop"),
|
||||
stop_hotkey, ss);
|
||||
|
||||
ss->prev_hotkey = obs_hotkey_register_source(source,
|
||||
"SlideShow.NextSlide",
|
||||
obs_module_text("SlideShow.NextSlide"),
|
||||
next_slide_hotkey, ss);
|
||||
ss->prev_hotkey = obs_hotkey_register_source(
|
||||
source, "SlideShow.NextSlide",
|
||||
obs_module_text("SlideShow.NextSlide"), next_slide_hotkey, ss);
|
||||
|
||||
ss->prev_hotkey = obs_hotkey_register_source(source,
|
||||
"SlideShow.PreviousSlide",
|
||||
obs_module_text("SlideShow.PreviousSlide"),
|
||||
previous_slide_hotkey, ss);
|
||||
ss->prev_hotkey = obs_hotkey_register_source(
|
||||
source, "SlideShow.PreviousSlide",
|
||||
obs_module_text("SlideShow.PreviousSlide"),
|
||||
previous_slide_hotkey, ss);
|
||||
|
||||
pthread_mutex_init_value(&ss->mutex);
|
||||
if (pthread_mutex_init(&ss->mutex, NULL) != 0)
|
||||
|
|
@ -682,7 +678,7 @@ static void ss_video_tick(void *data, float seconds)
|
|||
/* ----------------------------------------------------- */
|
||||
/* fade to transparency when the file list becomes empty */
|
||||
if (!ss->files.num) {
|
||||
obs_source_t* active_transition_source =
|
||||
obs_source_t *active_transition_source =
|
||||
obs_transition_get_active_source(ss->transition);
|
||||
|
||||
if (active_transition_source) {
|
||||
|
|
@ -725,8 +721,9 @@ static void ss_video_tick(void *data, float seconds)
|
|||
}
|
||||
|
||||
static inline bool ss_audio_render_(obs_source_t *transition, uint64_t *ts_out,
|
||||
struct obs_source_audio_mix *audio_output,
|
||||
uint32_t mixers, size_t channels, size_t sample_rate)
|
||||
struct obs_source_audio_mix *audio_output,
|
||||
uint32_t mixers, size_t channels,
|
||||
size_t sample_rate)
|
||||
{
|
||||
struct obs_source_audio_mix child_audio;
|
||||
uint64_t source_ts;
|
||||
|
|
@ -747,8 +744,9 @@ static inline bool ss_audio_render_(obs_source_t *transition, uint64_t *ts_out,
|
|||
float *out = audio_output->output[mix].data[ch];
|
||||
float *in = child_audio.output[mix].data[ch];
|
||||
|
||||
memcpy(out, in, AUDIO_OUTPUT_FRAMES *
|
||||
MAX_AUDIO_CHANNELS * sizeof(float));
|
||||
memcpy(out, in,
|
||||
AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS *
|
||||
sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -759,8 +757,9 @@ static inline bool ss_audio_render_(obs_source_t *transition, uint64_t *ts_out,
|
|||
}
|
||||
|
||||
static bool ss_audio_render(void *data, uint64_t *ts_out,
|
||||
struct obs_source_audio_mix *audio_output,
|
||||
uint32_t mixers, size_t channels, size_t sample_rate)
|
||||
struct obs_source_audio_mix *audio_output,
|
||||
uint32_t mixers, size_t channels,
|
||||
size_t sample_rate)
|
||||
{
|
||||
struct slideshow *ss = data;
|
||||
obs_source_t *transition = get_transition(ss);
|
||||
|
|
@ -770,7 +769,7 @@ static bool ss_audio_render(void *data, uint64_t *ts_out,
|
|||
return false;
|
||||
|
||||
success = ss_audio_render_(transition, ts_out, audio_output, mixers,
|
||||
channels, sample_rate);
|
||||
channels, sample_rate);
|
||||
|
||||
obs_source_release(transition);
|
||||
return success;
|
||||
|
|
@ -803,9 +802,10 @@ static void ss_defaults(obs_data_t *settings)
|
|||
obs_data_set_default_string(settings, S_TRANSITION, "fade");
|
||||
obs_data_set_default_int(settings, S_SLIDE_TIME, 8000);
|
||||
obs_data_set_default_int(settings, S_TR_SPEED, 700);
|
||||
obs_data_set_default_string(settings, S_CUSTOM_SIZE, T_CUSTOM_SIZE_AUTO);
|
||||
obs_data_set_default_string(settings, S_CUSTOM_SIZE,
|
||||
T_CUSTOM_SIZE_AUTO);
|
||||
obs_data_set_default_string(settings, S_BEHAVIOR,
|
||||
S_BEHAVIOR_ALWAYS_PLAY);
|
||||
S_BEHAVIOR_ALWAYS_PLAY);
|
||||
obs_data_set_default_string(settings, S_MODE, S_MODE_AUTO);
|
||||
obs_data_set_default_bool(settings, S_LOOP, true);
|
||||
}
|
||||
|
|
@ -813,12 +813,7 @@ static void ss_defaults(obs_data_t *settings)
|
|||
static const char *file_filter =
|
||||
"Image files (*.bmp *.tga *.png *.jpeg *.jpg *.gif)";
|
||||
|
||||
static const char *aspects[] = {
|
||||
"16:9",
|
||||
"16:10",
|
||||
"4:3",
|
||||
"1:1"
|
||||
};
|
||||
static const char *aspects[] = {"16:9", "16:10", "4:3", "1:1"};
|
||||
|
||||
#define NUM_ASPECTS (sizeof(aspects) / sizeof(const char *))
|
||||
|
||||
|
|
@ -841,36 +836,38 @@ static obs_properties_t *ss_properties(void *data)
|
|||
/* ----------------- */
|
||||
|
||||
p = obs_properties_add_list(ppts, S_BEHAVIOR, T_BEHAVIOR,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_list_add_string(p, T_BEHAVIOR_ALWAYS_PLAY,
|
||||
S_BEHAVIOR_ALWAYS_PLAY);
|
||||
S_BEHAVIOR_ALWAYS_PLAY);
|
||||
obs_property_list_add_string(p, T_BEHAVIOR_STOP_RESTART,
|
||||
S_BEHAVIOR_STOP_RESTART);
|
||||
S_BEHAVIOR_STOP_RESTART);
|
||||
obs_property_list_add_string(p, T_BEHAVIOR_PAUSE_UNPAUSE,
|
||||
S_BEHAVIOR_PAUSE_UNPAUSE);
|
||||
S_BEHAVIOR_PAUSE_UNPAUSE);
|
||||
|
||||
p = obs_properties_add_list(ppts, S_MODE, T_MODE,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
p = obs_properties_add_list(ppts, S_MODE, T_MODE, OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_list_add_string(p, T_MODE_AUTO, S_MODE_AUTO);
|
||||
obs_property_list_add_string(p, T_MODE_MANUAL, S_MODE_MANUAL);
|
||||
|
||||
p = obs_properties_add_list(ppts, S_TRANSITION, T_TRANSITION,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_list_add_string(p, T_TR_CUT, TR_CUT);
|
||||
obs_property_list_add_string(p, T_TR_FADE, TR_FADE);
|
||||
obs_property_list_add_string(p, T_TR_SWIPE, TR_SWIPE);
|
||||
obs_property_list_add_string(p, T_TR_SLIDE, TR_SLIDE);
|
||||
|
||||
obs_properties_add_int(ppts, S_SLIDE_TIME, T_SLIDE_TIME,
|
||||
50, 3600000, 50);
|
||||
obs_properties_add_int(ppts, S_TR_SPEED, T_TR_SPEED,
|
||||
0, 3600000, 50);
|
||||
obs_properties_add_int(ppts, S_SLIDE_TIME, T_SLIDE_TIME, 50, 3600000,
|
||||
50);
|
||||
obs_properties_add_int(ppts, S_TR_SPEED, T_TR_SPEED, 0, 3600000, 50);
|
||||
obs_properties_add_bool(ppts, S_LOOP, T_LOOP);
|
||||
obs_properties_add_bool(ppts, S_HIDE, T_HIDE);
|
||||
obs_properties_add_bool(ppts, S_RANDOMIZE, T_RANDOMIZE);
|
||||
|
||||
p = obs_properties_add_list(ppts, S_CUSTOM_SIZE, T_CUSTOM_SIZE,
|
||||
OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING);
|
||||
OBS_COMBO_TYPE_EDITABLE,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
|
||||
obs_property_list_add_string(p, T_CUSTOM_SIZE_AUTO, T_CUSTOM_SIZE_AUTO);
|
||||
|
||||
|
|
@ -897,7 +894,8 @@ static obs_properties_t *ss_properties(void *data)
|
|||
}
|
||||
|
||||
obs_properties_add_editable_list(ppts, S_FILES, T_FILES,
|
||||
OBS_EDITABLE_LIST_TYPE_FILES, file_filter, path.array);
|
||||
OBS_EDITABLE_LIST_TYPE_FILES,
|
||||
file_filter, path.array);
|
||||
dstr_free(&path);
|
||||
|
||||
return ppts;
|
||||
|
|
@ -924,23 +922,22 @@ static void ss_deactivate(void *data)
|
|||
}
|
||||
|
||||
struct obs_source_info slideshow_info = {
|
||||
.id = "slideshow",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO |
|
||||
OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_COMPOSITE,
|
||||
.get_name = ss_getname,
|
||||
.create = ss_create,
|
||||
.destroy = ss_destroy,
|
||||
.update = ss_update,
|
||||
.activate = ss_activate,
|
||||
.deactivate = ss_deactivate,
|
||||
.video_render = ss_video_render,
|
||||
.video_tick = ss_video_tick,
|
||||
.audio_render = ss_audio_render,
|
||||
.id = "slideshow",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_COMPOSITE,
|
||||
.get_name = ss_getname,
|
||||
.create = ss_create,
|
||||
.destroy = ss_destroy,
|
||||
.update = ss_update,
|
||||
.activate = ss_activate,
|
||||
.deactivate = ss_deactivate,
|
||||
.video_render = ss_video_render,
|
||||
.video_tick = ss_video_tick,
|
||||
.audio_render = ss_audio_render,
|
||||
.enum_active_sources = ss_enum_sources,
|
||||
.get_width = ss_width,
|
||||
.get_height = ss_height,
|
||||
.get_defaults = ss_defaults,
|
||||
.get_properties = ss_properties
|
||||
.get_width = ss_width,
|
||||
.get_height = ss_height,
|
||||
.get_defaults = ss_defaults,
|
||||
.get_properties = ss_properties,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
#define blog(level, msg, ...) blog(level, "alsa-input: " msg, ##__VA_ARGS__)
|
||||
|
||||
#define NSEC_PER_SEC 1000000000LL
|
||||
#define NSEC_PER_SEC 1000000000LL
|
||||
#define NSEC_PER_MSEC 1000000L
|
||||
#define STARTUP_TIMEOUT_NS (500 * NSEC_PER_MSEC)
|
||||
#define REOPEN_TIMEOUT 1000UL
|
||||
|
|
@ -63,11 +63,11 @@ struct alsa_data {
|
|||
uint64_t first_ts;
|
||||
};
|
||||
|
||||
static const char * alsa_get_name(void *);
|
||||
static bool alsa_devices_changed(obs_properties_t *props,
|
||||
obs_property_t *p, obs_data_t *settings);
|
||||
static obs_properties_t * alsa_get_properties(void *);
|
||||
static void * alsa_create(obs_data_t *, obs_source_t *);
|
||||
static const char *alsa_get_name(void *);
|
||||
static bool alsa_devices_changed(obs_properties_t *props, obs_property_t *p,
|
||||
obs_data_t *settings);
|
||||
static obs_properties_t *alsa_get_properties(void *);
|
||||
static void *alsa_create(obs_data_t *, obs_source_t *);
|
||||
static void alsa_destroy(void *);
|
||||
static void alsa_activate(void *);
|
||||
static void alsa_deactivate(void *);
|
||||
|
|
@ -75,19 +75,19 @@ static void alsa_get_defaults(obs_data_t *);
|
|||
static void alsa_update(void *, obs_data_t *);
|
||||
|
||||
struct obs_source_info alsa_input_capture = {
|
||||
.id = "alsa_input_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_AUDIO,
|
||||
.create = alsa_create,
|
||||
.destroy = alsa_destroy,
|
||||
.id = "alsa_input_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_AUDIO,
|
||||
.create = alsa_create,
|
||||
.destroy = alsa_destroy,
|
||||
#if SHUTDOWN_ON_DEACTIVATE
|
||||
.activate = alsa_activate,
|
||||
.deactivate = alsa_deactivate,
|
||||
.activate = alsa_activate,
|
||||
.deactivate = alsa_deactivate,
|
||||
#endif
|
||||
.update = alsa_update,
|
||||
.get_defaults = alsa_get_defaults,
|
||||
.get_name = alsa_get_name,
|
||||
.get_properties = alsa_get_properties
|
||||
.update = alsa_update,
|
||||
.get_defaults = alsa_get_defaults,
|
||||
.get_name = alsa_get_name,
|
||||
.get_properties = alsa_get_properties,
|
||||
};
|
||||
|
||||
static bool _alsa_try_open(struct alsa_data *);
|
||||
|
|
@ -96,28 +96,28 @@ static void _alsa_close(struct alsa_data *);
|
|||
static bool _alsa_configure(struct alsa_data *);
|
||||
static void _alsa_start_reopen(struct alsa_data *);
|
||||
static void _alsa_stop_reopen(struct alsa_data *);
|
||||
static void * _alsa_listen(void *);
|
||||
static void * _alsa_reopen(void *);
|
||||
static void *_alsa_listen(void *);
|
||||
static void *_alsa_reopen(void *);
|
||||
|
||||
static enum audio_format _alsa_to_obs_audio_format(snd_pcm_format_t);
|
||||
static enum speaker_layout _alsa_channels_to_obs_speakers(unsigned int);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void * alsa_create(obs_data_t *settings, obs_source_t *source)
|
||||
void *alsa_create(obs_data_t *settings, obs_source_t *source)
|
||||
{
|
||||
struct alsa_data *data = bzalloc(sizeof(struct alsa_data));
|
||||
|
||||
data->source = source;
|
||||
data->source = source;
|
||||
#if SHUTDOWN_ON_DEACTIVATE
|
||||
data->active = false;
|
||||
data->active = false;
|
||||
#endif
|
||||
data->buffer = NULL;
|
||||
data->device = NULL;
|
||||
data->buffer = NULL;
|
||||
data->device = NULL;
|
||||
data->first_ts = 0;
|
||||
data->handle = NULL;
|
||||
data->listen = false;
|
||||
data->reopen = false;
|
||||
data->handle = NULL;
|
||||
data->listen = false;
|
||||
data->reopen = false;
|
||||
data->listen_thread = 0;
|
||||
data->reopen_thread = 0;
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ void alsa_update(void *vptr, obs_data_t *settings)
|
|||
#endif
|
||||
}
|
||||
|
||||
const char * alsa_get_name(void *unused)
|
||||
const char *alsa_get_name(void *unused)
|
||||
{
|
||||
UNUSED_PARAMETER(unused);
|
||||
return obs_module_text("AlsaInput");
|
||||
|
|
@ -230,8 +230,8 @@ void alsa_get_defaults(obs_data_t *settings)
|
|||
obs_data_set_default_int(settings, "rate", 44100);
|
||||
}
|
||||
|
||||
static bool alsa_devices_changed(obs_properties_t *props,
|
||||
obs_property_t *p, obs_data_t *settings)
|
||||
static bool alsa_devices_changed(obs_properties_t *props, obs_property_t *p,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
UNUSED_PARAMETER(p);
|
||||
bool visible = false;
|
||||
|
|
@ -248,14 +248,14 @@ static bool alsa_devices_changed(obs_properties_t *props,
|
|||
return true;
|
||||
}
|
||||
|
||||
obs_properties_t * alsa_get_properties(void *unused)
|
||||
obs_properties_t *alsa_get_properties(void *unused)
|
||||
{
|
||||
void **hints;
|
||||
void **hint;
|
||||
char *name = NULL;
|
||||
char *descr = NULL;
|
||||
char *io = NULL;
|
||||
char *descr_i;
|
||||
char *descr_i;
|
||||
obs_properties_t *props;
|
||||
obs_property_t *devices;
|
||||
obs_property_t *rate;
|
||||
|
|
@ -265,17 +265,18 @@ obs_properties_t * alsa_get_properties(void *unused)
|
|||
props = obs_properties_create();
|
||||
|
||||
devices = obs_properties_add_list(props, "device_id",
|
||||
obs_module_text("Device"), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
obs_module_text("Device"),
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
|
||||
obs_property_list_add_string(devices, "Default", "default");
|
||||
|
||||
obs_properties_add_text(props, "custom_pcm",
|
||||
obs_module_text("PCM"), OBS_TEXT_DEFAULT);
|
||||
obs_properties_add_text(props, "custom_pcm", obs_module_text("PCM"),
|
||||
OBS_TEXT_DEFAULT);
|
||||
|
||||
rate = obs_properties_add_list(props, "rate",
|
||||
obs_module_text("Rate"), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
rate = obs_properties_add_list(props, "rate", obs_module_text("Rate"),
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
|
||||
obs_property_set_modified_callback(devices, alsa_devices_changed);
|
||||
|
||||
|
|
@ -304,10 +305,10 @@ obs_properties_t * alsa_get_properties(void *unused)
|
|||
descr_i = descr;
|
||||
while (*descr_i) {
|
||||
if (*descr_i == '\n') {
|
||||
*descr_i = '\0';
|
||||
break;
|
||||
}
|
||||
else ++descr_i;
|
||||
*descr_i = '\0';
|
||||
break;
|
||||
} else
|
||||
++descr_i;
|
||||
}
|
||||
|
||||
obs_property_list_add_string(devices, descr, name);
|
||||
|
|
@ -350,11 +351,11 @@ bool _alsa_open(struct alsa_data *data)
|
|||
pthread_attr_t attr;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_open(&data->handle, data->device,
|
||||
SND_PCM_STREAM_CAPTURE, 0);
|
||||
err = snd_pcm_open(&data->handle, data->device, SND_PCM_STREAM_CAPTURE,
|
||||
0);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR, "Failed to open '%s': %s",
|
||||
data->device, snd_strerror(err));
|
||||
blog(LOG_ERROR, "Failed to open '%s': %s", data->device,
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -362,8 +363,7 @@ bool _alsa_open(struct alsa_data *data)
|
|||
goto cleanup;
|
||||
|
||||
if (snd_pcm_state(data->handle) != SND_PCM_STATE_PREPARED) {
|
||||
blog(LOG_ERROR, "Device not prepared: '%s'",
|
||||
data->device);
|
||||
blog(LOG_ERROR, "Device not prepared: '%s'", data->device);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
@ -371,8 +371,8 @@ bool _alsa_open(struct alsa_data *data)
|
|||
|
||||
err = snd_pcm_start(data->handle);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR, "Failed to start '%s': %s",
|
||||
data->device, snd_strerror(err));
|
||||
blog(LOG_ERROR, "Failed to start '%s': %s", data->device,
|
||||
snd_strerror(err));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
@ -385,8 +385,8 @@ bool _alsa_open(struct alsa_data *data)
|
|||
if (err) {
|
||||
pthread_attr_destroy(&attr);
|
||||
blog(LOG_ERROR,
|
||||
"Failed to create capture thread for device '%s'.",
|
||||
data->device);
|
||||
"Failed to create capture thread for device '%s'.",
|
||||
data->device);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +412,7 @@ void _alsa_close(struct alsa_data *data)
|
|||
}
|
||||
|
||||
if (data->buffer)
|
||||
bfree(data->buffer), data->buffer = NULL;
|
||||
bfree(data->buffer), data->buffer = NULL;
|
||||
}
|
||||
|
||||
bool _alsa_configure(struct alsa_data *data)
|
||||
|
|
@ -425,37 +425,33 @@ bool _alsa_configure(struct alsa_data *data)
|
|||
|
||||
err = snd_pcm_hw_params_any(data->handle, hwparams);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR,
|
||||
"snd_pcm_hw_params_any failed: %s",
|
||||
snd_strerror(err));
|
||||
blog(LOG_ERROR, "snd_pcm_hw_params_any failed: %s",
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_access(data->handle, hwparams,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR,
|
||||
"snd_pcm_hw_params_set_access failed: %s",
|
||||
snd_strerror(err));
|
||||
blog(LOG_ERROR, "snd_pcm_hw_params_set_access failed: %s",
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
data->format = SND_PCM_FORMAT_S16;
|
||||
err = snd_pcm_hw_params_set_format(data->handle, hwparams,
|
||||
data->format);
|
||||
data->format);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR,
|
||||
"snd_pcm_hw_params_set_format failed: %s",
|
||||
snd_strerror(err));
|
||||
blog(LOG_ERROR, "snd_pcm_hw_params_set_format failed: %s",
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_rate_near(data->handle, hwparams,
|
||||
&data->rate, 0);
|
||||
&data->rate, 0);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR,
|
||||
"snd_pcm_hw_params_set_rate_near failed: %s",
|
||||
snd_strerror(err));
|
||||
blog(LOG_ERROR, "snd_pcm_hw_params_set_rate_near failed: %s",
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
blog(LOG_INFO, "PCM '%s' rate set to %d", data->device, data->rate);
|
||||
|
|
@ -465,34 +461,34 @@ bool _alsa_configure(struct alsa_data *data)
|
|||
data->channels = 2;
|
||||
|
||||
err = snd_pcm_hw_params_set_channels_near(data->handle, hwparams,
|
||||
&data->channels);
|
||||
&data->channels);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR,
|
||||
"snd_pcm_hw_params_set_channels_near failed: %s",
|
||||
snd_strerror(err));
|
||||
"snd_pcm_hw_params_set_channels_near failed: %s",
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
blog(LOG_INFO, "PCM '%s' channels set to %d",
|
||||
data->device, data->channels);
|
||||
blog(LOG_INFO, "PCM '%s' channels set to %d", data->device,
|
||||
data->channels);
|
||||
|
||||
err = snd_pcm_hw_params(data->handle, hwparams);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR, "snd_pcm_hw_params failed: %s",
|
||||
snd_strerror(err));
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_period_size(hwparams, &data->period_size,
|
||||
&dir);
|
||||
&dir);
|
||||
if (err < 0) {
|
||||
blog(LOG_ERROR,
|
||||
"snd_pcm_hw_params_get_period_size failed: %s",
|
||||
snd_strerror(err));
|
||||
blog(LOG_ERROR, "snd_pcm_hw_params_get_period_size failed: %s",
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
data->sample_size = (data->channels
|
||||
* snd_pcm_format_physical_width(data->format)) / 8;
|
||||
data->sample_size =
|
||||
(data->channels * snd_pcm_format_physical_width(data->format)) /
|
||||
8;
|
||||
|
||||
if (data->buffer)
|
||||
bfree(data->buffer);
|
||||
|
|
@ -515,8 +511,8 @@ void _alsa_start_reopen(struct alsa_data *data)
|
|||
err = pthread_create(&data->reopen_thread, &attr, _alsa_reopen, data);
|
||||
if (err) {
|
||||
blog(LOG_ERROR,
|
||||
"Failed to create reopen thread for device '%s'.",
|
||||
data->device);
|
||||
"Failed to create reopen thread for device '%s'.",
|
||||
data->device);
|
||||
}
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
|
|
@ -535,23 +531,23 @@ void _alsa_stop_reopen(struct alsa_data *data)
|
|||
os_event_reset(data->abort_event);
|
||||
}
|
||||
|
||||
void * _alsa_listen(void *attr)
|
||||
void *_alsa_listen(void *attr)
|
||||
{
|
||||
struct alsa_data *data = attr;
|
||||
struct obs_source_audio out;
|
||||
|
||||
blog(LOG_DEBUG, "Capture thread started.");
|
||||
|
||||
out.data[0] = data->buffer;
|
||||
out.format = _alsa_to_obs_audio_format(data->format);
|
||||
out.data[0] = data->buffer;
|
||||
out.format = _alsa_to_obs_audio_format(data->format);
|
||||
out.speakers = _alsa_channels_to_obs_speakers(data->channels);
|
||||
out.samples_per_sec = data->rate;
|
||||
|
||||
os_atomic_set_bool(&data->listen, true);
|
||||
|
||||
do {
|
||||
snd_pcm_sframes_t frames = snd_pcm_readi(data->handle,
|
||||
data->buffer, data->period_size);
|
||||
snd_pcm_sframes_t frames = snd_pcm_readi(
|
||||
data->handle, data->buffer, data->period_size);
|
||||
|
||||
if (!os_atomic_load_bool(&data->listen))
|
||||
break;
|
||||
|
|
@ -565,8 +561,8 @@ void * _alsa_listen(void *attr)
|
|||
}
|
||||
|
||||
out.frames = frames;
|
||||
out.timestamp = os_gettime_ns()
|
||||
- ((frames * NSEC_PER_SEC) / data->rate);
|
||||
out.timestamp = os_gettime_ns() -
|
||||
((frames * NSEC_PER_SEC) / data->rate);
|
||||
|
||||
if (!data->first_ts)
|
||||
data->first_ts = out.timestamp + STARTUP_TIMEOUT_NS;
|
||||
|
|
@ -581,7 +577,7 @@ void * _alsa_listen(void *attr)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void * _alsa_reopen(void *attr)
|
||||
void *_alsa_reopen(void *attr)
|
||||
{
|
||||
struct alsa_data *data = attr;
|
||||
unsigned long timeout = REOPEN_TIMEOUT;
|
||||
|
|
@ -609,11 +605,16 @@ void * _alsa_reopen(void *attr)
|
|||
enum audio_format _alsa_to_obs_audio_format(snd_pcm_format_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case SND_PCM_FORMAT_U8: return AUDIO_FORMAT_U8BIT;
|
||||
case SND_PCM_FORMAT_S16_LE: return AUDIO_FORMAT_16BIT;
|
||||
case SND_PCM_FORMAT_S32_LE: return AUDIO_FORMAT_32BIT;
|
||||
case SND_PCM_FORMAT_FLOAT_LE: return AUDIO_FORMAT_FLOAT;
|
||||
default: break;
|
||||
case SND_PCM_FORMAT_U8:
|
||||
return AUDIO_FORMAT_U8BIT;
|
||||
case SND_PCM_FORMAT_S16_LE:
|
||||
return AUDIO_FORMAT_16BIT;
|
||||
case SND_PCM_FORMAT_S32_LE:
|
||||
return AUDIO_FORMAT_32BIT;
|
||||
case SND_PCM_FORMAT_FLOAT_LE:
|
||||
return AUDIO_FORMAT_FLOAT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return AUDIO_FORMAT_UNKNOWN;
|
||||
|
|
@ -621,16 +622,22 @@ enum audio_format _alsa_to_obs_audio_format(snd_pcm_format_t format)
|
|||
|
||||
enum speaker_layout _alsa_channels_to_obs_speakers(unsigned int channels)
|
||||
{
|
||||
switch(channels) {
|
||||
case 1: return SPEAKERS_MONO;
|
||||
case 2: return SPEAKERS_STEREO;
|
||||
case 3: return SPEAKERS_2POINT1;
|
||||
case 4: return SPEAKERS_4POINT0;
|
||||
case 5: return SPEAKERS_4POINT1;
|
||||
case 6: return SPEAKERS_5POINT1;
|
||||
case 8: return SPEAKERS_7POINT1;
|
||||
switch (channels) {
|
||||
case 1:
|
||||
return SPEAKERS_MONO;
|
||||
case 2:
|
||||
return SPEAKERS_STEREO;
|
||||
case 3:
|
||||
return SPEAKERS_2POINT1;
|
||||
case 4:
|
||||
return SPEAKERS_4POINT0;
|
||||
case 5:
|
||||
return SPEAKERS_4POINT1;
|
||||
case 6:
|
||||
return SPEAKERS_5POINT1;
|
||||
case 8:
|
||||
return SPEAKERS_7POINT1;
|
||||
}
|
||||
|
||||
return SPEAKERS_UNKNOWN;
|
||||
}
|
||||
|
||||
|
|
|
|||
3
plugins/linux-alsa/data/locale/bg-BG.ini
Normal file
3
plugins/linux-alsa/data/locale/bg-BG.ini
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
AlsaInput="Устройство за улавяне на звука (ALSA)"
|
||||
Device="Устройство"
|
||||
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
AlsaInput="Dispositivo de captura de audio (ALSA)"
|
||||
AlsaInput="Dispositivo de captura de son (ALSA)"
|
||||
Device="Dispositivo"
|
||||
|
||||
|
|
|
|||
2
plugins/linux-alsa/data/locale/sl-SI.ini
Normal file
2
plugins/linux-alsa/data/locale/sl-SI.ini
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
AlsaInput="Naprava za zajemanje zvoka (ALSA)"
|
||||
|
||||
|
|
@ -30,4 +30,3 @@ bool obs_module_load(void)
|
|||
obs_register_source(&alsa_input_capture);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ X11SharedMemoryScreenInput="Bildschirmaufnahme (XSHM)"
|
|||
Screen="Bildschirm"
|
||||
CaptureCursor="Mauszeiger aufnehmen"
|
||||
AdvancedSettings="Erweiterte Einstellungen"
|
||||
XServer="X-Server"
|
||||
XServer="X‐Server"
|
||||
XCCapture="Fensteraufnahme (Xcomposite)"
|
||||
Window="Fenster"
|
||||
CropTop="Oben abschneiden (Pixel)"
|
||||
|
|
@ -10,7 +10,7 @@ CropLeft="Links abschneiden (Pixel)"
|
|||
CropRight="Rechts abschneiden (Pixel)"
|
||||
CropBottom="Unten abschneiden (Pixel)"
|
||||
SwapRedBlue="Rot und Blau tauschen"
|
||||
LockX="X-Server während der Aufnahme sperren"
|
||||
IncludeXBorder="X-Rahmen anzeigen"
|
||||
ExcludeAlpha="Alphaloses Texturformat verwenden (Mesa-Problemumgehung)"
|
||||
LockX="X‐Server während der Aufnahme sperren"
|
||||
IncludeXBorder="X‐Rahmen anzeigen"
|
||||
ExcludeAlpha="Alphaloses Texturformat verwenden (Mesa‐Problemumgehung)"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
X11SharedMemoryScreenInput="Captura de pantalla (XSHM)"
|
||||
Screen="Pantalla"
|
||||
CaptureCursor="Captura de cursor"
|
||||
CaptureCursor="Capturar o cursor"
|
||||
AdvancedSettings="Axustes avanzados"
|
||||
XServer="X Server"
|
||||
XServer="Servidor das X"
|
||||
XCCapture="Captura de xanela (Xcomposite)"
|
||||
Window="Xanela"
|
||||
CropTop="Recortar por arriba (píxeles)"
|
||||
CropLeft="Recortar pola esquerda (píxeles)"
|
||||
CropRight="Recortar pola dereita (píxeles)"
|
||||
CropBottom="Recortar por abaixo (píxeles)"
|
||||
SwapRedBlue="Trocar vermello e azul"
|
||||
LockX="Bloquear o servidor X durante a captura"
|
||||
CropTop="Recortar por riba (píxeis)"
|
||||
CropLeft="Recortar pola esquerda (píxeis)"
|
||||
CropRight="Recortar pola dereita (píxeis)"
|
||||
CropBottom="Recortar por baixo (píxeis)"
|
||||
SwapRedBlue="Intercambiar vermello e azul"
|
||||
LockX="Bloquear o servidor das X durante a captura"
|
||||
IncludeXBorder="Incluír o bordo da xanela X"
|
||||
ExcludeAlpha="Empregar o formato de textura sen alfa (solución Mesa)"
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ CropLeft="Trunchiază stânga (pixeli)"
|
|||
CropRight="Trunchiază dreapta (pixeli)"
|
||||
CropBottom="Trunchiază partea inferioară (pixeli)"
|
||||
SwapRedBlue="Schimbă roșu cu albastru"
|
||||
LockX="Blochează serverul X atunci când se capturează"
|
||||
LockX="Blochează X server când se capturează"
|
||||
IncludeXBorder="Include marginea cu X"
|
||||
ExcludeAlpha="Folosește formatul de texturi fără alpha (soluție de evitare pentru Mesa)"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
X11SharedMemoryScreenInput="Zajemanje zaslona (XSHM)"
|
||||
Screen="Zaslon"
|
||||
CaptureCursor="Zajemaj kazalec"
|
||||
CaptureCursor="Zajemi kazalec"
|
||||
AdvancedSettings="Napredne nastavitve"
|
||||
XServer="X Server"
|
||||
XCCapture="Zajemanje okna (Xcomposite)"
|
||||
|
|
|
|||
|
|
@ -11,291 +11,259 @@
|
|||
|
||||
#include "xcompcap-helper.hpp"
|
||||
|
||||
namespace XCompcap
|
||||
namespace XCompcap {
|
||||
static Display *xdisplay = 0;
|
||||
|
||||
Display *disp()
|
||||
{
|
||||
static Display* xdisplay = 0;
|
||||
if (!xdisplay)
|
||||
xdisplay = XOpenDisplay(NULL);
|
||||
|
||||
Display *disp()
|
||||
{
|
||||
if (!xdisplay)
|
||||
xdisplay = XOpenDisplay(NULL);
|
||||
return xdisplay;
|
||||
}
|
||||
|
||||
return xdisplay;
|
||||
}
|
||||
void cleanupDisplay()
|
||||
{
|
||||
if (!xdisplay)
|
||||
return;
|
||||
|
||||
void cleanupDisplay()
|
||||
{
|
||||
if (!xdisplay)
|
||||
return;
|
||||
XCloseDisplay(xdisplay);
|
||||
xdisplay = 0;
|
||||
}
|
||||
|
||||
XCloseDisplay(xdisplay);
|
||||
xdisplay = 0;
|
||||
}
|
||||
static void getAllWindows(Window parent, std::list<Window> &windows)
|
||||
{
|
||||
UNUSED_PARAMETER(parent);
|
||||
UNUSED_PARAMETER(windows);
|
||||
}
|
||||
|
||||
static void getAllWindows(Window parent, std::list<Window>& windows)
|
||||
{
|
||||
UNUSED_PARAMETER(parent);
|
||||
UNUSED_PARAMETER(windows);
|
||||
}
|
||||
std::list<Window> getAllWindows()
|
||||
{
|
||||
std::list<Window> res;
|
||||
|
||||
std::list<Window> getAllWindows()
|
||||
{
|
||||
std::list<Window> res;
|
||||
for (int i = 0; i < ScreenCount(disp()); ++i)
|
||||
getAllWindows(RootWindow(disp(), i), res);
|
||||
|
||||
for (int i = 0; i < ScreenCount(disp()); ++i)
|
||||
getAllWindows(RootWindow(disp(), i), res);
|
||||
return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
// Specification for checking for ewmh support at
|
||||
// http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472693600
|
||||
|
||||
// Specification for checking for ewmh support at
|
||||
// http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472693600
|
||||
bool ewmhIsSupported()
|
||||
{
|
||||
Display *display = disp();
|
||||
Atom netSupportingWmCheck =
|
||||
XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", true);
|
||||
Atom actualType;
|
||||
int format = 0;
|
||||
unsigned long num = 0, bytes = 0;
|
||||
unsigned char *data = NULL;
|
||||
Window ewmh_window = 0;
|
||||
|
||||
bool ewmhIsSupported()
|
||||
{
|
||||
Display *display = disp();
|
||||
Atom netSupportingWmCheck = XInternAtom(display,
|
||||
"_NET_SUPPORTING_WM_CHECK", true);
|
||||
Atom actualType;
|
||||
int format = 0;
|
||||
unsigned long num = 0, bytes = 0;
|
||||
unsigned char *data = NULL;
|
||||
Window ewmh_window = 0;
|
||||
int status = XGetWindowProperty(display, DefaultRootWindow(display),
|
||||
netSupportingWmCheck, 0L, 1L, false,
|
||||
XA_WINDOW, &actualType, &format, &num,
|
||||
&bytes, &data);
|
||||
|
||||
int status = XGetWindowProperty(
|
||||
display,
|
||||
DefaultRootWindow(display),
|
||||
netSupportingWmCheck,
|
||||
0L,
|
||||
1L,
|
||||
false,
|
||||
XA_WINDOW,
|
||||
&actualType,
|
||||
&format,
|
||||
&num,
|
||||
&bytes,
|
||||
&data);
|
||||
|
||||
if (status == Success) {
|
||||
if (num > 0) {
|
||||
ewmh_window = ((Window*)data)[0];
|
||||
}
|
||||
if (data) {
|
||||
XFree(data);
|
||||
data = NULL;
|
||||
}
|
||||
if (status == Success) {
|
||||
if (num > 0) {
|
||||
ewmh_window = ((Window *)data)[0];
|
||||
}
|
||||
|
||||
if (ewmh_window) {
|
||||
status = XGetWindowProperty(
|
||||
display,
|
||||
ewmh_window,
|
||||
netSupportingWmCheck,
|
||||
0L,
|
||||
1L,
|
||||
false,
|
||||
XA_WINDOW,
|
||||
&actualType,
|
||||
&format,
|
||||
&num,
|
||||
&bytes,
|
||||
&data);
|
||||
if (status != Success || num == 0 ||
|
||||
ewmh_window != ((Window*)data)[0]) {
|
||||
ewmh_window = 0;
|
||||
}
|
||||
if (status == Success && data) {
|
||||
XFree(data);
|
||||
}
|
||||
if (data) {
|
||||
XFree(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
return ewmh_window != 0;
|
||||
}
|
||||
|
||||
std::list<Window> getTopLevelWindows()
|
||||
{
|
||||
std::list<Window> res;
|
||||
|
||||
if (!ewmhIsSupported()) {
|
||||
blog(LOG_WARNING, "Unable to query window list "
|
||||
"because window manager "
|
||||
"does not support extended "
|
||||
"window manager Hints");
|
||||
return res;
|
||||
if (ewmh_window) {
|
||||
status = XGetWindowProperty(display, ewmh_window,
|
||||
netSupportingWmCheck, 0L, 1L, false,
|
||||
XA_WINDOW, &actualType, &format,
|
||||
&num, &bytes, &data);
|
||||
if (status != Success || num == 0 ||
|
||||
ewmh_window != ((Window *)data)[0]) {
|
||||
ewmh_window = 0;
|
||||
}
|
||||
|
||||
Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true);
|
||||
Atom actualType;
|
||||
int format;
|
||||
unsigned long num, bytes;
|
||||
Window* data = 0;
|
||||
|
||||
for (int i = 0; i < ScreenCount(disp()); ++i) {
|
||||
Window rootWin = RootWindow(disp(), i);
|
||||
|
||||
int status = XGetWindowProperty(
|
||||
disp(),
|
||||
rootWin,
|
||||
netClList,
|
||||
0L,
|
||||
~0L,
|
||||
false,
|
||||
AnyPropertyType,
|
||||
&actualType,
|
||||
&format,
|
||||
&num,
|
||||
&bytes,
|
||||
(uint8_t**)&data);
|
||||
|
||||
if (status != Success) {
|
||||
blog(LOG_WARNING, "Failed getting root "
|
||||
"window properties");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned long i = 0; i < num; ++i)
|
||||
res.push_back(data[i]);
|
||||
|
||||
if (status == Success && data) {
|
||||
XFree(data);
|
||||
}
|
||||
}
|
||||
|
||||
return ewmh_window != 0;
|
||||
}
|
||||
|
||||
std::list<Window> getTopLevelWindows()
|
||||
{
|
||||
std::list<Window> res;
|
||||
|
||||
if (!ewmhIsSupported()) {
|
||||
blog(LOG_WARNING, "Unable to query window list "
|
||||
"because window manager "
|
||||
"does not support extended "
|
||||
"window manager Hints");
|
||||
return res;
|
||||
}
|
||||
|
||||
int getRootWindowScreen(Window root)
|
||||
{
|
||||
XWindowAttributes attr;
|
||||
Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true);
|
||||
Atom actualType;
|
||||
int format;
|
||||
unsigned long num, bytes;
|
||||
Window *data = 0;
|
||||
|
||||
if (!XGetWindowAttributes(disp(), root, &attr))
|
||||
return DefaultScreen(disp());
|
||||
for (int i = 0; i < ScreenCount(disp()); ++i) {
|
||||
Window rootWin = RootWindow(disp(), i);
|
||||
|
||||
return XScreenNumberOfScreen(attr.screen);
|
||||
}
|
||||
int status = XGetWindowProperty(disp(), rootWin, netClList, 0L,
|
||||
~0L, false, AnyPropertyType,
|
||||
&actualType, &format, &num,
|
||||
&bytes, (uint8_t **)&data);
|
||||
|
||||
std::string getWindowAtom(Window win, const char *atom)
|
||||
{
|
||||
Atom netWmName = XInternAtom(disp(), atom, false);
|
||||
int n;
|
||||
char **list = 0;
|
||||
XTextProperty tp;
|
||||
std::string res = "unknown";
|
||||
|
||||
XGetTextProperty(disp(), win, &tp, netWmName);
|
||||
|
||||
if (!tp.nitems)
|
||||
XGetWMName(disp(), win, &tp);
|
||||
|
||||
if (!tp.nitems)
|
||||
return "error";
|
||||
|
||||
if (tp.encoding == XA_STRING) {
|
||||
res = (char*)tp.value;
|
||||
} else {
|
||||
int ret = XmbTextPropertyToTextList(disp(), &tp, &list,
|
||||
&n);
|
||||
|
||||
if (ret >= Success && n > 0 && *list) {
|
||||
res = *list;
|
||||
XFreeStringList(list);
|
||||
}
|
||||
if (status != Success) {
|
||||
blog(LOG_WARNING, "Failed getting root "
|
||||
"window properties");
|
||||
continue;
|
||||
}
|
||||
|
||||
char *conv = nullptr;
|
||||
if (os_mbs_to_utf8_ptr(res.c_str(), 0, &conv))
|
||||
res = conv;
|
||||
bfree(conv);
|
||||
for (unsigned long i = 0; i < num; ++i)
|
||||
res.push_back(data[i]);
|
||||
|
||||
XFree(tp.value);
|
||||
|
||||
return res;
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
std::string getWindowCommand(Window win)
|
||||
{
|
||||
Atom xi = XInternAtom(disp(), "WM_COMMAND", false);
|
||||
int n;
|
||||
char **list = 0;
|
||||
XTextProperty tp;
|
||||
std::string res = "error";
|
||||
return res;
|
||||
}
|
||||
|
||||
XGetTextProperty(disp(), win, &tp, xi);
|
||||
int getRootWindowScreen(Window root)
|
||||
{
|
||||
XWindowAttributes attr;
|
||||
|
||||
if (!tp.nitems)
|
||||
return std::string();
|
||||
if (!XGetWindowAttributes(disp(), root, &attr))
|
||||
return DefaultScreen(disp());
|
||||
|
||||
if (tp.encoding == XA_STRING) {
|
||||
res = (char*)tp.value;
|
||||
} else {
|
||||
int ret = XmbTextPropertyToTextList(disp(), &tp, &list,
|
||||
&n);
|
||||
if (ret >= Success && n > 0 && *list) {
|
||||
res = *list;
|
||||
XFreeStringList(list);
|
||||
}
|
||||
return XScreenNumberOfScreen(attr.screen);
|
||||
}
|
||||
|
||||
std::string getWindowAtom(Window win, const char *atom)
|
||||
{
|
||||
Atom netWmName = XInternAtom(disp(), atom, false);
|
||||
int n;
|
||||
char **list = 0;
|
||||
XTextProperty tp;
|
||||
std::string res = "unknown";
|
||||
|
||||
XGetTextProperty(disp(), win, &tp, netWmName);
|
||||
|
||||
if (!tp.nitems)
|
||||
XGetWMName(disp(), win, &tp);
|
||||
|
||||
if (!tp.nitems)
|
||||
return "error";
|
||||
|
||||
if (tp.encoding == XA_STRING) {
|
||||
res = (char *)tp.value;
|
||||
} else {
|
||||
int ret = XmbTextPropertyToTextList(disp(), &tp, &list, &n);
|
||||
|
||||
if (ret >= Success && n > 0 && *list) {
|
||||
res = *list;
|
||||
XFreeStringList(list);
|
||||
}
|
||||
|
||||
XFree(tp.value);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int getWindowPid(Window win)
|
||||
{
|
||||
UNUSED_PARAMETER(win);
|
||||
return 1234; //TODO
|
||||
}
|
||||
char *conv = nullptr;
|
||||
if (os_mbs_to_utf8_ptr(res.c_str(), 0, &conv))
|
||||
res = conv;
|
||||
bfree(conv);
|
||||
|
||||
static std::unordered_set<Window> changedWindows;
|
||||
static pthread_mutex_t changeLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
void processEvents()
|
||||
{
|
||||
PLock lock(&changeLock);
|
||||
XFree(tp.value);
|
||||
|
||||
XLockDisplay(disp());
|
||||
return res;
|
||||
}
|
||||
|
||||
while (XEventsQueued(disp(), QueuedAfterReading) > 0) {
|
||||
XEvent ev;
|
||||
std::string getWindowCommand(Window win)
|
||||
{
|
||||
Atom xi = XInternAtom(disp(), "WM_COMMAND", false);
|
||||
int n;
|
||||
char **list = 0;
|
||||
XTextProperty tp;
|
||||
std::string res = "error";
|
||||
|
||||
XNextEvent(disp(), &ev);
|
||||
XGetTextProperty(disp(), win, &tp, xi);
|
||||
|
||||
if (ev.type == ConfigureNotify)
|
||||
changedWindows.insert(ev.xconfigure.event);
|
||||
if (!tp.nitems)
|
||||
return std::string();
|
||||
|
||||
if (ev.type == MapNotify)
|
||||
changedWindows.insert(ev.xmap.event);
|
||||
|
||||
if (ev.type == Expose)
|
||||
changedWindows.insert(ev.xexpose.window);
|
||||
|
||||
if (ev.type == VisibilityNotify)
|
||||
changedWindows.insert(ev.xvisibility.window);
|
||||
|
||||
if (ev.type == DestroyNotify)
|
||||
changedWindows.insert(ev.xdestroywindow.event);
|
||||
if (tp.encoding == XA_STRING) {
|
||||
res = (char *)tp.value;
|
||||
} else {
|
||||
int ret = XmbTextPropertyToTextList(disp(), &tp, &list, &n);
|
||||
if (ret >= Success && n > 0 && *list) {
|
||||
res = *list;
|
||||
XFreeStringList(list);
|
||||
}
|
||||
|
||||
XUnlockDisplay(disp());
|
||||
}
|
||||
|
||||
bool windowWasReconfigured(Window win)
|
||||
{
|
||||
PLock lock(&changeLock);
|
||||
XFree(tp.value);
|
||||
|
||||
auto it = changedWindows.find(win);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (it != changedWindows.end()) {
|
||||
changedWindows.erase(it);
|
||||
return true;
|
||||
}
|
||||
int getWindowPid(Window win)
|
||||
{
|
||||
UNUSED_PARAMETER(win);
|
||||
return 1234; //TODO
|
||||
}
|
||||
|
||||
return false;
|
||||
static std::unordered_set<Window> changedWindows;
|
||||
static pthread_mutex_t changeLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
void processEvents()
|
||||
{
|
||||
PLock lock(&changeLock);
|
||||
|
||||
XLockDisplay(disp());
|
||||
|
||||
while (XEventsQueued(disp(), QueuedAfterReading) > 0) {
|
||||
XEvent ev;
|
||||
|
||||
XNextEvent(disp(), &ev);
|
||||
|
||||
if (ev.type == ConfigureNotify)
|
||||
changedWindows.insert(ev.xconfigure.event);
|
||||
|
||||
if (ev.type == MapNotify)
|
||||
changedWindows.insert(ev.xmap.event);
|
||||
|
||||
if (ev.type == Expose)
|
||||
changedWindows.insert(ev.xexpose.window);
|
||||
|
||||
if (ev.type == VisibilityNotify)
|
||||
changedWindows.insert(ev.xvisibility.window);
|
||||
|
||||
if (ev.type == DestroyNotify)
|
||||
changedWindows.insert(ev.xdestroywindow.event);
|
||||
}
|
||||
|
||||
XUnlockDisplay(disp());
|
||||
}
|
||||
|
||||
bool windowWasReconfigured(Window win)
|
||||
{
|
||||
PLock lock(&changeLock);
|
||||
|
||||
auto it = changedWindows.find(win);
|
||||
|
||||
if (it != changedWindows.end()) {
|
||||
changedWindows.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
PLock::PLock(pthread_mutex_t* mtx, bool trylock)
|
||||
:m(mtx)
|
||||
PLock::PLock(pthread_mutex_t *mtx, bool trylock) : m(mtx)
|
||||
{
|
||||
if (trylock)
|
||||
islock = mtx && pthread_mutex_trylock(mtx) == 0;
|
||||
|
|
@ -331,11 +299,9 @@ void PLock::lock()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool* curErrorTarget = 0;
|
||||
static bool *curErrorTarget = 0;
|
||||
static char curErrorText[200];
|
||||
static int xerrorlock_handler(Display* disp, XErrorEvent* err)
|
||||
static int xerrorlock_handler(Display *disp, XErrorEvent *err)
|
||||
{
|
||||
|
||||
if (curErrorTarget)
|
||||
|
|
@ -451,7 +417,6 @@ void XDisplayLock::unlock()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
ObsGsContextHolder::ObsGsContextHolder()
|
||||
{
|
||||
obs_enter_graphics();
|
||||
|
|
|
|||
|
|
@ -5,17 +5,15 @@
|
|||
|
||||
#define blog(level, msg, ...) blog(level, "xcompcap: " msg, ##__VA_ARGS__)
|
||||
|
||||
|
||||
class PLock
|
||||
{
|
||||
class PLock {
|
||||
pthread_mutex_t *m;
|
||||
bool islock;
|
||||
|
||||
public:
|
||||
PLock(const PLock&) = delete;
|
||||
PLock& operator=(const PLock&) = delete;
|
||||
public:
|
||||
PLock(const PLock &) = delete;
|
||||
PLock &operator=(const PLock &) = delete;
|
||||
|
||||
PLock(pthread_mutex_t* mtx, bool trylock = false);
|
||||
PLock(pthread_mutex_t *mtx, bool trylock = false);
|
||||
|
||||
~PLock();
|
||||
|
||||
|
|
@ -25,15 +23,14 @@ class PLock
|
|||
void lock();
|
||||
};
|
||||
|
||||
class XErrorLock
|
||||
{
|
||||
class XErrorLock {
|
||||
bool islock;
|
||||
bool goterr;
|
||||
XErrorHandler prevhandler;
|
||||
|
||||
public:
|
||||
XErrorLock(const XErrorLock&) = delete;
|
||||
XErrorLock& operator=(const XErrorLock&) = delete;
|
||||
public:
|
||||
XErrorLock(const XErrorLock &) = delete;
|
||||
XErrorLock &operator=(const XErrorLock &) = delete;
|
||||
|
||||
XErrorLock();
|
||||
~XErrorLock();
|
||||
|
|
@ -48,13 +45,12 @@ class XErrorLock
|
|||
void resetError();
|
||||
};
|
||||
|
||||
class XDisplayLock
|
||||
{
|
||||
class XDisplayLock {
|
||||
bool islock;
|
||||
|
||||
public:
|
||||
XDisplayLock(const XDisplayLock&) = delete;
|
||||
XDisplayLock& operator=(const XDisplayLock&) = delete;
|
||||
public:
|
||||
XDisplayLock(const XDisplayLock &) = delete;
|
||||
XDisplayLock &operator=(const XDisplayLock &) = delete;
|
||||
|
||||
XDisplayLock();
|
||||
~XDisplayLock();
|
||||
|
|
@ -65,39 +61,37 @@ class XDisplayLock
|
|||
void lock();
|
||||
};
|
||||
|
||||
class ObsGsContextHolder
|
||||
{
|
||||
public:
|
||||
ObsGsContextHolder(const ObsGsContextHolder&) = delete;
|
||||
ObsGsContextHolder& operator=(const ObsGsContextHolder&) = delete;
|
||||
class ObsGsContextHolder {
|
||||
public:
|
||||
ObsGsContextHolder(const ObsGsContextHolder &) = delete;
|
||||
ObsGsContextHolder &operator=(const ObsGsContextHolder &) = delete;
|
||||
|
||||
ObsGsContextHolder();
|
||||
~ObsGsContextHolder();
|
||||
};
|
||||
|
||||
namespace XCompcap
|
||||
namespace XCompcap {
|
||||
Display *disp();
|
||||
void cleanupDisplay();
|
||||
|
||||
std::string getWindowCommand(Window win);
|
||||
int getRootWindowScreen(Window root);
|
||||
std::string getWindowAtom(Window win, const char *atom);
|
||||
int getWindowPid(Window win);
|
||||
bool ewmhIsSupported();
|
||||
std::list<Window> getTopLevelWindows();
|
||||
std::list<Window> getAllWindows();
|
||||
|
||||
inline std::string getWindowName(Window win)
|
||||
{
|
||||
Display* disp();
|
||||
void cleanupDisplay();
|
||||
|
||||
std::string getWindowCommand(Window win);
|
||||
int getRootWindowScreen(Window root);
|
||||
std::string getWindowAtom(Window win, const char *atom);
|
||||
int getWindowPid(Window win);
|
||||
bool ewmhIsSupported();
|
||||
std::list<Window> getTopLevelWindows();
|
||||
std::list<Window> getAllWindows();
|
||||
|
||||
inline std::string getWindowName(Window win)
|
||||
{
|
||||
return getWindowAtom(win, "_NET_WM_NAME");
|
||||
}
|
||||
|
||||
inline std::string getWindowClass(Window win)
|
||||
{
|
||||
return getWindowAtom(win, "WM_CLASS");
|
||||
}
|
||||
|
||||
void processEvents();
|
||||
bool windowWasReconfigured(Window win);
|
||||
return getWindowAtom(win, "_NET_WM_NAME");
|
||||
}
|
||||
|
||||
inline std::string getWindowClass(Window win)
|
||||
{
|
||||
return getWindowAtom(win, "WM_CLASS");
|
||||
}
|
||||
|
||||
void processEvents();
|
||||
bool windowWasReconfigured(Window win);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ bool XCompcapMain::init()
|
|||
|
||||
if (major == 0 && minor < 2) {
|
||||
blog(LOG_ERROR, "Xcomposite extension is too old: %d.%d < 0.2",
|
||||
major, minor);
|
||||
major, minor);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -51,43 +51,41 @@ obs_properties_t *XCompcapMain::properties()
|
|||
{
|
||||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_property_t *wins = obs_properties_add_list(props, "capture_window",
|
||||
obs_module_text("Window"),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_t *wins = obs_properties_add_list(
|
||||
props, "capture_window", obs_module_text("Window"),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
|
||||
for (Window win: XCompcap::getTopLevelWindows()) {
|
||||
for (Window win : XCompcap::getTopLevelWindows()) {
|
||||
std::string wname = XCompcap::getWindowName(win);
|
||||
std::string cls = XCompcap::getWindowClass(win);
|
||||
std::string winid = std::to_string((long long)win);
|
||||
std::string desc =
|
||||
(winid + WIN_STRING_DIV + wname +
|
||||
WIN_STRING_DIV + cls);
|
||||
(winid + WIN_STRING_DIV + wname + WIN_STRING_DIV + cls);
|
||||
|
||||
obs_property_list_add_string(wins, wname.c_str(),
|
||||
desc.c_str());
|
||||
obs_property_list_add_string(wins, wname.c_str(), desc.c_str());
|
||||
}
|
||||
|
||||
obs_properties_add_int(props, "cut_top", obs_module_text("CropTop"),
|
||||
0, 4096, 1);
|
||||
obs_properties_add_int(props, "cut_top", obs_module_text("CropTop"), 0,
|
||||
4096, 1);
|
||||
obs_properties_add_int(props, "cut_left", obs_module_text("CropLeft"),
|
||||
0, 4096, 1);
|
||||
0, 4096, 1);
|
||||
obs_properties_add_int(props, "cut_right", obs_module_text("CropRight"),
|
||||
0, 4096, 1);
|
||||
0, 4096, 1);
|
||||
obs_properties_add_int(props, "cut_bot", obs_module_text("CropBottom"),
|
||||
0, 4096, 1);
|
||||
0, 4096, 1);
|
||||
|
||||
obs_properties_add_bool(props, "swap_redblue",
|
||||
obs_module_text("SwapRedBlue"));
|
||||
obs_module_text("SwapRedBlue"));
|
||||
obs_properties_add_bool(props, "lock_x", obs_module_text("LockX"));
|
||||
|
||||
obs_properties_add_bool(props, "show_cursor",
|
||||
obs_module_text("CaptureCursor"));
|
||||
obs_module_text("CaptureCursor"));
|
||||
|
||||
obs_properties_add_bool(props, "include_border",
|
||||
obs_module_text("IncludeXBorder"));
|
||||
obs_module_text("IncludeXBorder"));
|
||||
|
||||
obs_properties_add_bool(props, "exclude_alpha",
|
||||
obs_module_text("ExcludeAlpha"));
|
||||
obs_module_text("ExcludeAlpha"));
|
||||
|
||||
return props;
|
||||
}
|
||||
|
|
@ -108,20 +106,24 @@ void XCompcapMain::defaults(obs_data_t *settings)
|
|||
|
||||
#define FIND_WINDOW_INTERVAL 2.0
|
||||
|
||||
struct XCompcapMain_private
|
||||
{
|
||||
struct XCompcapMain_private {
|
||||
XCompcapMain_private()
|
||||
:win(0)
|
||||
,cut_top(0), cur_cut_top(0)
|
||||
,cut_left(0), cur_cut_left(0)
|
||||
,cut_right(0), cur_cut_right(0)
|
||||
,cut_bot(0), cur_cut_bot(0)
|
||||
,inverted(false)
|
||||
,width(0),height(0)
|
||||
,pixmap(0)
|
||||
,glxpixmap(0)
|
||||
,tex(0)
|
||||
,gltex(0)
|
||||
: win(0),
|
||||
cut_top(0),
|
||||
cur_cut_top(0),
|
||||
cut_left(0),
|
||||
cur_cut_left(0),
|
||||
cut_right(0),
|
||||
cur_cut_right(0),
|
||||
cut_bot(0),
|
||||
cur_cut_bot(0),
|
||||
inverted(false),
|
||||
width(0),
|
||||
height(0),
|
||||
pixmap(0),
|
||||
glxpixmap(0),
|
||||
tex(0),
|
||||
gltex(0)
|
||||
{
|
||||
pthread_mutexattr_init(&lockattr);
|
||||
pthread_mutexattr_settype(&lockattr, PTHREAD_MUTEX_RECURSIVE);
|
||||
|
|
@ -169,7 +171,6 @@ struct XCompcapMain_private
|
|||
xcursor_t *cursor = nullptr;
|
||||
};
|
||||
|
||||
|
||||
XCompcapMain::XCompcapMain(obs_data_t *settings, obs_source_t *source)
|
||||
{
|
||||
p = new XCompcapMain_private;
|
||||
|
|
@ -231,7 +232,7 @@ static Window getWindowFromString(std::string wstr)
|
|||
std::string wcls = wstr.substr(lastMark + markSize);
|
||||
|
||||
Window matchedNameWin = wid;
|
||||
for (Window cwin: XCompcap::getTopLevelWindows()) {
|
||||
for (Window cwin : XCompcap::getTopLevelWindows()) {
|
||||
std::string cwinname = XCompcap::getWindowName(cwin);
|
||||
std::string ccls = XCompcap::getWindowClass(cwin);
|
||||
|
||||
|
|
@ -249,38 +250,103 @@ static Window getWindowFromString(std::string wstr)
|
|||
static void xcc_cleanup(XCompcapMain_private *p)
|
||||
{
|
||||
PLock lock(&p->lock);
|
||||
XDisplayLock xlock;
|
||||
XErrorLock xlock;
|
||||
|
||||
if (p->gltex) {
|
||||
GLuint gltex = *(GLuint*)gs_texture_get_obj(p->gltex);
|
||||
GLuint gltex = *(GLuint *)gs_texture_get_obj(p->gltex);
|
||||
glBindTexture(GL_TEXTURE_2D, gltex);
|
||||
glXReleaseTexImageEXT(xdisp, p->glxpixmap, GLX_FRONT_LEFT_EXT);
|
||||
if (p->glxpixmap) {
|
||||
glXReleaseTexImageEXT(xdisp, p->glxpixmap,
|
||||
GLX_FRONT_LEFT_EXT);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR,
|
||||
"cleanup glXReleaseTexImageEXT failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.resetError();
|
||||
}
|
||||
glXDestroyPixmap(xdisp, p->glxpixmap);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR,
|
||||
"cleanup glXDestroyPixmap failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.resetError();
|
||||
}
|
||||
p->glxpixmap = 0;
|
||||
}
|
||||
gs_texture_destroy(p->gltex);
|
||||
p->gltex = 0;
|
||||
}
|
||||
|
||||
if (p->glxpixmap) {
|
||||
glXDestroyPixmap(xdisp, p->glxpixmap);
|
||||
p->glxpixmap = 0;
|
||||
}
|
||||
|
||||
if (p->pixmap) {
|
||||
XFreePixmap(xdisp, p->pixmap);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "cleanup glXDestroyPixmap failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.resetError();
|
||||
}
|
||||
p->pixmap = 0;
|
||||
}
|
||||
|
||||
if (p->win) {
|
||||
XCompositeUnredirectWindow(xdisp, p->win,
|
||||
CompositeRedirectAutomatic);
|
||||
CompositeRedirectAutomatic);
|
||||
XSelectInput(xdisp, p->win, 0);
|
||||
p->win = 0;
|
||||
}
|
||||
|
||||
if (p->tex) {
|
||||
gs_texture_destroy(p->tex);
|
||||
p->tex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static gs_color_format gs_format_from_tex()
|
||||
{
|
||||
GLint iformat = 0;
|
||||
// consider GL_ARB_internalformat_query
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT,
|
||||
&iformat);
|
||||
|
||||
// These formats are known to be wrong on Intel platforms. We intentionally
|
||||
// use swapped internal formats here to preserve historic behavior which
|
||||
// swapped colors accidentally and because D3D11 would not support a
|
||||
// GS_RGBX format
|
||||
switch (iformat) {
|
||||
case GL_RGB:
|
||||
return GS_BGRX;
|
||||
case GL_RGBA:
|
||||
return GS_RGBA;
|
||||
default:
|
||||
return GS_RGBA;
|
||||
}
|
||||
}
|
||||
|
||||
// from libobs-opengl/gl-subsystem.h because we need to handle GLX modifying textures outside libobs.
|
||||
struct fb_info;
|
||||
|
||||
struct gs_texture {
|
||||
gs_device_t *device;
|
||||
enum gs_texture_type type;
|
||||
enum gs_color_format format;
|
||||
GLenum gl_format;
|
||||
GLenum gl_target;
|
||||
GLenum gl_internal_format;
|
||||
GLenum gl_type;
|
||||
GLuint texture;
|
||||
uint32_t levels;
|
||||
bool is_dynamic;
|
||||
bool is_render_target;
|
||||
bool is_dummy;
|
||||
bool gen_mipmaps;
|
||||
|
||||
gs_samplerstate_t *cur_sampler;
|
||||
struct fbo_info *fbo;
|
||||
};
|
||||
// End shitty hack.
|
||||
|
||||
void XCompcapMain::updateSettings(obs_data_t *settings)
|
||||
{
|
||||
PLock lock(&p->lock);
|
||||
XErrorLock xlock;
|
||||
ObsGsContextHolder obsctx;
|
||||
|
||||
blog(LOG_DEBUG, "Settings updating");
|
||||
|
|
@ -290,8 +356,8 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
xcc_cleanup(p);
|
||||
|
||||
if (settings) {
|
||||
const char *windowName = obs_data_get_string(settings,
|
||||
"capture_window");
|
||||
const char *windowName =
|
||||
obs_data_get_string(settings, "capture_window");
|
||||
|
||||
p->windowName = windowName;
|
||||
p->win = getWindowFromString(windowName);
|
||||
|
|
@ -303,30 +369,28 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
p->lockX = obs_data_get_bool(settings, "lock_x");
|
||||
p->swapRedBlue = obs_data_get_bool(settings, "swap_redblue");
|
||||
p->show_cursor = obs_data_get_bool(settings, "show_cursor");
|
||||
p->include_border = obs_data_get_bool(settings, "include_border");
|
||||
p->include_border =
|
||||
obs_data_get_bool(settings, "include_border");
|
||||
p->exclude_alpha = obs_data_get_bool(settings, "exclude_alpha");
|
||||
p->draw_opaque = false;
|
||||
} else {
|
||||
p->win = prevWin;
|
||||
}
|
||||
|
||||
xlock.resetError();
|
||||
|
||||
XErrorLock xlock;
|
||||
if (p->win)
|
||||
XCompositeRedirectWindow(xdisp, p->win,
|
||||
CompositeRedirectAutomatic);
|
||||
|
||||
CompositeRedirectAutomatic);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "XCompositeRedirectWindow failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.getErrorText().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->win)
|
||||
XSelectInput(xdisp, p->win,
|
||||
StructureNotifyMask
|
||||
| ExposureMask
|
||||
| VisibilityChangeMask);
|
||||
StructureNotifyMask | ExposureMask |
|
||||
VisibilityChangeMask);
|
||||
XSync(xdisp, 0);
|
||||
|
||||
XWindowAttributes attr;
|
||||
|
|
@ -342,75 +406,26 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
int x, y;
|
||||
|
||||
XTranslateCoordinates(xdisp, p->win, attr.root, 0, 0, &x, &y,
|
||||
&child);
|
||||
&child);
|
||||
xcursor_offset(p->cursor, x, y);
|
||||
}
|
||||
|
||||
gs_color_format cf = GS_RGBA;
|
||||
|
||||
if (p->exclude_alpha) {
|
||||
cf = GS_BGRX;
|
||||
}
|
||||
|
||||
bool has_alpha = true;
|
||||
|
||||
const int attrs[] =
|
||||
{
|
||||
GLX_BIND_TO_TEXTURE_RGBA_EXT, GL_TRUE,
|
||||
GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
|
||||
GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
|
||||
GLX_ALPHA_SIZE, 8,
|
||||
GLX_DOUBLEBUFFER, GL_FALSE,
|
||||
None
|
||||
};
|
||||
|
||||
const int config_attrs[] = {GLX_BIND_TO_TEXTURE_RGBA_EXT,
|
||||
GL_TRUE,
|
||||
GLX_DRAWABLE_TYPE,
|
||||
GLX_PIXMAP_BIT,
|
||||
GLX_BIND_TO_TEXTURE_TARGETS_EXT,
|
||||
GLX_TEXTURE_2D_BIT_EXT,
|
||||
GLX_DOUBLEBUFFER,
|
||||
GL_FALSE,
|
||||
None};
|
||||
int nelem = 0;
|
||||
GLXFBConfig *configs = glXGetFBConfigs(xdisp,
|
||||
XCompcap::getRootWindowScreen(attr.root),
|
||||
&nelem);
|
||||
GLXFBConfig *configs = glXChooseFBConfig(
|
||||
xdisp, XCompcap::getRootWindowScreen(attr.root), config_attrs,
|
||||
&nelem);
|
||||
|
||||
if (nelem <= 0) {
|
||||
blog(LOG_ERROR, "no fb configs available");
|
||||
p->win = 0;
|
||||
p->height = 0;
|
||||
p->width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
GLXFBConfig config;
|
||||
for (int i = 0; i < nelem; i++) {
|
||||
config = configs[i];
|
||||
XVisualInfo *visual = glXGetVisualFromFBConfig(xdisp, config);
|
||||
if (!visual)
|
||||
continue;
|
||||
|
||||
if (attr.visual->visualid != visual->visualid) {
|
||||
XFree(visual);
|
||||
continue;
|
||||
}
|
||||
XFree(visual);
|
||||
|
||||
int value;
|
||||
glXGetFBConfigAttrib(xdisp, config, GLX_ALPHA_SIZE, &value);
|
||||
if (value != 8)
|
||||
has_alpha = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
XFree(configs);
|
||||
configs = glXChooseFBConfig(xdisp,
|
||||
XCompcap::getRootWindowScreen(attr.root),
|
||||
attrs, &nelem);
|
||||
|
||||
if (nelem <= 0) {
|
||||
blog(LOG_ERROR, "no matching fb config found");
|
||||
p->win = 0;
|
||||
p->height = 0;
|
||||
p->width = 0;
|
||||
return;
|
||||
}
|
||||
bool found = false;
|
||||
GLXFBConfig config;
|
||||
for (int i = 0; i < nelem; i++) {
|
||||
config = configs[i];
|
||||
XVisualInfo *visual = glXGetVisualFromFBConfig(xdisp, config);
|
||||
|
|
@ -425,10 +440,16 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
config = configs[0];
|
||||
if (!found) {
|
||||
blog(LOG_ERROR, "no matching fb config found");
|
||||
p->win = 0;
|
||||
p->height = 0;
|
||||
p->width = 0;
|
||||
XFree(configs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cf == GS_BGRX || !has_alpha) {
|
||||
if (p->exclude_alpha || attr.depth != 32) {
|
||||
p->draw_opaque = true;
|
||||
}
|
||||
|
||||
|
|
@ -462,93 +483,83 @@ void XCompcapMain::updateSettings(obs_data_t *settings)
|
|||
p->cur_cut_right = 0;
|
||||
}
|
||||
|
||||
if (p->tex)
|
||||
gs_texture_destroy(p->tex);
|
||||
|
||||
uint8_t *texData = new uint8_t[width() * height() * 4];
|
||||
|
||||
memset(texData, 0, width() * height() * 4);
|
||||
|
||||
const uint8_t* texDataArr[] = { texData, 0 };
|
||||
|
||||
p->tex = gs_texture_create(width(), height(), cf, 1,
|
||||
texDataArr, 0);
|
||||
|
||||
delete[] texData;
|
||||
|
||||
if (p->swapRedBlue) {
|
||||
GLuint tex = *(GLuint*)gs_texture_get_obj(p->tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
// Precautionary since we dont error check every GLX call above.
|
||||
xlock.resetError();
|
||||
|
||||
p->pixmap = XCompositeNameWindowPixmap(xdisp, p->win);
|
||||
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "XCompositeNameWindowPixmap failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.getErrorText().c_str());
|
||||
p->pixmap = 0;
|
||||
XFree(configs);
|
||||
return;
|
||||
}
|
||||
|
||||
const int attribs_alpha[] =
|
||||
{
|
||||
GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
|
||||
GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
|
||||
None
|
||||
};
|
||||
|
||||
const int attribs_no_alpha[] =
|
||||
{
|
||||
GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
|
||||
GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
|
||||
None
|
||||
};
|
||||
|
||||
const int *attribs = cf == GS_RGBA ? attribs_alpha : attribs_no_alpha;
|
||||
|
||||
p->glxpixmap = glXCreatePixmap(xdisp, config, p->pixmap, attribs);
|
||||
// Should be consistent format with config we are using. Since we searched on RGBA lets use RGBA here.
|
||||
const int pixmap_attrs[] = {GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
|
||||
GLX_TEXTURE_FORMAT_EXT,
|
||||
GLX_TEXTURE_FORMAT_RGBA_EXT, None};
|
||||
|
||||
p->glxpixmap = glXCreatePixmap(xdisp, config, p->pixmap, pixmap_attrs);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "glXCreatePixmap failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
xlock.getErrorText().c_str());
|
||||
XFreePixmap(xdisp, p->pixmap);
|
||||
XFree(configs);
|
||||
p->pixmap = 0;
|
||||
p->glxpixmap = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
XFree(configs);
|
||||
|
||||
p->gltex = gs_texture_create(p->width, p->height, cf, 1, 0,
|
||||
GS_GL_DUMMYTEX);
|
||||
|
||||
GLuint gltex = *(GLuint*)gs_texture_get_obj(p->gltex);
|
||||
// Build an OBS texture to bind the pixmap to.
|
||||
p->gltex = gs_texture_create(p->width, p->height, GS_RGBA, 1, 0,
|
||||
GS_GL_DUMMYTEX);
|
||||
GLuint gltex = *(GLuint *)gs_texture_get_obj(p->gltex);
|
||||
glBindTexture(GL_TEXTURE_2D, gltex);
|
||||
glXBindTexImageEXT(xdisp, p->glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
if (xlock.gotError()) {
|
||||
blog(LOG_ERROR, "glXBindTexImageEXT failed: %s",
|
||||
xlock.getErrorText().c_str());
|
||||
XFreePixmap(xdisp, p->pixmap);
|
||||
XFree(configs);
|
||||
p->pixmap = 0;
|
||||
p->glxpixmap = 0;
|
||||
return;
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// glxBindTexImageEXT might modify the textures format.
|
||||
gs_color_format format = gs_format_from_tex();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
// sync OBS texture format based on any glxBindTexImageEXT changes
|
||||
p->gltex->format = format;
|
||||
|
||||
// Create a pure OBS texture to use for rendering. Using the same
|
||||
// format so we can copy instead of drawing from the source gltex.
|
||||
if (p->tex)
|
||||
gs_texture_destroy(p->tex);
|
||||
p->tex = gs_texture_create(width(), height(), format, 1, 0,
|
||||
GS_GL_DUMMYTEX);
|
||||
if (p->swapRedBlue) {
|
||||
GLuint tex = *(GLuint *)gs_texture_get_obj(p->tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
if (!p->windowName.empty()) {
|
||||
blog(LOG_INFO, "[window-capture: '%s'] update settings:\n"
|
||||
"\ttitle: %s\n"
|
||||
"\tclass: %s\n"
|
||||
"\tHas alpha: %s\n"
|
||||
"\tFound proper GLXFBConfig: %s\n",
|
||||
obs_source_get_name(p->source),
|
||||
XCompcap::getWindowName(p->win).c_str(),
|
||||
XCompcap::getWindowClass(p->win).c_str(),
|
||||
has_alpha ? "yes" : "no",
|
||||
found ? "yes" : "no");
|
||||
blog(LOG_DEBUG, "\n"
|
||||
"\tid: %s",
|
||||
std::to_string((long long)p->win).c_str());
|
||||
blog(LOG_INFO,
|
||||
"[window-capture: '%s'] update settings:\n"
|
||||
"\ttitle: %s\n"
|
||||
"\tclass: %s\n"
|
||||
"\tBit depth: %i\n"
|
||||
"\tFound proper GLXFBConfig (in %i): %s\n",
|
||||
obs_source_get_name(p->source),
|
||||
XCompcap::getWindowName(p->win).c_str(),
|
||||
XCompcap::getWindowClass(p->win).c_str(), attr.depth,
|
||||
nelem, found ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -596,33 +607,28 @@ void XCompcapMain::tick(float seconds)
|
|||
obs_enter_graphics();
|
||||
|
||||
if (p->lockX) {
|
||||
// XDisplayLock is still live so we should already be locked.
|
||||
XLockDisplay(xdisp);
|
||||
XSync(xdisp, 0);
|
||||
}
|
||||
|
||||
if (p->include_border) {
|
||||
gs_copy_texture_region(
|
||||
p->tex, 0, 0,
|
||||
p->gltex,
|
||||
p->cur_cut_left,
|
||||
p->cur_cut_top,
|
||||
width(), height());
|
||||
gs_copy_texture_region(p->tex, 0, 0, p->gltex, p->cur_cut_left,
|
||||
p->cur_cut_top, width(), height());
|
||||
} else {
|
||||
gs_copy_texture_region(
|
||||
p->tex, 0, 0,
|
||||
p->gltex,
|
||||
p->cur_cut_left + p->border,
|
||||
p->cur_cut_top + p->border,
|
||||
width(), height());
|
||||
gs_copy_texture_region(p->tex, 0, 0, p->gltex,
|
||||
p->cur_cut_left + p->border,
|
||||
p->cur_cut_top + p->border, width(),
|
||||
height());
|
||||
}
|
||||
|
||||
if (p->cursor && p->show_cursor) {
|
||||
xcursor_tick(p->cursor);
|
||||
|
||||
p->cursor_outside =
|
||||
p->cursor->x < p->cur_cut_left ||
|
||||
p->cursor->y < p->cur_cut_top ||
|
||||
p->cursor->x > int(p->width - p->cur_cut_right) ||
|
||||
p->cursor->x < p->cur_cut_left ||
|
||||
p->cursor->y < p->cur_cut_top ||
|
||||
p->cursor->x > int(p->width - p->cur_cut_right) ||
|
||||
p->cursor->y > int(p->height - p->cur_cut_bot);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
struct XCompcapMain_private;
|
||||
|
||||
class XCompcapMain
|
||||
{
|
||||
public:
|
||||
class XCompcapMain {
|
||||
public:
|
||||
static bool init();
|
||||
static void deinit();
|
||||
|
||||
|
|
@ -22,6 +21,6 @@ class XCompcapMain
|
|||
uint32_t width();
|
||||
uint32_t height();
|
||||
|
||||
private:
|
||||
private:
|
||||
XCompcapMain_private *p;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,38 +2,38 @@
|
|||
|
||||
#include "xcompcap-main.hpp"
|
||||
|
||||
static void* xcompcap_create(obs_data_t *settings, obs_source_t *source)
|
||||
static void *xcompcap_create(obs_data_t *settings, obs_source_t *source)
|
||||
{
|
||||
return new XCompcapMain(settings, source);
|
||||
}
|
||||
|
||||
static void xcompcap_destroy(void *data)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
delete cc;
|
||||
}
|
||||
|
||||
static void xcompcap_video_tick(void* data, float seconds)
|
||||
static void xcompcap_video_tick(void *data, float seconds)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
cc->tick(seconds);
|
||||
}
|
||||
|
||||
static void xcompcap_video_render(void* data, gs_effect_t *effect)
|
||||
static void xcompcap_video_render(void *data, gs_effect_t *effect)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
cc->render(effect);
|
||||
}
|
||||
|
||||
static uint32_t xcompcap_getwidth(void* data)
|
||||
static uint32_t xcompcap_getwidth(void *data)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
return cc->width();
|
||||
}
|
||||
|
||||
static uint32_t xcompcap_getheight(void* data)
|
||||
static uint32_t xcompcap_getheight(void *data)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
return cc->height();
|
||||
}
|
||||
|
||||
|
|
@ -51,11 +51,11 @@ void xcompcap_defaults(obs_data_t *settings)
|
|||
|
||||
void xcompcap_update(void *data, obs_data_t *settings)
|
||||
{
|
||||
XCompcapMain* cc = (XCompcapMain*)data;
|
||||
XCompcapMain *cc = (XCompcapMain *)data;
|
||||
cc->updateSettings(settings);
|
||||
}
|
||||
|
||||
static const char* xcompcap_getname(void*)
|
||||
static const char *xcompcap_getname(void *)
|
||||
{
|
||||
return obs_module_text("XCCapture");
|
||||
}
|
||||
|
|
@ -69,20 +69,19 @@ extern "C" void xcomposite_load(void)
|
|||
memset(&sinfo, 0, sizeof(obs_source_info));
|
||||
|
||||
sinfo.id = "xcomposite_input";
|
||||
sinfo.output_flags = OBS_SOURCE_VIDEO |
|
||||
OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE;
|
||||
sinfo.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE;
|
||||
|
||||
sinfo.get_name = xcompcap_getname;
|
||||
sinfo.create = xcompcap_create;
|
||||
sinfo.destroy = xcompcap_destroy;
|
||||
sinfo.get_name = xcompcap_getname;
|
||||
sinfo.create = xcompcap_create;
|
||||
sinfo.destroy = xcompcap_destroy;
|
||||
sinfo.get_properties = xcompcap_props;
|
||||
sinfo.get_defaults = xcompcap_defaults;
|
||||
sinfo.update = xcompcap_update;
|
||||
sinfo.video_tick = xcompcap_video_tick;
|
||||
sinfo.video_render = xcompcap_video_render;
|
||||
sinfo.get_width = xcompcap_getwidth;
|
||||
sinfo.get_height = xcompcap_getheight;
|
||||
sinfo.get_defaults = xcompcap_defaults;
|
||||
sinfo.update = xcompcap_update;
|
||||
sinfo.video_tick = xcompcap_video_tick;
|
||||
sinfo.video_render = xcompcap_video_render;
|
||||
sinfo.get_width = xcompcap_getwidth;
|
||||
sinfo.get_height = xcompcap_getheight;
|
||||
|
||||
obs_register_source(&sinfo);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,26 +26,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
* size or by creating a new texture if the size is different
|
||||
*/
|
||||
static void xcb_xcursor_create(xcb_xcursor_t *data,
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc)
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc)
|
||||
{
|
||||
uint32_t *pixels = xcb_xfixes_get_cursor_image_cursor_image(xc);
|
||||
if (!pixels)
|
||||
return;
|
||||
|
||||
if (data->tex && data->last_height == xc->width &&
|
||||
data->last_width == xc->height) {
|
||||
gs_texture_set_image(data->tex, (const uint8_t *) pixels,
|
||||
xc->width * sizeof(uint32_t), false);
|
||||
data->last_width == xc->height) {
|
||||
gs_texture_set_image(data->tex, (const uint8_t *)pixels,
|
||||
xc->width * sizeof(uint32_t), false);
|
||||
} else {
|
||||
if (data->tex)
|
||||
gs_texture_destroy(data->tex);
|
||||
|
||||
data->tex = gs_texture_create(xc->width, xc->height,
|
||||
GS_BGRA, 1, (const uint8_t **) &pixels, GS_DYNAMIC);
|
||||
data->tex = gs_texture_create(xc->width, xc->height, GS_BGRA, 1,
|
||||
(const uint8_t **)&pixels,
|
||||
GS_DYNAMIC);
|
||||
}
|
||||
|
||||
data->last_serial = xc->cursor_serial;
|
||||
data->last_width = xc->width;
|
||||
data->last_width = xc->width;
|
||||
data->last_height = xc->height;
|
||||
}
|
||||
|
||||
|
|
@ -58,8 +59,8 @@ xcb_xcursor_t *xcb_xcursor_init(xcb_connection_t *xcb)
|
|||
|
||||
xcb_xfixes_query_version_cookie_t xfix_c;
|
||||
|
||||
xfix_c = xcb_xfixes_query_version_unchecked(xcb,
|
||||
XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
|
||||
xfix_c = xcb_xfixes_query_version_unchecked(
|
||||
xcb, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
|
||||
free(xcb_xfixes_query_version_reply(xcb, xfix_c, NULL));
|
||||
|
||||
return data;
|
||||
|
|
@ -73,7 +74,7 @@ void xcb_xcursor_destroy(xcb_xcursor_t *data)
|
|||
}
|
||||
|
||||
void xcb_xcursor_update(xcb_xcursor_t *data,
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc)
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc)
|
||||
{
|
||||
if (!data || !xc)
|
||||
return;
|
||||
|
|
@ -81,8 +82,8 @@ void xcb_xcursor_update(xcb_xcursor_t *data,
|
|||
if (!data->tex || data->last_serial != xc->cursor_serial)
|
||||
xcb_xcursor_create(data, xc);
|
||||
|
||||
data->x = xc->x - data->x_org;
|
||||
data->y = xc->y - data->y_org;
|
||||
data->x = xc->x - data->x_org;
|
||||
data->y = xc->y - data->y_org;
|
||||
data->x_render = data->x - xc->xhot;
|
||||
data->y_render = data->y - xc->yhot;
|
||||
}
|
||||
|
|
@ -92,7 +93,7 @@ void xcb_xcursor_render(xcb_xcursor_t *data)
|
|||
if (!data->tex)
|
||||
return;
|
||||
|
||||
gs_effect_t *effect = gs_get_effect();
|
||||
gs_effect_t *effect = gs_get_effect();
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
||||
gs_effect_set_texture(image, data->tex);
|
||||
|
||||
|
|
@ -109,9 +110,8 @@ void xcb_xcursor_render(xcb_xcursor_t *data)
|
|||
gs_blend_state_pop();
|
||||
}
|
||||
|
||||
void xcb_xcursor_offset(xcb_xcursor_t* data, const int x_org, const int y_org)
|
||||
void xcb_xcursor_offset(xcb_xcursor_t *data, const int x_org, const int y_org)
|
||||
{
|
||||
data->x_org = x_org;
|
||||
data->y_org = y_org;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ typedef struct {
|
|||
unsigned int last_height;
|
||||
gs_texture_t *tex;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int x_org;
|
||||
int y_org;
|
||||
float x_render;
|
||||
float y_render;
|
||||
int x;
|
||||
int y;
|
||||
int x_org;
|
||||
int y_org;
|
||||
float x_render;
|
||||
float y_render;
|
||||
} xcb_xcursor_t;
|
||||
|
||||
/**
|
||||
|
|
@ -60,7 +60,7 @@ void xcb_xcursor_destroy(xcb_xcursor_t *data);
|
|||
*
|
||||
*/
|
||||
void xcb_xcursor_update(xcb_xcursor_t *data,
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc);
|
||||
xcb_xfixes_get_cursor_image_reply_t *xc);
|
||||
|
||||
/**
|
||||
* Draw the cursor
|
||||
|
|
|
|||
|
|
@ -28,12 +28,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
* Theres a lot of talk about this in other implementation and they tend to
|
||||
* be really complicated, but this naive approach seems to work fine ...
|
||||
*/
|
||||
static uint32_t *xcursor_pixels(XFixesCursorImage *xc) {
|
||||
static uint32_t *xcursor_pixels(XFixesCursorImage *xc)
|
||||
{
|
||||
uint_fast32_t size = xc->width * xc->height;
|
||||
uint32_t *pixels = bmalloc(size * sizeof(uint32_t));
|
||||
|
||||
for (uint_fast32_t i = 0; i < size; ++i)
|
||||
pixels[i] = (uint32_t) xc->pixels[i];
|
||||
pixels[i] = (uint32_t)xc->pixels[i];
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
|
@ -42,22 +43,23 @@ static uint32_t *xcursor_pixels(XFixesCursorImage *xc) {
|
|||
* Create the cursor texture, either by updating if the new cursor has the same
|
||||
* size or by creating a new texture if the size is different
|
||||
*/
|
||||
static void xcursor_create(xcursor_t *data, XFixesCursorImage *xc) {
|
||||
static void xcursor_create(xcursor_t *data, XFixesCursorImage *xc)
|
||||
{
|
||||
uint32_t *pixels = xcursor_pixels(xc);
|
||||
if (!pixels)
|
||||
return;
|
||||
|
||||
if (data->tex
|
||||
&& data->last_height == xc->width
|
||||
&& data->last_width == xc->height) {
|
||||
gs_texture_set_image(data->tex, (const uint8_t *) pixels,
|
||||
xc->width * sizeof(uint32_t), False);
|
||||
if (data->tex && data->last_height == xc->width &&
|
||||
data->last_width == xc->height) {
|
||||
gs_texture_set_image(data->tex, (const uint8_t *)pixels,
|
||||
xc->width * sizeof(uint32_t), False);
|
||||
} else {
|
||||
if (data->tex)
|
||||
gs_texture_destroy(data->tex);
|
||||
|
||||
data->tex = gs_texture_create(xc->width, xc->height,
|
||||
GS_BGRA, 1, (const uint8_t **) &pixels, GS_DYNAMIC);
|
||||
data->tex = gs_texture_create(xc->width, xc->height, GS_BGRA, 1,
|
||||
(const uint8_t **)&pixels,
|
||||
GS_DYNAMIC);
|
||||
}
|
||||
|
||||
bfree(pixels);
|
||||
|
|
@ -67,7 +69,8 @@ static void xcursor_create(xcursor_t *data, XFixesCursorImage *xc) {
|
|||
data->last_height = xc->height;
|
||||
}
|
||||
|
||||
xcursor_t *xcursor_init(Display *dpy) {
|
||||
xcursor_t *xcursor_init(Display *dpy)
|
||||
{
|
||||
xcursor_t *data = bzalloc(sizeof(xcursor_t));
|
||||
|
||||
data->dpy = dpy;
|
||||
|
|
@ -76,13 +79,15 @@ xcursor_t *xcursor_init(Display *dpy) {
|
|||
return data;
|
||||
}
|
||||
|
||||
void xcursor_destroy(xcursor_t *data) {
|
||||
void xcursor_destroy(xcursor_t *data)
|
||||
{
|
||||
if (data->tex)
|
||||
gs_texture_destroy(data->tex);
|
||||
bfree(data);
|
||||
}
|
||||
|
||||
void xcursor_tick(xcursor_t *data) {
|
||||
void xcursor_tick(xcursor_t *data)
|
||||
{
|
||||
XFixesCursorImage *xc = XFixesGetCursorImage(data->dpy);
|
||||
if (!xc)
|
||||
return;
|
||||
|
|
@ -98,11 +103,12 @@ void xcursor_tick(xcursor_t *data) {
|
|||
XFree(xc);
|
||||
}
|
||||
|
||||
void xcursor_render(xcursor_t *data) {
|
||||
void xcursor_render(xcursor_t *data)
|
||||
{
|
||||
if (!data->tex)
|
||||
return;
|
||||
|
||||
gs_effect_t *effect = gs_get_effect();
|
||||
gs_effect_t *effect = gs_get_effect();
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
||||
gs_effect_set_texture(image, data->tex);
|
||||
|
||||
|
|
@ -119,9 +125,8 @@ void xcursor_render(xcursor_t *data) {
|
|||
gs_blend_state_pop();
|
||||
}
|
||||
|
||||
void xcursor_offset(xcursor_t* data, int_fast32_t x_org, int_fast32_t y_org)
|
||||
void xcursor_offset(xcursor_t *data, int_fast32_t x_org, int_fast32_t y_org)
|
||||
{
|
||||
data->x_org = x_org;
|
||||
data->y_org = y_org;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ bool xinerama_is_active(xcb_connection_t *xcb)
|
|||
|
||||
bool active = true;
|
||||
xcb_xinerama_is_active_cookie_t xnr_c;
|
||||
xcb_xinerama_is_active_reply_t *xnr_r;
|
||||
xcb_xinerama_is_active_reply_t *xnr_r;
|
||||
|
||||
xnr_c = xcb_xinerama_is_active_unchecked(xcb);
|
||||
xnr_r = xcb_xinerama_is_active_reply(xcb, xnr_c, NULL);
|
||||
|
|
@ -48,7 +48,7 @@ int xinerama_screen_count(xcb_connection_t *xcb)
|
|||
|
||||
int screens = 0;
|
||||
xcb_xinerama_query_screens_cookie_t scr_c;
|
||||
xcb_xinerama_query_screens_reply_t *scr_r;
|
||||
xcb_xinerama_query_screens_reply_t *scr_r;
|
||||
|
||||
scr_c = xcb_xinerama_query_screens_unchecked(xcb);
|
||||
scr_r = xcb_xinerama_query_screens_reply(xcb, scr_c, NULL);
|
||||
|
|
@ -60,15 +60,15 @@ int xinerama_screen_count(xcb_connection_t *xcb)
|
|||
}
|
||||
|
||||
int xinerama_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *x, int_fast32_t *y,
|
||||
int_fast32_t *w, int_fast32_t *h)
|
||||
int_fast32_t *x, int_fast32_t *y, int_fast32_t *w,
|
||||
int_fast32_t *h)
|
||||
{
|
||||
if (!xcb)
|
||||
goto fail;
|
||||
|
||||
bool success = false;
|
||||
xcb_xinerama_query_screens_cookie_t scr_c;
|
||||
xcb_xinerama_query_screens_reply_t *scr_r;
|
||||
xcb_xinerama_query_screens_reply_t *scr_r;
|
||||
xcb_xinerama_screen_info_iterator_t iter;
|
||||
|
||||
scr_c = xcb_xinerama_query_screens_unchecked(xcb);
|
||||
|
|
@ -113,7 +113,7 @@ int randr_screen_count(xcb_connection_t *xcb)
|
|||
screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data;
|
||||
|
||||
xcb_randr_get_screen_resources_cookie_t res_c;
|
||||
xcb_randr_get_screen_resources_reply_t* res_r;
|
||||
xcb_randr_get_screen_resources_reply_t *res_r;
|
||||
|
||||
res_c = xcb_randr_get_screen_resources(xcb, screen->root);
|
||||
res_r = xcb_randr_get_screen_resources_reply(xcb, res_c, 0);
|
||||
|
|
@ -124,15 +124,14 @@ int randr_screen_count(xcb_connection_t *xcb)
|
|||
}
|
||||
|
||||
int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *x, int_fast32_t *y,
|
||||
int_fast32_t *w, int_fast32_t *h,
|
||||
xcb_screen_t **rscreen)
|
||||
int_fast32_t *x, int_fast32_t *y, int_fast32_t *w,
|
||||
int_fast32_t *h, xcb_screen_t **rscreen)
|
||||
{
|
||||
xcb_screen_t *xscreen;
|
||||
xscreen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data;
|
||||
|
||||
xcb_randr_get_screen_resources_cookie_t res_c;
|
||||
xcb_randr_get_screen_resources_reply_t* res_r;
|
||||
xcb_randr_get_screen_resources_reply_t *res_r;
|
||||
|
||||
res_c = xcb_randr_get_screen_resources(xcb, xscreen->root);
|
||||
res_r = xcb_randr_get_screen_resources_reply(xcb, res_c, 0);
|
||||
|
|
@ -168,8 +167,8 @@ fail:
|
|||
return -1;
|
||||
}
|
||||
|
||||
int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *w, int_fast32_t *h)
|
||||
int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, int_fast32_t *w,
|
||||
int_fast32_t *h)
|
||||
{
|
||||
if (!xcb)
|
||||
goto fail;
|
||||
|
|
@ -194,14 +193,14 @@ fail:
|
|||
return -1;
|
||||
}
|
||||
|
||||
xcb_shm_t* xshm_xcb_attach(xcb_connection_t *xcb, const int w, const int h)
|
||||
xcb_shm_t *xshm_xcb_attach(xcb_connection_t *xcb, const int w, const int h)
|
||||
{
|
||||
if (!xcb)
|
||||
return NULL;
|
||||
|
||||
xcb_shm_t *shm = bzalloc(sizeof(xcb_shm_t));
|
||||
shm->xcb = xcb;
|
||||
shm->seg = xcb_generate_id(shm->xcb);
|
||||
shm->xcb = xcb;
|
||||
shm->seg = xcb_generate_id(shm->xcb);
|
||||
|
||||
shm->shmid = shmget(IPC_PRIVATE, w * h * 4, IPC_CREAT | 0777);
|
||||
if (shm->shmid == -1)
|
||||
|
|
@ -224,7 +223,7 @@ void xshm_xcb_detach(xcb_shm_t *shm)
|
|||
|
||||
xcb_shm_detach(shm->xcb, shm->seg);
|
||||
|
||||
if ((char *) shm->data != (char *) -1)
|
||||
if ((char *)shm->data != (char *)-1)
|
||||
shmdt(shm->data);
|
||||
|
||||
if (shm->shmid != -1)
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ extern "C" {
|
|||
|
||||
typedef struct {
|
||||
xcb_connection_t *xcb;
|
||||
xcb_shm_seg_t seg;
|
||||
int shmid;
|
||||
uint8_t *data;
|
||||
xcb_shm_seg_t seg;
|
||||
int shmid;
|
||||
uint8_t *data;
|
||||
} xcb_shm_t;
|
||||
|
||||
/**
|
||||
|
|
@ -61,8 +61,8 @@ int xinerama_screen_count(xcb_connection_t *xcb);
|
|||
* @return < 0 on error
|
||||
*/
|
||||
int xinerama_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *x, int_fast32_t *y,
|
||||
int_fast32_t *w, int_fast32_t *h);
|
||||
int_fast32_t *x, int_fast32_t *y, int_fast32_t *w,
|
||||
int_fast32_t *h);
|
||||
|
||||
/**
|
||||
* Check for Randr extension
|
||||
|
|
@ -93,9 +93,8 @@ int randr_screen_count(xcb_connection_t *xcb);
|
|||
* @return < 0 on error
|
||||
*/
|
||||
int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *x, int_fast32_t *y,
|
||||
int_fast32_t *w, int_fast32_t *h,
|
||||
xcb_screen_t **rscreen);
|
||||
int_fast32_t *x, int_fast32_t *y, int_fast32_t *w,
|
||||
int_fast32_t *h, xcb_screen_t **rscreen);
|
||||
|
||||
/**
|
||||
* Get screen geometry for a X11 screen
|
||||
|
|
@ -109,8 +108,8 @@ int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
|||
*
|
||||
* @return < 0 on error
|
||||
*/
|
||||
int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
|
||||
int_fast32_t *w, int_fast32_t *h);
|
||||
int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, int_fast32_t *w,
|
||||
int_fast32_t *h);
|
||||
|
||||
/**
|
||||
* Attach a shared memory segment to the X-Server
|
||||
|
|
|
|||
|
|
@ -33,26 +33,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define blog(level, msg, ...) blog(level, "xshm-input: " msg, ##__VA_ARGS__)
|
||||
|
||||
struct xshm_data {
|
||||
obs_source_t *source;
|
||||
obs_source_t *source;
|
||||
|
||||
xcb_connection_t *xcb;
|
||||
xcb_screen_t *xcb_screen;
|
||||
xcb_shm_t *xshm;
|
||||
xcb_xcursor_t *cursor;
|
||||
xcb_screen_t *xcb_screen;
|
||||
xcb_shm_t *xshm;
|
||||
xcb_xcursor_t *cursor;
|
||||
|
||||
char *server;
|
||||
uint_fast32_t screen_id;
|
||||
int_fast32_t x_org;
|
||||
int_fast32_t y_org;
|
||||
int_fast32_t width;
|
||||
int_fast32_t height;
|
||||
char *server;
|
||||
uint_fast32_t screen_id;
|
||||
int_fast32_t x_org;
|
||||
int_fast32_t y_org;
|
||||
int_fast32_t width;
|
||||
int_fast32_t height;
|
||||
|
||||
gs_texture_t *texture;
|
||||
gs_texture_t *texture;
|
||||
|
||||
bool show_cursor;
|
||||
bool use_xinerama;
|
||||
bool use_randr;
|
||||
bool advanced;
|
||||
bool show_cursor;
|
||||
bool use_xinerama;
|
||||
bool use_randr;
|
||||
bool advanced;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -66,8 +66,8 @@ static inline void xshm_resize_texture(struct xshm_data *data)
|
|||
{
|
||||
if (data->texture)
|
||||
gs_texture_destroy(data->texture);
|
||||
data->texture = gs_texture_create(data->width, data->height,
|
||||
GS_BGRA, 1, NULL, GS_DYNAMIC);
|
||||
data->texture = gs_texture_create(data->width, data->height, GS_BGRA, 1,
|
||||
NULL, GS_DYNAMIC);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -102,26 +102,23 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data)
|
|||
int_fast32_t old_height = data->height;
|
||||
|
||||
if (data->use_randr) {
|
||||
if (randr_screen_geo(data->xcb, data->screen_id,
|
||||
&data->x_org, &data->y_org,
|
||||
&data->width, &data->height,
|
||||
&data->xcb_screen) < 0) {
|
||||
if (randr_screen_geo(data->xcb, data->screen_id, &data->x_org,
|
||||
&data->y_org, &data->width, &data->height,
|
||||
&data->xcb_screen) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (data->use_xinerama) {
|
||||
} else if (data->use_xinerama) {
|
||||
if (xinerama_screen_geo(data->xcb, data->screen_id,
|
||||
&data->x_org, &data->y_org,
|
||||
&data->width, &data->height) < 0) {
|
||||
&data->x_org, &data->y_org,
|
||||
&data->width, &data->height) < 0) {
|
||||
return -1;
|
||||
}
|
||||
data->xcb_screen = xcb_get_screen(data->xcb, 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
data->x_org = 0;
|
||||
data->y_org = 0;
|
||||
if (x11_screen_geo(data->xcb, data->screen_id,
|
||||
&data->width, &data->height) < 0) {
|
||||
if (x11_screen_geo(data->xcb, data->screen_id, &data->width,
|
||||
&data->height) < 0) {
|
||||
return -1;
|
||||
}
|
||||
data->xcb_screen = xcb_get_screen(data->xcb, data->screen_id);
|
||||
|
|
@ -132,9 +129,10 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data)
|
|||
return -1;
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "Geometry %"PRIdFAST32"x%"PRIdFAST32
|
||||
" @ %"PRIdFAST32",%"PRIdFAST32,
|
||||
data->width, data->height, data->x_org, data->y_org);
|
||||
blog(LOG_INFO,
|
||||
"Geometry %" PRIdFAST32 "x%" PRIdFAST32 " @ %" PRIdFAST32
|
||||
",%" PRIdFAST32,
|
||||
data->width, data->height, data->x_org, data->y_org);
|
||||
|
||||
if (old_width == data->width && old_height == data->height)
|
||||
return 0;
|
||||
|
|
@ -145,7 +143,7 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data)
|
|||
/**
|
||||
* Returns the name of the plugin
|
||||
*/
|
||||
static const char* xshm_getname(void *unused)
|
||||
static const char *xshm_getname(void *unused)
|
||||
{
|
||||
UNUSED_PARAMETER(unused);
|
||||
return obs_module_text("X11SharedMemoryScreenInput");
|
||||
|
|
@ -190,8 +188,8 @@ static void xshm_capture_stop(struct xshm_data *data)
|
|||
*/
|
||||
static void xshm_capture_start(struct xshm_data *data)
|
||||
{
|
||||
const char *server = (data->advanced && *data->server)
|
||||
? data->server : NULL;
|
||||
const char *server = (data->advanced && *data->server) ? data->server
|
||||
: NULL;
|
||||
|
||||
data->xcb = xcb_connect(server, NULL);
|
||||
if (!data->xcb || xcb_connection_has_error(data->xcb)) {
|
||||
|
|
@ -239,10 +237,10 @@ static void xshm_update(void *vptr, obs_data_t *settings)
|
|||
|
||||
xshm_capture_stop(data);
|
||||
|
||||
data->screen_id = obs_data_get_int(settings, "screen");
|
||||
data->screen_id = obs_data_get_int(settings, "screen");
|
||||
data->show_cursor = obs_data_get_bool(settings, "show_cursor");
|
||||
data->advanced = obs_data_get_bool(settings, "advanced");
|
||||
data->server = bstrdup(obs_data_get_string(settings, "server"));
|
||||
data->advanced = obs_data_get_bool(settings, "advanced");
|
||||
data->server = bstrdup(obs_data_get_string(settings, "server"));
|
||||
|
||||
xshm_capture_start(data);
|
||||
}
|
||||
|
|
@ -260,11 +258,11 @@ static void xshm_defaults(obs_data_t *defaults)
|
|||
/**
|
||||
* Toggle visibility of advanced settings
|
||||
*/
|
||||
static bool xshm_toggle_advanced(obs_properties_t *props,
|
||||
obs_property_t *p, obs_data_t *settings)
|
||||
static bool xshm_toggle_advanced(obs_properties_t *props, obs_property_t *p,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
UNUSED_PARAMETER(p);
|
||||
const bool visible = obs_data_get_bool(settings, "advanced");
|
||||
const bool visible = obs_data_get_bool(settings, "advanced");
|
||||
obs_property_t *server = obs_properties_get(props, "server");
|
||||
|
||||
obs_property_set_visible(server, visible);
|
||||
|
|
@ -278,14 +276,14 @@ static bool xshm_toggle_advanced(obs_properties_t *props,
|
|||
/**
|
||||
* The x server was changed
|
||||
*/
|
||||
static bool xshm_server_changed(obs_properties_t *props,
|
||||
obs_property_t *p, obs_data_t *settings)
|
||||
static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
UNUSED_PARAMETER(p);
|
||||
|
||||
bool advanced = obs_data_get_bool(settings, "advanced");
|
||||
bool advanced = obs_data_get_bool(settings, "advanced");
|
||||
int_fast32_t old_screen = obs_data_get_int(settings, "screen");
|
||||
const char *server = obs_data_get_string(settings, "server");
|
||||
const char *server = obs_data_get_string(settings, "server");
|
||||
obs_property_t *screens = obs_properties_get(props, "screen");
|
||||
|
||||
/* we want a real NULL here in case there is no string here */
|
||||
|
|
@ -303,11 +301,11 @@ static bool xshm_server_changed(obs_properties_t *props,
|
|||
dstr_init(&screen_info);
|
||||
bool randr = randr_is_active(xcb);
|
||||
bool xinerama = xinerama_is_active(xcb);
|
||||
int_fast32_t count = (randr) ?
|
||||
randr_screen_count(xcb) :
|
||||
(xinerama) ?
|
||||
xinerama_screen_count(xcb) :
|
||||
xcb_setup_roots_length(xcb_get_setup(xcb));
|
||||
int_fast32_t count =
|
||||
(randr) ? randr_screen_count(xcb)
|
||||
: (xinerama)
|
||||
? xinerama_screen_count(xcb)
|
||||
: xcb_setup_roots_length(xcb_get_setup(xcb));
|
||||
|
||||
for (int_fast32_t i = 0; i < count; ++i) {
|
||||
int_fast32_t x, y, w, h;
|
||||
|
|
@ -320,21 +318,22 @@ static bool xshm_server_changed(obs_properties_t *props,
|
|||
else
|
||||
x11_screen_geo(xcb, i, &w, &h);
|
||||
|
||||
dstr_printf(&screen_info, "Screen %"PRIuFAST32" (%"PRIuFAST32
|
||||
"x%"PRIuFAST32" @ %"PRIuFAST32
|
||||
",%"PRIuFAST32")", i, w, h, x, y);
|
||||
dstr_printf(&screen_info,
|
||||
"Screen %" PRIuFAST32 " (%" PRIuFAST32
|
||||
"x%" PRIuFAST32 " @ %" PRIuFAST32 ",%" PRIuFAST32
|
||||
")",
|
||||
i, w, h, x, y);
|
||||
|
||||
obs_property_list_add_int(screens, screen_info.array, i);
|
||||
}
|
||||
|
||||
/* handle missing screen */
|
||||
if (old_screen + 1 > count) {
|
||||
dstr_printf(&screen_info, "Screen %"PRIuFAST32" (not found)",
|
||||
old_screen);
|
||||
size_t index = obs_property_list_add_int(screens,
|
||||
screen_info.array, old_screen);
|
||||
dstr_printf(&screen_info, "Screen %" PRIuFAST32 " (not found)",
|
||||
old_screen);
|
||||
size_t index = obs_property_list_add_int(
|
||||
screens, screen_info.array, old_screen);
|
||||
obs_property_list_item_disable(screens, index, true);
|
||||
|
||||
}
|
||||
|
||||
dstr_free(&screen_info);
|
||||
|
|
@ -355,13 +354,13 @@ static obs_properties_t *xshm_properties(void *vptr)
|
|||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_properties_add_list(props, "screen", obs_module_text("Screen"),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_properties_add_bool(props, "show_cursor",
|
||||
obs_module_text("CaptureCursor"));
|
||||
obs_property_t *advanced = obs_properties_add_bool(props, "advanced",
|
||||
obs_module_text("AdvancedSettings"));
|
||||
obs_property_t *server = obs_properties_add_text(props, "server",
|
||||
obs_module_text("XServer"), OBS_TEXT_DEFAULT);
|
||||
obs_module_text("CaptureCursor"));
|
||||
obs_property_t *advanced = obs_properties_add_bool(
|
||||
props, "advanced", obs_module_text("AdvancedSettings"));
|
||||
obs_property_t *server = obs_properties_add_text(
|
||||
props, "server", obs_module_text("XServer"), OBS_TEXT_DEFAULT);
|
||||
|
||||
obs_property_set_modified_callback(advanced, xshm_toggle_advanced);
|
||||
obs_property_set_modified_callback(server, xshm_server_changed);
|
||||
|
|
@ -415,14 +414,16 @@ static void xshm_video_tick(void *vptr, float seconds)
|
|||
if (!obs_source_showing(data->source))
|
||||
return;
|
||||
|
||||
xcb_shm_get_image_cookie_t img_c;
|
||||
xcb_shm_get_image_reply_t *img_r;
|
||||
xcb_shm_get_image_cookie_t img_c;
|
||||
xcb_shm_get_image_reply_t *img_r;
|
||||
xcb_xfixes_get_cursor_image_cookie_t cur_c;
|
||||
xcb_xfixes_get_cursor_image_reply_t *cur_r;
|
||||
xcb_xfixes_get_cursor_image_reply_t *cur_r;
|
||||
|
||||
img_c = xcb_shm_get_image_unchecked(data->xcb, data->xcb_screen->root,
|
||||
data->x_org, data->y_org, data->width, data->height,
|
||||
~0, XCB_IMAGE_FORMAT_Z_PIXMAP, data->xshm->seg, 0);
|
||||
data->x_org, data->y_org,
|
||||
data->width, data->height, ~0,
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP,
|
||||
data->xshm->seg, 0);
|
||||
cur_c = xcb_xfixes_get_cursor_image_unchecked(data->xcb);
|
||||
|
||||
img_r = xcb_shm_get_image_reply(data->xcb, img_c, NULL);
|
||||
|
|
@ -433,8 +434,8 @@ static void xshm_video_tick(void *vptr, float seconds)
|
|||
|
||||
obs_enter_graphics();
|
||||
|
||||
gs_texture_set_image(data->texture, (void *) data->xshm->data,
|
||||
data->width * 4, false);
|
||||
gs_texture_set_image(data->texture, (void *)data->xshm->data,
|
||||
data->width * 4, false);
|
||||
xcb_xcursor_update(data->cursor, cur_r);
|
||||
|
||||
obs_leave_graphics();
|
||||
|
|
@ -491,19 +492,18 @@ static uint32_t xshm_getheight(void *vptr)
|
|||
}
|
||||
|
||||
struct obs_source_info xshm_input = {
|
||||
.id = "xshm_input",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO |
|
||||
OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE,
|
||||
.get_name = xshm_getname,
|
||||
.create = xshm_create,
|
||||
.destroy = xshm_destroy,
|
||||
.update = xshm_update,
|
||||
.get_defaults = xshm_defaults,
|
||||
.id = "xshm_input",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE,
|
||||
.get_name = xshm_getname,
|
||||
.create = xshm_create,
|
||||
.destroy = xshm_destroy,
|
||||
.update = xshm_update,
|
||||
.get_defaults = xshm_defaults,
|
||||
.get_properties = xshm_properties,
|
||||
.video_tick = xshm_video_tick,
|
||||
.video_render = xshm_video_render,
|
||||
.get_width = xshm_getwidth,
|
||||
.get_height = xshm_getheight
|
||||
.video_tick = xshm_video_tick,
|
||||
.video_render = xshm_video_render,
|
||||
.get_width = xshm_getwidth,
|
||||
.get_height = xshm_getheight,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
StartJACKServer="JACK-Server starten"
|
||||
StartJACKServer="JACK‐Server starten"
|
||||
Channels="Anzahl der Kanäle"
|
||||
JACKInput="JACK-Eingabe-Client"
|
||||
JACKInput="JACK‐Eingabe‐Client"
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ static const char *jack_input_getname(void *unused)
|
|||
*/
|
||||
static void jack_destroy(void *vptr)
|
||||
{
|
||||
struct jack_data* data = (struct jack_data*)vptr;
|
||||
struct jack_data *data = (struct jack_data *)vptr;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
|
@ -51,14 +51,14 @@ static void jack_destroy(void *vptr)
|
|||
*/
|
||||
static void jack_update(void *vptr, obs_data_t *settings)
|
||||
{
|
||||
struct jack_data* data = (struct jack_data*)vptr;
|
||||
struct jack_data *data = (struct jack_data *)vptr;
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
const char *new_device;
|
||||
bool settings_changed = false;
|
||||
bool settings_changed = false;
|
||||
bool new_jack_start_server = obs_data_get_bool(settings, "startjack");
|
||||
int new_channel_count = obs_data_get_int(settings, "channels");
|
||||
int new_channel_count = obs_data_get_int(settings, "channels");
|
||||
|
||||
if (new_jack_start_server != data->start_jack_server) {
|
||||
data->start_jack_server = new_jack_start_server;
|
||||
|
|
@ -99,7 +99,7 @@ static void *jack_create(obs_data_t *settings, obs_source_t *source)
|
|||
struct jack_data *data = bzalloc(sizeof(struct jack_data));
|
||||
|
||||
pthread_mutex_init(&data->jack_mutex, NULL);
|
||||
data->source = source;
|
||||
data->source = source;
|
||||
data->channels = -1;
|
||||
|
||||
jack_update(data, settings);
|
||||
|
|
@ -129,22 +129,22 @@ static obs_properties_t *jack_input_properties(void *unused)
|
|||
|
||||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_properties_add_int(props, "channels",
|
||||
obs_module_text("Channels"), 1, 8, 1);
|
||||
obs_properties_add_int(props, "channels", obs_module_text("Channels"),
|
||||
1, 8, 1);
|
||||
obs_properties_add_bool(props, "startjack",
|
||||
obs_module_text("StartJACKServer"));
|
||||
obs_module_text("StartJACKServer"));
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
struct obs_source_info jack_output_capture = {
|
||||
.id = "jack_output_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_AUDIO,
|
||||
.get_name = jack_input_getname,
|
||||
.create = jack_create,
|
||||
.destroy = jack_destroy,
|
||||
.update = jack_update,
|
||||
.get_defaults = jack_input_defaults,
|
||||
.get_properties = jack_input_properties
|
||||
.id = "jack_output_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_AUDIO,
|
||||
.get_name = jack_input_getname,
|
||||
.create = jack_create,
|
||||
.destroy = jack_destroy,
|
||||
.update = jack_update,
|
||||
.get_defaults = jack_input_defaults,
|
||||
.get_properties = jack_input_properties,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,33 +36,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/
|
||||
static enum speaker_layout jack_channels_to_obs_speakers(uint_fast32_t channels)
|
||||
{
|
||||
switch(channels) {
|
||||
case 1: return SPEAKERS_MONO;
|
||||
case 2: return SPEAKERS_STEREO;
|
||||
case 3: return SPEAKERS_2POINT1;
|
||||
case 5: return SPEAKERS_4POINT1;
|
||||
case 6: return SPEAKERS_5POINT1;
|
||||
switch (channels) {
|
||||
case 1:
|
||||
return SPEAKERS_MONO;
|
||||
case 2:
|
||||
return SPEAKERS_STEREO;
|
||||
case 3:
|
||||
return SPEAKERS_2POINT1;
|
||||
case 5:
|
||||
return SPEAKERS_4POINT1;
|
||||
case 6:
|
||||
return SPEAKERS_5POINT1;
|
||||
/* What should we do with 7 channels? */
|
||||
/* case 7: return SPEAKERS_...; */
|
||||
case 8: return SPEAKERS_7POINT1;
|
||||
case 8:
|
||||
return SPEAKERS_7POINT1;
|
||||
}
|
||||
|
||||
return SPEAKERS_UNKNOWN;
|
||||
}
|
||||
|
||||
int jack_process_callback(jack_nframes_t nframes, void* arg)
|
||||
int jack_process_callback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
struct jack_data* data = (struct jack_data*)arg;
|
||||
struct jack_data *data = (struct jack_data *)arg;
|
||||
if (data == 0)
|
||||
return 0;
|
||||
|
||||
pthread_mutex_lock(&data->jack_mutex);
|
||||
|
||||
struct obs_source_audio out;
|
||||
out.speakers = jack_channels_to_obs_speakers(data->channels);
|
||||
out.samples_per_sec = jack_get_sample_rate (data->jack_client);
|
||||
out.speakers = jack_channels_to_obs_speakers(data->channels);
|
||||
out.samples_per_sec = jack_get_sample_rate(data->jack_client);
|
||||
/* format is always 32 bit float for jack */
|
||||
out.format = AUDIO_FORMAT_FLOAT_PLANAR;
|
||||
out.format = AUDIO_FORMAT_FLOAT_PLANAR;
|
||||
|
||||
for (unsigned int i = 0; i < data->channels; ++i) {
|
||||
jack_default_audio_sample_t *jack_buffer =
|
||||
|
|
@ -71,61 +77,61 @@ int jack_process_callback(jack_nframes_t nframes, void* arg)
|
|||
out.data[i] = (uint8_t *)jack_buffer;
|
||||
}
|
||||
|
||||
out.frames = nframes;
|
||||
out.frames = nframes;
|
||||
out.timestamp = os_gettime_ns() -
|
||||
jack_frames_to_time(data->jack_client, nframes);
|
||||
jack_frames_to_time(data->jack_client, nframes);
|
||||
|
||||
obs_source_output_audio(data->source, &out);
|
||||
pthread_mutex_unlock(&data->jack_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int_fast32_t jack_init(struct jack_data* data)
|
||||
int_fast32_t jack_init(struct jack_data *data)
|
||||
{
|
||||
pthread_mutex_lock(&data->jack_mutex);
|
||||
|
||||
if (data->jack_client != NULL)
|
||||
goto good;
|
||||
|
||||
jack_options_t jack_option = data->start_jack_server ?
|
||||
JackNullOption : JackNoStartServer;
|
||||
jack_options_t jack_option =
|
||||
data->start_jack_server ? JackNullOption : JackNoStartServer;
|
||||
|
||||
data->jack_client = jack_client_open(data->device, jack_option, 0);
|
||||
if (data->jack_client == NULL) {
|
||||
blog(LOG_ERROR,
|
||||
"jack_client_open Error:"
|
||||
"Could not create JACK client! %s",
|
||||
data->device);
|
||||
"jack_client_open Error:"
|
||||
"Could not create JACK client! %s",
|
||||
data->device);
|
||||
goto error;
|
||||
}
|
||||
|
||||
data->jack_ports = (jack_port_t**)bzalloc(
|
||||
sizeof(jack_port_t*) * data->channels);
|
||||
data->jack_ports =
|
||||
(jack_port_t **)bzalloc(sizeof(jack_port_t *) * data->channels);
|
||||
for (unsigned int i = 0; i < data->channels; ++i) {
|
||||
char port_name[10] = {'\0'};
|
||||
snprintf(port_name, sizeof(port_name), "in_%u", i+1);
|
||||
snprintf(port_name, sizeof(port_name), "in_%u", i + 1);
|
||||
|
||||
data->jack_ports[i] = jack_port_register(data->jack_client,
|
||||
port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
||||
data->jack_ports[i] = jack_port_register(
|
||||
data->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsInput, 0);
|
||||
if (data->jack_ports[i] == NULL) {
|
||||
blog(LOG_ERROR,
|
||||
"jack_port_register Error:"
|
||||
"Could not create JACK port! %s",
|
||||
port_name);
|
||||
"jack_port_register Error:"
|
||||
"Could not create JACK port! %s",
|
||||
port_name);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (jack_set_process_callback(data->jack_client,
|
||||
jack_process_callback, data) != 0) {
|
||||
if (jack_set_process_callback(data->jack_client, jack_process_callback,
|
||||
data) != 0) {
|
||||
blog(LOG_ERROR, "jack_set_process_callback Error");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (jack_activate(data->jack_client) != 0) {
|
||||
blog(LOG_ERROR,
|
||||
"jack_activate Error:"
|
||||
"Could not activate JACK client!");
|
||||
blog(LOG_ERROR, "jack_activate Error:"
|
||||
"Could not activate JACK client!");
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +144,7 @@ error:
|
|||
return 1;
|
||||
}
|
||||
|
||||
void deactivate_jack(struct jack_data* data)
|
||||
void deactivate_jack(struct jack_data *data)
|
||||
{
|
||||
pthread_mutex_lock(&data->jack_mutex);
|
||||
|
||||
|
|
@ -146,7 +152,8 @@ void deactivate_jack(struct jack_data* data)
|
|||
if (data->jack_ports != NULL) {
|
||||
for (int i = 0; i < data->channels; ++i) {
|
||||
if (data->jack_ports[i] != NULL)
|
||||
jack_port_unregister(data->jack_client,
|
||||
jack_port_unregister(
|
||||
data->jack_client,
|
||||
data->jack_ports[i]);
|
||||
}
|
||||
bfree(data->jack_ports);
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ struct jack_data {
|
|||
/**
|
||||
* Initialize the jack client and register the ports
|
||||
*/
|
||||
int_fast32_t jack_init(struct jack_data* data);
|
||||
int_fast32_t jack_init(struct jack_data *data);
|
||||
|
||||
/**
|
||||
* Destroys the jack client and unregisters the ports
|
||||
*/
|
||||
void deactivate_jack(struct jack_data* data);
|
||||
void deactivate_jack(struct jack_data *data);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
PulseInput="Captura de entrada audio (PulseAudio)"
|
||||
PulseOutput="Captura de saída de audio (PulseAudio)"
|
||||
PulseInput="Captura de entrada son (PulseAudio)"
|
||||
PulseOutput="Captura de saída de son (PulseAudio)"
|
||||
Device="Dispositivo"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
PulseInput="შემავალი ხმოვანი სიგნალის ჩაწერა (PulseAudio)"
|
||||
PulseInput="შემავალი ხმის ჩაწერა (PulseAudio)"
|
||||
PulseOutput="გამოტანილი ხმის ჩაწერა (PulseAudio)"
|
||||
Device="მოწყობილობა"
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
#include "pulse-wrapper.h"
|
||||
|
||||
#define NSEC_PER_SEC 1000000000LL
|
||||
#define NSEC_PER_SEC 1000000000LL
|
||||
#define NSEC_PER_MSEC 1000000L
|
||||
|
||||
#define PULSE_DATA(voidptr) struct pulse_data *data = voidptr;
|
||||
|
|
@ -53,15 +53,19 @@ static void pulse_stop_recording(struct pulse_data *data);
|
|||
/**
|
||||
* get obs from pulse audio format
|
||||
*/
|
||||
static enum audio_format pulse_to_obs_audio_format(
|
||||
pa_sample_format_t format)
|
||||
static enum audio_format pulse_to_obs_audio_format(pa_sample_format_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case PA_SAMPLE_U8: return AUDIO_FORMAT_U8BIT;
|
||||
case PA_SAMPLE_S16LE: return AUDIO_FORMAT_16BIT;
|
||||
case PA_SAMPLE_S32LE: return AUDIO_FORMAT_32BIT;
|
||||
case PA_SAMPLE_FLOAT32LE: return AUDIO_FORMAT_FLOAT;
|
||||
default: return AUDIO_FORMAT_UNKNOWN;
|
||||
case PA_SAMPLE_U8:
|
||||
return AUDIO_FORMAT_U8BIT;
|
||||
case PA_SAMPLE_S16LE:
|
||||
return AUDIO_FORMAT_16BIT;
|
||||
case PA_SAMPLE_S32LE:
|
||||
return AUDIO_FORMAT_32BIT;
|
||||
case PA_SAMPLE_FLOAT32LE:
|
||||
return AUDIO_FORMAT_FLOAT;
|
||||
default:
|
||||
return AUDIO_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
return AUDIO_FORMAT_UNKNOWN;
|
||||
|
|
@ -77,17 +81,24 @@ static enum audio_format pulse_to_obs_audio_format(
|
|||
* @note This *might* not work for some rather unusual setups, but should work
|
||||
* fine for the majority of cases.
|
||||
*/
|
||||
static enum speaker_layout pulse_channels_to_obs_speakers(
|
||||
uint_fast32_t channels)
|
||||
static enum speaker_layout
|
||||
pulse_channels_to_obs_speakers(uint_fast32_t channels)
|
||||
{
|
||||
switch(channels) {
|
||||
case 1: return SPEAKERS_MONO;
|
||||
case 2: return SPEAKERS_STEREO;
|
||||
case 3: return SPEAKERS_2POINT1;
|
||||
case 4: return SPEAKERS_4POINT0;
|
||||
case 5: return SPEAKERS_4POINT1;
|
||||
case 6: return SPEAKERS_5POINT1;
|
||||
case 8: return SPEAKERS_7POINT1;
|
||||
switch (channels) {
|
||||
case 1:
|
||||
return SPEAKERS_MONO;
|
||||
case 2:
|
||||
return SPEAKERS_STEREO;
|
||||
case 3:
|
||||
return SPEAKERS_2POINT1;
|
||||
case 4:
|
||||
return SPEAKERS_4POINT0;
|
||||
case 5:
|
||||
return SPEAKERS_4POINT1;
|
||||
case 6:
|
||||
return SPEAKERS_5POINT1;
|
||||
case 8:
|
||||
return SPEAKERS_7POINT1;
|
||||
}
|
||||
|
||||
return SPEAKERS_UNKNOWN;
|
||||
|
|
@ -185,19 +196,18 @@ static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
|
|||
|
||||
if (!frames) {
|
||||
blog(LOG_ERROR, "Got audio hole of %u bytes",
|
||||
(unsigned int) bytes);
|
||||
(unsigned int)bytes);
|
||||
pa_stream_drop(data->stream);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
struct obs_source_audio out;
|
||||
out.speakers = data->speakers;
|
||||
out.speakers = data->speakers;
|
||||
out.samples_per_sec = data->samples_per_sec;
|
||||
out.format = pulse_to_obs_audio_format(data->format);
|
||||
out.data[0] = (uint8_t *) frames;
|
||||
out.frames = bytes / data->bytes_per_frame;
|
||||
out.timestamp = get_sample_time(out.frames,
|
||||
out.samples_per_sec);
|
||||
out.format = pulse_to_obs_audio_format(data->format);
|
||||
out.data[0] = (uint8_t *)frames;
|
||||
out.frames = bytes / data->bytes_per_frame;
|
||||
out.timestamp = get_sample_time(out.frames, out.samples_per_sec);
|
||||
|
||||
if (!data->first_ts)
|
||||
data->first_ts = out.timestamp + STARTUP_TIMEOUT_NS;
|
||||
|
|
@ -217,29 +227,32 @@ exit:
|
|||
* Server info callback
|
||||
*/
|
||||
static void pulse_server_info(pa_context *c, const pa_server_info *i,
|
||||
void *userdata)
|
||||
void *userdata)
|
||||
{
|
||||
UNUSED_PARAMETER(c);
|
||||
PULSE_DATA(userdata);
|
||||
|
||||
blog(LOG_INFO, "Server name: '%s %s'",
|
||||
i->server_name, i->server_version);
|
||||
blog(LOG_INFO, "Server name: '%s %s'", i->server_name,
|
||||
i->server_version);
|
||||
|
||||
if (data->device && strcmp("default", data->device) == 0) {
|
||||
if (data->input) {
|
||||
bfree(data->device);
|
||||
data->device = bstrdup(i->default_source_name);
|
||||
|
||||
blog(LOG_DEBUG, "Default input device: '%s'", data->device);
|
||||
blog(LOG_DEBUG, "Default input device: '%s'",
|
||||
data->device);
|
||||
} else {
|
||||
char *monitor = bzalloc(strlen(i->default_sink_name) + 9);
|
||||
char *monitor =
|
||||
bzalloc(strlen(i->default_sink_name) + 9);
|
||||
strcat(monitor, i->default_sink_name);
|
||||
strcat(monitor, ".monitor");
|
||||
|
||||
bfree(data->device);
|
||||
data->device = bstrdup(monitor);
|
||||
|
||||
blog(LOG_DEBUG, "Default output device: '%s'", data->device);
|
||||
blog(LOG_DEBUG, "Default output device: '%s'",
|
||||
data->device);
|
||||
bfree(monitor);
|
||||
}
|
||||
}
|
||||
|
|
@ -254,7 +267,7 @@ static void pulse_server_info(pa_context *c, const pa_server_info *i,
|
|||
* configured to something obs can't deal with.
|
||||
*/
|
||||
static void pulse_source_info(pa_context *c, const pa_source_info *i, int eol,
|
||||
void *userdata)
|
||||
void *userdata)
|
||||
{
|
||||
UNUSED_PARAMETER(c);
|
||||
PULSE_DATA(userdata);
|
||||
|
|
@ -267,35 +280,36 @@ static void pulse_source_info(pa_context *c, const pa_source_info *i, int eol,
|
|||
if (eol > 0)
|
||||
goto skip;
|
||||
|
||||
blog(LOG_INFO, "Audio format: %s, %"PRIu32" Hz"
|
||||
", %"PRIu8" channels",
|
||||
pa_sample_format_to_string(i->sample_spec.format),
|
||||
i->sample_spec.rate,
|
||||
i->sample_spec.channels);
|
||||
blog(LOG_INFO,
|
||||
"Audio format: %s, %" PRIu32 " Hz"
|
||||
", %" PRIu8 " channels",
|
||||
pa_sample_format_to_string(i->sample_spec.format),
|
||||
i->sample_spec.rate, i->sample_spec.channels);
|
||||
|
||||
pa_sample_format_t format = i->sample_spec.format;
|
||||
if (pulse_to_obs_audio_format(format) == AUDIO_FORMAT_UNKNOWN) {
|
||||
format = PA_SAMPLE_FLOAT32LE;
|
||||
|
||||
blog(LOG_INFO, "Sample format %s not supported by OBS,"
|
||||
"using %s instead for recording",
|
||||
pa_sample_format_to_string(i->sample_spec.format),
|
||||
pa_sample_format_to_string(format));
|
||||
blog(LOG_INFO,
|
||||
"Sample format %s not supported by OBS,"
|
||||
"using %s instead for recording",
|
||||
pa_sample_format_to_string(i->sample_spec.format),
|
||||
pa_sample_format_to_string(format));
|
||||
}
|
||||
|
||||
uint8_t channels = i->sample_spec.channels;
|
||||
if (pulse_channels_to_obs_speakers(channels) == SPEAKERS_UNKNOWN) {
|
||||
channels = 2;
|
||||
|
||||
blog(LOG_INFO, "%c channels not supported by OBS,"
|
||||
"using %c instead for recording",
|
||||
i->sample_spec.channels,
|
||||
channels);
|
||||
blog(LOG_INFO,
|
||||
"%c channels not supported by OBS,"
|
||||
"using %c instead for recording",
|
||||
i->sample_spec.channels, channels);
|
||||
}
|
||||
|
||||
data->format = format;
|
||||
data->format = format;
|
||||
data->samples_per_sec = i->sample_spec.rate;
|
||||
data->channels = channels;
|
||||
data->channels = channels;
|
||||
|
||||
skip:
|
||||
pulse_signal(0);
|
||||
|
|
@ -313,24 +327,25 @@ skip:
|
|||
*/
|
||||
static int_fast32_t pulse_start_recording(struct pulse_data *data)
|
||||
{
|
||||
if (pulse_get_server_info(pulse_server_info, (void *) data) < 0) {
|
||||
if (pulse_get_server_info(pulse_server_info, (void *)data) < 0) {
|
||||
blog(LOG_ERROR, "Unable to get server info !");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pulse_get_source_info(pulse_source_info, data->device,
|
||||
(void *) data) < 0) {
|
||||
(void *)data) < 0) {
|
||||
blog(LOG_ERROR, "Unable to get source info !");
|
||||
return -1;
|
||||
}
|
||||
if (data->format == PA_SAMPLE_INVALID) {
|
||||
blog(LOG_ERROR, "An error occurred while getting the source info!");
|
||||
blog(LOG_ERROR,
|
||||
"An error occurred while getting the source info!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_sample_spec spec;
|
||||
spec.format = data->format;
|
||||
spec.rate = data->samples_per_sec;
|
||||
spec.format = data->format;
|
||||
spec.rate = data->samples_per_sec;
|
||||
spec.channels = data->channels;
|
||||
|
||||
if (!pa_sample_spec_valid(&spec)) {
|
||||
|
|
@ -344,7 +359,7 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data)
|
|||
pa_channel_map channel_map = pulse_channel_map(data->speakers);
|
||||
|
||||
data->stream = pulse_stream_new(obs_source_get_name(data->source),
|
||||
&spec, &channel_map);
|
||||
&spec, &channel_map);
|
||||
if (!data->stream) {
|
||||
blog(LOG_ERROR, "Unable to create stream");
|
||||
return -1;
|
||||
|
|
@ -352,21 +367,21 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data)
|
|||
|
||||
pulse_lock();
|
||||
pa_stream_set_read_callback(data->stream, pulse_stream_read,
|
||||
(void *) data);
|
||||
(void *)data);
|
||||
pulse_unlock();
|
||||
|
||||
pa_buffer_attr attr;
|
||||
attr.fragsize = pa_usec_to_bytes(25000, &spec);
|
||||
attr.maxlength = (uint32_t) -1;
|
||||
attr.minreq = (uint32_t) -1;
|
||||
attr.prebuf = (uint32_t) -1;
|
||||
attr.tlength = (uint32_t) -1;
|
||||
attr.fragsize = pa_usec_to_bytes(25000, &spec);
|
||||
attr.maxlength = (uint32_t)-1;
|
||||
attr.minreq = (uint32_t)-1;
|
||||
attr.prebuf = (uint32_t)-1;
|
||||
attr.tlength = (uint32_t)-1;
|
||||
|
||||
pa_stream_flags_t flags = PA_STREAM_ADJUST_LATENCY;
|
||||
|
||||
pulse_lock();
|
||||
int_fast32_t ret = pa_stream_connect_record(data->stream, data->device,
|
||||
&attr, flags);
|
||||
&attr, flags);
|
||||
pulse_unlock();
|
||||
if (ret < 0) {
|
||||
pulse_stop_recording(data);
|
||||
|
|
@ -392,8 +407,9 @@ static void pulse_stop_recording(struct pulse_data *data)
|
|||
}
|
||||
|
||||
blog(LOG_INFO, "Stopped recording from '%s'", data->device);
|
||||
blog(LOG_INFO, "Got %"PRIuFAST32" packets with %"PRIuFAST64" frames",
|
||||
data->packets, data->frames);
|
||||
blog(LOG_INFO,
|
||||
"Got %" PRIuFAST32 " packets with %" PRIuFAST64 " frames",
|
||||
data->packets, data->frames);
|
||||
|
||||
data->first_ts = 0;
|
||||
data->packets = 0;
|
||||
|
|
@ -404,14 +420,14 @@ static void pulse_stop_recording(struct pulse_data *data)
|
|||
* input info callback
|
||||
*/
|
||||
static void pulse_input_info(pa_context *c, const pa_source_info *i, int eol,
|
||||
void *userdata)
|
||||
void *userdata)
|
||||
{
|
||||
UNUSED_PARAMETER(c);
|
||||
if (eol != 0 || i->monitor_of_sink != PA_INVALID_INDEX)
|
||||
goto skip;
|
||||
|
||||
obs_property_list_add_string((obs_property_t*) userdata,
|
||||
i->description, i->name);
|
||||
obs_property_list_add_string((obs_property_t *)userdata, i->description,
|
||||
i->name);
|
||||
|
||||
skip:
|
||||
pulse_signal(0);
|
||||
|
|
@ -421,14 +437,14 @@ skip:
|
|||
* output info callback
|
||||
*/
|
||||
static void pulse_output_info(pa_context *c, const pa_sink_info *i, int eol,
|
||||
void *userdata)
|
||||
void *userdata)
|
||||
{
|
||||
UNUSED_PARAMETER(c);
|
||||
if (eol != 0 || i->monitor_source == PA_INVALID_INDEX)
|
||||
goto skip;
|
||||
|
||||
obs_property_list_add_string((obs_property_t*) userdata,
|
||||
i->description, i->monitor_source_name);
|
||||
obs_property_list_add_string((obs_property_t *)userdata, i->description,
|
||||
i->monitor_source_name);
|
||||
|
||||
skip:
|
||||
pulse_signal(0);
|
||||
|
|
@ -440,22 +456,22 @@ skip:
|
|||
static obs_properties_t *pulse_properties(bool input)
|
||||
{
|
||||
obs_properties_t *props = obs_properties_create();
|
||||
obs_property_t *devices = obs_properties_add_list(props, "device_id",
|
||||
obs_module_text("Device"), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_t *devices = obs_properties_add_list(
|
||||
props, "device_id", obs_module_text("Device"),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
|
||||
pulse_init();
|
||||
if (input)
|
||||
pulse_get_source_info_list(pulse_input_info, (void *) devices);
|
||||
pulse_get_source_info_list(pulse_input_info, (void *)devices);
|
||||
else
|
||||
pulse_get_sink_info_list(pulse_output_info, (void *) devices);
|
||||
pulse_get_sink_info_list(pulse_output_info, (void *)devices);
|
||||
pulse_unref();
|
||||
|
||||
size_t count = obs_property_list_item_count(devices);
|
||||
|
||||
if (count > 0)
|
||||
obs_property_list_insert_string(devices, 0,
|
||||
obs_module_text("Default"), "default");
|
||||
obs_property_list_insert_string(
|
||||
devices, 0, obs_module_text("Default"), "default");
|
||||
|
||||
return props;
|
||||
}
|
||||
|
|
@ -544,12 +560,13 @@ static void pulse_update(void *vptr, obs_data_t *settings)
|
|||
/**
|
||||
* Create the plugin object
|
||||
*/
|
||||
static void *pulse_create(obs_data_t *settings, obs_source_t *source, bool input)
|
||||
static void *pulse_create(obs_data_t *settings, obs_source_t *source,
|
||||
bool input)
|
||||
{
|
||||
struct pulse_data *data = bzalloc(sizeof(struct pulse_data));
|
||||
|
||||
data->input = input;
|
||||
data->source = source;
|
||||
data->input = input;
|
||||
data->source = source;
|
||||
|
||||
pulse_init();
|
||||
pulse_update(data, settings);
|
||||
|
|
@ -568,28 +585,26 @@ static void *pulse_output_create(obs_data_t *settings, obs_source_t *source)
|
|||
}
|
||||
|
||||
struct obs_source_info pulse_input_capture = {
|
||||
.id = "pulse_input_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_AUDIO |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE,
|
||||
.get_name = pulse_input_getname,
|
||||
.create = pulse_input_create,
|
||||
.destroy = pulse_destroy,
|
||||
.update = pulse_update,
|
||||
.get_defaults = pulse_defaults,
|
||||
.get_properties = pulse_input_properties
|
||||
.id = "pulse_input_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE,
|
||||
.get_name = pulse_input_getname,
|
||||
.create = pulse_input_create,
|
||||
.destroy = pulse_destroy,
|
||||
.update = pulse_update,
|
||||
.get_defaults = pulse_defaults,
|
||||
.get_properties = pulse_input_properties,
|
||||
};
|
||||
|
||||
struct obs_source_info pulse_output_capture = {
|
||||
.id = "pulse_output_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_AUDIO |
|
||||
OBS_SOURCE_DO_NOT_DUPLICATE |
|
||||
OBS_SOURCE_DO_NOT_SELF_MONITOR,
|
||||
.get_name = pulse_output_getname,
|
||||
.create = pulse_output_create,
|
||||
.destroy = pulse_destroy,
|
||||
.update = pulse_update,
|
||||
.get_defaults = pulse_defaults,
|
||||
.get_properties = pulse_output_properties
|
||||
.id = "pulse_output_capture",
|
||||
.type = OBS_SOURCE_TYPE_INPUT,
|
||||
.output_flags = OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE |
|
||||
OBS_SOURCE_DO_NOT_SELF_MONITOR,
|
||||
.get_name = pulse_output_getname,
|
||||
.create = pulse_output_create,
|
||||
.destroy = pulse_destroy,
|
||||
.update = pulse_update,
|
||||
.get_defaults = pulse_defaults,
|
||||
.get_properties = pulse_output_properties,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ static void pulse_init_context()
|
|||
pa_threaded_mainloop_get_api(pulse_mainloop), "OBS", p);
|
||||
|
||||
pa_context_set_state_callback(pulse_context,
|
||||
pulse_context_state_changed, NULL);
|
||||
pulse_context_state_changed, NULL);
|
||||
|
||||
pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
|
||||
pa_proplist_free(p);
|
||||
|
|
@ -163,15 +163,15 @@ void pulse_accept()
|
|||
pa_threaded_mainloop_accept(pulse_mainloop);
|
||||
}
|
||||
|
||||
int_fast32_t pulse_get_source_info_list(pa_source_info_cb_t cb, void* userdata)
|
||||
int_fast32_t pulse_get_source_info_list(pa_source_info_cb_t cb, void *userdata)
|
||||
{
|
||||
if (pulse_context_ready() < 0)
|
||||
return -1;
|
||||
|
||||
pulse_lock();
|
||||
|
||||
pa_operation *op = pa_context_get_source_info_list(
|
||||
pulse_context, cb, userdata);
|
||||
pa_operation *op =
|
||||
pa_context_get_source_info_list(pulse_context, cb, userdata);
|
||||
if (!op) {
|
||||
pulse_unlock();
|
||||
return -1;
|
||||
|
|
@ -192,8 +192,8 @@ int_fast32_t pulse_get_sink_info_list(pa_sink_info_cb_t cb, void *userdata)
|
|||
|
||||
pulse_lock();
|
||||
|
||||
pa_operation *op = pa_context_get_sink_info_list(
|
||||
pulse_context, cb, userdata);
|
||||
pa_operation *op =
|
||||
pa_context_get_sink_info_list(pulse_context, cb, userdata);
|
||||
if (!op) {
|
||||
pulse_unlock();
|
||||
return -1;
|
||||
|
|
@ -208,7 +208,7 @@ int_fast32_t pulse_get_sink_info_list(pa_sink_info_cb_t cb, void *userdata)
|
|||
}
|
||||
|
||||
int_fast32_t pulse_get_source_info(pa_source_info_cb_t cb, const char *name,
|
||||
void *userdata)
|
||||
void *userdata)
|
||||
{
|
||||
if (pulse_context_ready() < 0)
|
||||
return -1;
|
||||
|
|
@ -230,15 +230,15 @@ int_fast32_t pulse_get_source_info(pa_source_info_cb_t cb, const char *name,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int_fast32_t pulse_get_server_info(pa_server_info_cb_t cb, void* userdata)
|
||||
int_fast32_t pulse_get_server_info(pa_server_info_cb_t cb, void *userdata)
|
||||
{
|
||||
if (pulse_context_ready() < 0)
|
||||
return -1;
|
||||
|
||||
pulse_lock();
|
||||
|
||||
pa_operation *op = pa_context_get_server_info(
|
||||
pulse_context, cb, userdata);
|
||||
pa_operation *op =
|
||||
pa_context_get_server_info(pulse_context, cb, userdata);
|
||||
if (!op) {
|
||||
pulse_unlock();
|
||||
return -1;
|
||||
|
|
@ -251,8 +251,8 @@ int_fast32_t pulse_get_server_info(pa_server_info_cb_t cb, void* userdata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
pa_stream* pulse_stream_new(const char* name, const pa_sample_spec* ss,
|
||||
const pa_channel_map* map)
|
||||
pa_stream *pulse_stream_new(const char *name, const pa_sample_spec *ss,
|
||||
const pa_channel_map *map)
|
||||
{
|
||||
if (pulse_context_ready() < 0)
|
||||
return NULL;
|
||||
|
|
@ -260,11 +260,10 @@ pa_stream* pulse_stream_new(const char* name, const pa_sample_spec* ss,
|
|||
pulse_lock();
|
||||
|
||||
pa_proplist *p = pulse_properties();
|
||||
pa_stream *s = pa_stream_new_with_proplist(
|
||||
pulse_context, name, ss, map, p);
|
||||
pa_stream *s =
|
||||
pa_stream_new_with_proplist(pulse_context, name, ss, map, p);
|
||||
pa_proplist_free(p);
|
||||
|
||||
pulse_unlock();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ int_fast32_t pulse_get_sink_info_list(pa_sink_info_cb_t cb, void *userdata);
|
|||
* @warning call without active locks
|
||||
*/
|
||||
int_fast32_t pulse_get_source_info(pa_source_info_cb_t cb, const char *name,
|
||||
void *userdata);
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* Request server information
|
||||
|
|
@ -148,4 +148,4 @@ int_fast32_t pulse_get_server_info(pa_server_info_cb_t cb, void *userdata);
|
|||
* @warning call without active locks
|
||||
*/
|
||||
pa_stream *pulse_stream_new(const char *name, const pa_sample_spec *ss,
|
||||
const pa_channel_map *map);
|
||||
const pa_channel_map *map);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
V4L2Input="جهاز التقاط الفيديو (V4L2)"
|
||||
Device="الجهاز"
|
||||
Input="الوارد"
|
||||
VideoFormat="تنسيق الفيديو"
|
||||
VideoStandard="معيار الفيديو"
|
||||
DVTiming="توقيت DV"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Fotogrames per segon"
|
|||
LeaveUnchanged="No ho canviïs"
|
||||
UseBuffering="Usa memòria intermèdia"
|
||||
ColorRange="Gamma de colors"
|
||||
ColorRange.Default="Per defecte"
|
||||
ColorRange.Partial="Parcial"
|
||||
ColorRange.Full="Complet"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Snímkovací frekvence"
|
|||
LeaveUnchanged="Ponechat nezměněné"
|
||||
UseBuffering="Použít vyrovnávací paměť"
|
||||
ColorRange="Rozsah barev"
|
||||
ColorRange.Default="Výchozí"
|
||||
ColorRange.Partial="Částečný"
|
||||
ColorRange.Full="Plný"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Billedhastighed"
|
|||
LeaveUnchanged="Behold uændret"
|
||||
UseBuffering="Benyt buffering"
|
||||
ColorRange="Farveområde"
|
||||
ColorRange.Default="Standard"
|
||||
ColorRange.Partial="Delvist"
|
||||
ColorRange.Full="Fuldt"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ Device="Gerät"
|
|||
Input="Eingabe"
|
||||
VideoFormat="Videoformat"
|
||||
VideoStandard="Videostandard"
|
||||
DVTiming="DV-Timing"
|
||||
DVTiming="DV‐Timing"
|
||||
Resolution="Auflösung"
|
||||
FrameRate="Bildrate"
|
||||
LeaveUnchanged="Unverändert lassen"
|
||||
UseBuffering="Puffern benutzen"
|
||||
ColorRange="Farbbereich"
|
||||
ColorRange.Default="Standard"
|
||||
ColorRange.Partial="Begrenzt"
|
||||
ColorRange.Full="Voll"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,5 +9,6 @@ FrameRate="Frame Rate"
|
|||
LeaveUnchanged="Leave Unchanged"
|
||||
UseBuffering="Use Buffering"
|
||||
ColorRange="Color Range"
|
||||
ColorRange.Default="Default"
|
||||
ColorRange.Partial="Partial"
|
||||
ColorRange.Full="Full"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Frecuencia de imágenes"
|
|||
LeaveUnchanged="Dejar sin cambios"
|
||||
UseBuffering="Utilizar el almacenamiento en búfer"
|
||||
ColorRange="Gama de Colores"
|
||||
ColorRange.Default="Predeterminado"
|
||||
ColorRange.Partial="Parcial"
|
||||
ColorRange.Full="Completo"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Fotograma emaria"
|
|||
LeaveUnchanged="Utzi aldatu gabe"
|
||||
UseBuffering="Erabili bufferreratzea"
|
||||
ColorRange="Kolore tartea"
|
||||
ColorRange.Default="Lehenetsia"
|
||||
ColorRange.Partial="Partziala"
|
||||
ColorRange.Full="Osoa"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Kuvanopeus"
|
|||
LeaveUnchanged="Jätä ennalleen"
|
||||
UseBuffering="Käytä puskurointia"
|
||||
ColorRange="Värialue"
|
||||
ColorRange.Default="Oletus"
|
||||
ColorRange.Partial="Osittainen"
|
||||
ColorRange.Full="Täysi"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Images par seconde"
|
|||
LeaveUnchanged="Annuler les modifications"
|
||||
UseBuffering="Utiliser le tampon mémoire"
|
||||
ColorRange="Gamme de couleurs"
|
||||
ColorRange.Default="Par défaut"
|
||||
ColorRange.Partial="Partielle"
|
||||
ColorRange.Full="Complète"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,13 @@ Device="Dispositivo"
|
|||
Input="Entrada"
|
||||
VideoFormat="Formato de vídeo"
|
||||
VideoStandard="Vídeo estándar"
|
||||
DVTiming="Axuste DV"
|
||||
DVTiming="Sincronización do vídeo dixital (DV)"
|
||||
Resolution="Resolución"
|
||||
FrameRate="Velocidade de fotogramas"
|
||||
FrameRate="Taxa de fotogramas"
|
||||
LeaveUnchanged="Deixar sen cambios"
|
||||
UseBuffering="Utilizar o almacenamento no búfer"
|
||||
UseBuffering="Utilizar o almacenamento na memoria temporal"
|
||||
ColorRange="Gama de cores"
|
||||
ColorRange.Default="Predeterminado"
|
||||
ColorRange.Partial="Parcial"
|
||||
ColorRange.Full="Total"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Képkockasebesség"
|
|||
LeaveUnchanged="Változatlanul hagyni"
|
||||
UseBuffering="Pufferelés használata"
|
||||
ColorRange="Színtartomány"
|
||||
ColorRange.Default="Alapértelmezett"
|
||||
ColorRange.Partial="Részleges"
|
||||
ColorRange.Full="Teljes"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Velocità dei fotogrammi"
|
|||
LeaveUnchanged="Lascia invariato"
|
||||
UseBuffering="Utilizza il buffering"
|
||||
ColorRange="Gamma di colori"
|
||||
ColorRange.Default="Predefinito"
|
||||
ColorRange.Partial="Parziale"
|
||||
ColorRange.Full="Intero"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="フレームレート"
|
|||
LeaveUnchanged="変更せずに戻る"
|
||||
UseBuffering="バッファリングを使用する"
|
||||
ColorRange="色範囲"
|
||||
ColorRange.Default="既定"
|
||||
ColorRange.Partial="一部"
|
||||
ColorRange.Full="全部"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
V4L2Input="ვიდეოს გადამღები მოწყობილობა (V4L2)"
|
||||
V4L2Input="ვიდეოჩამწერი მოწყობილობა (V4L2)"
|
||||
Device="მოწყობილობა"
|
||||
Input="შემავალი"
|
||||
Input="შეტანა"
|
||||
VideoFormat="ვიდეოს ფორმატი"
|
||||
VideoStandard="ვიდეოს სტანდარტი"
|
||||
DVTiming="ციფრული ვიდეოს სინქრონიზაცია"
|
||||
|
|
@ -9,6 +9,7 @@ FrameRate="კადრის სიხშირე"
|
|||
LeaveUnchanged="უცვლელად დატოვება"
|
||||
UseBuffering="ბუფერიზაციის გამოყენება"
|
||||
ColorRange="ფერთა გამა"
|
||||
ColorRange.Default="ნაგულისხმევი"
|
||||
ColorRange.Partial="ნაწილობრივი"
|
||||
ColorRange.Full="სრული"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="프레임 레이트"
|
|||
LeaveUnchanged="저장하지 않고 두기"
|
||||
UseBuffering="버퍼링 사용"
|
||||
ColorRange="색상 범위"
|
||||
ColorRange.Default="기본값"
|
||||
ColorRange.Partial="부분"
|
||||
ColorRange.Full="전체"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Frame Rate"
|
|||
LeaveUnchanged="Ongewijzigd Laten"
|
||||
UseBuffering="Buffering Gebruiken"
|
||||
ColorRange="Kleurbereik"
|
||||
ColorRange.Default="Standaard"
|
||||
ColorRange.Partial="Gedeeltelijk"
|
||||
ColorRange.Full="Volledig"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Klatki na sekundę"
|
|||
LeaveUnchanged="Pozostaw bez zmian"
|
||||
UseBuffering="Użyj buforowania"
|
||||
ColorRange="Zakres kolorów"
|
||||
ColorRange.Default="Domyślny"
|
||||
ColorRange.Partial="Częściowy"
|
||||
ColorRange.Full="Pełny"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Taxa de quadros"
|
|||
LeaveUnchanged="Deixar inalterado"
|
||||
UseBuffering="Utilizar Buffering"
|
||||
ColorRange="Intervalo de Cor"
|
||||
ColorRange.Default="Padrão"
|
||||
ColorRange.Partial="Parcial"
|
||||
ColorRange.Full="Completo"
|
||||
|
||||
|
|
|
|||
|
|
@ -8,4 +8,8 @@ Resolution="Rezoluție"
|
|||
FrameRate="Frecvență de cadre"
|
||||
LeaveUnchanged="Lasă neschimbat"
|
||||
UseBuffering="Folosește zona tampon"
|
||||
ColorRange="Gamă de culori"
|
||||
ColorRange.Default="Implicită"
|
||||
ColorRange.Partial="Parțială"
|
||||
ColorRange.Full="Completă"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Частота кадров"
|
|||
LeaveUnchanged="Оставить без изменений"
|
||||
UseBuffering="Использовать буферизацию"
|
||||
ColorRange="Цветовой диапазон"
|
||||
ColorRange.Default="По умолчанию"
|
||||
ColorRange.Partial="Частичный"
|
||||
ColorRange.Full="Полный"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Bildhastighet"
|
|||
LeaveUnchanged="Lämna oförändrat"
|
||||
UseBuffering="Använd buffer"
|
||||
ColorRange="Färgintervall"
|
||||
ColorRange.Default="Standard"
|
||||
ColorRange.Partial="Delvis"
|
||||
ColorRange.Full="Full"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Kare Hızı"
|
|||
LeaveUnchanged="Değişmeden Bırak"
|
||||
UseBuffering="Arabelleğe Almayı Kullan"
|
||||
ColorRange="Renk Aralığı"
|
||||
ColorRange.Default="Varsayılan"
|
||||
ColorRange.Partial="Kısmi"
|
||||
ColorRange.Full="Tam"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="Частота кадрів"
|
|||
LeaveUnchanged="Залишити без змін"
|
||||
UseBuffering="Увімкнути буферизацію"
|
||||
ColorRange="Колірний діапазон"
|
||||
ColorRange.Default="За замовчанням"
|
||||
ColorRange.Partial="Частковий"
|
||||
ColorRange.Full="Повний"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ FrameRate="帧率"
|
|||
LeaveUnchanged="保持不变"
|
||||
UseBuffering="使用缓冲"
|
||||
ColorRange="颜色范围"
|
||||
ColorRange.Default="默认"
|
||||
ColorRange.Partial="部分"
|
||||
ColorRange.Full="全部"
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue