Syphon source now grabs the IOSurface before receiving a frame notification, so restored Syphon Sources from 'fast' SyphonInjected processes work

This commit is contained in:
Zakk 2015-02-15 08:37:15 -05:00
parent a82c18d092
commit 96455ca5b0
14 changed files with 43 additions and 238 deletions

View file

@ -50,9 +50,6 @@
-(bool) startCaptureSession:(NSError **)error;
-(bool) stopCaptureSession;
-(id) initForProxy;
-(void)captureVideoOutput:(CMSampleBufferRef)sampleBuffer;
-(void)captureAudioOutput:(CMSampleBufferRef)sampleBuffer;

View file

@ -44,7 +44,6 @@
-(void)copyAudioBufferList:(AudioBufferList *)bufferList;
-(void) copyIntoFormat:(const AudioStreamBasicDescription *)newFormat;
-(void) nextMovie;
-(void)removeQueueItems:(NSIndexSet *)movieIndexes;
-(void)preallocateAudioBuffers:(CMItemCount)frameCount audioFormat:(const AudioStreamBasicDescription *)audioFormat;

View file

@ -132,6 +132,23 @@
return _isFlipped;
}
-(void)publishSurface:(IOSurfaceRef)surface
{
uint32_t newSeed = IOSurfaceGetSeed(surface);
if (newSeed != _surfaceSeed)
{
CIImage *newImage = [[CIImage alloc] initWithIOSurface:surface plane:0 format:kCIFormatARGB8 options:nil];
_surfaceSeed = newSeed;
[self updateLayersWithBlock:^(CALayer *layer) {
((CSIOSurfaceLayer *)layer).ioImage = newImage;
}];
}
}
-(void) startSyphon
{
@ -148,30 +165,22 @@
if (_syphonServer)
{
_syphon_client = [[SyphonClient alloc] initWithServerDescription:_syphonServer.copy options:nil newFrameHandler:^(SyphonClient *client) {
//this call retains the surface, so be sure to release it if we don't care about it anymore
IOSurfaceRef newSurface = [client IOSurface];
uint32_t newSeed = IOSurfaceGetSeed(newSurface);
if (newSeed != _surfaceSeed)
{
CIImage *newImage = [[CIImage alloc] initWithIOSurface:newSurface plane:0 format:kCIFormatARGB8 options:nil];
_surfaceSeed = newSeed;
[self updateLayersWithBlock:^(CALayer *layer) {
((CSIOSurfaceLayer *)layer).ioImage = newImage;
}];
CFRelease(newSurface);
} else {
CFRelease(newSurface);
}
}];
_syphon_client = [[SyphonClient alloc] initWithServerDescription:_syphonServer.copy options:nil newFrameHandler:^(SyphonClient *client) {
//this call retains the surface, so be sure to release it if we don't care about it anymore
IOSurfaceRef newSurface = [client IOSurface];
[self publishSurface:newSurface];
CFRelease(newSurface);
}];
@synchronized(self)
IOSurfaceRef newSurface = [_syphon_client IOSurface];
if (newSurface)
{
_serverSurface = [_syphon_client IOSurface];
[self publishSurface:newSurface];
CFRelease(newSurface);
}

View file

@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
34FDA82D1A84BFF400E7F65E /* CSTimeCaptureFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FDA82C1A84BFF400E7F65E /* CSTimeCaptureFactory.m */; };
34FDA8301A84C08A00E7F65E /* CSTimeCaptureBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FDA82F1A84C08A00E7F65E /* CSTimeCaptureBase.m */; };
34FDA8331A84C15800E7F65E /* CSCurrentTimeCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FDA8321A84C15800E7F65E /* CSCurrentTimeCapture.m */; };
34FDA83C1A84C34A00E7F65E /* CSCurrentTimeCaptureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FDA83A1A84C34A00E7F65E /* CSCurrentTimeCaptureViewController.m */; };
34FDA83D1A84C34A00E7F65E /* CSCurrentTimeCaptureViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34FDA83B1A84C34A00E7F65E /* CSCurrentTimeCaptureViewController.xib */; };
@ -43,8 +42,6 @@
34FDA8291A84BFA800E7F65E /* CSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNotifications.h; path = PluginHeaders/CSNotifications.h; sourceTree = "<group>"; };
34FDA82B1A84BFF400E7F65E /* CSTimeCaptureFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSTimeCaptureFactory.h; sourceTree = "<group>"; };
34FDA82C1A84BFF400E7F65E /* CSTimeCaptureFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSTimeCaptureFactory.m; sourceTree = "<group>"; };
34FDA82E1A84C08A00E7F65E /* CSTimeCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSTimeCaptureBase.h; sourceTree = "<group>"; };
34FDA82F1A84C08A00E7F65E /* CSTimeCaptureBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSTimeCaptureBase.m; sourceTree = "<group>"; };
34FDA8311A84C15800E7F65E /* CSCurrentTimeCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCurrentTimeCapture.h; path = ../CSCurrentTimeCapture.h; sourceTree = "<group>"; };
34FDA8321A84C15800E7F65E /* CSCurrentTimeCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSCurrentTimeCapture.m; path = ../CSCurrentTimeCapture.m; sourceTree = "<group>"; };
34FDA8391A84C34A00E7F65E /* CSCurrentTimeCaptureViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCurrentTimeCaptureViewController.h; path = ../CSCurrentTimeCaptureViewController.h; sourceTree = "<group>"; };
@ -113,8 +110,6 @@
34FDA82B1A84BFF400E7F65E /* CSTimeCaptureFactory.h */,
34FDA82C1A84BFF400E7F65E /* CSTimeCaptureFactory.m */,
34FDA8001A84BF9300E7F65E /* Supporting Files */,
34FDA82E1A84C08A00E7F65E /* CSTimeCaptureBase.h */,
34FDA82F1A84C08A00E7F65E /* CSTimeCaptureBase.m */,
);
path = CSTimeCapturePlugin;
sourceTree = "<group>";
@ -266,7 +261,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
34FDA8301A84C08A00E7F65E /* CSTimeCaptureBase.m in Sources */,
34FDA8521A88360400E7F65E /* CSCountdownTimeCapture.m in Sources */,
34FDA82D1A84BFF400E7F65E /* CSTimeCaptureFactory.m in Sources */,
34FDA83C1A84C34A00E7F65E /* CSCurrentTimeCaptureViewController.m in Sources */,
@ -411,6 +405,7 @@
34FDA8061A84BF9300E7F65E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};

View file

@ -1,22 +0,0 @@
//
// CSTimeCaptureBase.h
// CSTimeCapturePlugin
//
// Created by Zakk on 2/6/15.
// Copyright (c) 2015 Zakk. All rights reserved.
//
#import "CSTextCaptureBase.h"
@interface CSTimeCaptureBase : CSTextCaptureBase
@property (strong) NSDateFormatter *formatter;
@property (strong) NSDate *startDate;
@property (strong) NSDate *endDate;
@property (strong) NSString *format;
@property (assign) NSDateFormatterStyle timeStyle;
@property (assign) NSDateFormatterStyle dateStyle;
@property (strong) NSDictionary *styleTypeMap;
@end

View file

@ -1,64 +0,0 @@
//
// CSTimeCaptureBase.m
// CSTimeCapturePlugin
//
// Created by Zakk on 2/6/15.
// Copyright (c) 2015 Zakk. All rights reserved.
//
#import "CSTimeCaptureBase.h"
@implementation CSTimeCaptureBase
-(instancetype)init
{
if (self = [super init])
{
self.styleTypeMap = @{@"None": @(NSDateFormatterNoStyle),
@"Short": @(NSDateFormatterShortStyle),
@"Medium": @(NSDateFormatterMediumStyle),
@"Long": @(NSDateFormatterLongStyle),
@"Full": @(NSDateFormatterFullStyle)
};
self.formatter = [[NSDateFormatter alloc] init];
self.formatter.dateStyle = NSDateFormatterMediumStyle;
self.formatter.timeStyle = NSDateFormatterMediumStyle;
//self.formatter.dateFormat = @"mm:ss.SS";
}
return self;
}
-(NSArray *)styleTypes
{
return _styleTypeMap.allKeys;
}
-(void)frameTick
{
NSDate *displayDate;
NSCalendar *cal = [NSCalendar currentCalendar];
if (self.startDate)
{
NSTimeInterval interval = -[self.startDate timeIntervalSinceNow];
} else if (self.endDate) {
NSTimeInterval interval = [self.endDate timeIntervalSinceNow];
} else {
displayDate = [NSDate date];
}
self.text = [_formatter stringFromDate:displayDate];
}
@end

View file

@ -59,11 +59,9 @@ static const uint HOURS_PER_DAY = 24;
- (NSString*) stringFromInterval:(NSTimeInterval) interval
{
uint wholeSeconds = (uint) interval;
uint milliseconds = (uint) (fmod(interval, 1.0) * 1000);
char msg[512] = {0};
NSString *message = timeFormat.mutableCopy;
NSMutableString *message = timeFormat.mutableCopy;
/*
timeFormat = [dayRegex
stringByReplacingMatchesInString:timeFormat
@ -129,12 +127,12 @@ static const uint HOURS_PER_DAY = 24;
{
NSTextCheckingResult *res;
while (res = [milliSecondRegex firstMatchInString:message options:0 range:workingRange])
while ((res = [milliSecondRegex firstMatchInString:message options:0 range:workingRange]))
{
uint length = res.range.length;
unsigned long length = res.range.length;
uint milliseconds = (uint) (fmod(interval, 1.0) * pow(10, length));
[message replaceCharactersInRange:res.range withString:[NSString stringWithFormat:@"%0*d", length, milliseconds]];
[message replaceCharactersInRange:res.range withString:[NSString stringWithFormat:@"%0*d", (int)length, milliseconds]];
workingRange = NSMakeRange(0, message.length);
}
}
@ -144,10 +142,10 @@ static const uint HOURS_PER_DAY = 24;
{
NSTextCheckingResult *res;
while (res = [regex firstMatchInString:message options:0 range:workingRange])
while ((res = [regex firstMatchInString:message options:0 range:workingRange]))
{
uint length = res.range.length;
[message replaceCharactersInRange:res.range withString:[NSString stringWithFormat:@"%0*d", length, value]];
unsigned long length = res.range.length;
[message replaceCharactersInRange:res.range withString:[NSString stringWithFormat:@"%0*d", (int)length, value]];
workingRange = NSMakeRange(0, message.length);
}
}

View file

@ -80,12 +80,8 @@
345F8B1E1A12AC31009A81E3 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 345F8B1C1A12AC2C009A81E3 /* AudioUnit.framework */; };
345F8B251A14E6A0009A81E3 /* CAMultiAudioNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B241A14E6A0009A81E3 /* CAMultiAudioNode.m */; };
345F8B261A14E6A0009A81E3 /* CAMultiAudioNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B241A14E6A0009A81E3 /* CAMultiAudioNode.m */; };
345F8B291A14EEE2009A81E3 /* CAMultiAudioDeviceInput.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B281A14EEE2009A81E3 /* CAMultiAudioDeviceInput.m */; };
345F8B2A1A14EEE2009A81E3 /* CAMultiAudioDeviceInput.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B281A14EEE2009A81E3 /* CAMultiAudioDeviceInput.m */; };
345F8B2D1A156E15009A81E3 /* CAMultiAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B2C1A156E15009A81E3 /* CAMultiAudioDevice.m */; };
345F8B2E1A156E15009A81E3 /* CAMultiAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B2C1A156E15009A81E3 /* CAMultiAudioDevice.m */; };
345F8B311A15716A009A81E3 /* CAMultiAudioDeviceOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B301A15716A009A81E3 /* CAMultiAudioDeviceOutput.m */; };
345F8B321A15716A009A81E3 /* CAMultiAudioDeviceOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B301A15716A009A81E3 /* CAMultiAudioDeviceOutput.m */; };
345F8B351A157433009A81E3 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 345F8B341A157433009A81E3 /* CoreAudio.framework */; };
345F8B361A157444009A81E3 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 345F8B341A157433009A81E3 /* CoreAudio.framework */; };
345F8B391A15D850009A81E3 /* CAMultiAudioDefaultOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B381A15D850009A81E3 /* CAMultiAudioDefaultOutput.m */; };
@ -474,12 +470,8 @@
345F8B1F1A14DAB3009A81E3 /* CAMultiAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudio.h; path = CAMultiAudio/CAMultiAudio.h; sourceTree = "<group>"; };
345F8B231A14E6A0009A81E3 /* CAMultiAudioNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioNode.h; path = CAMultiAudio/CAMultiAudioNode.h; sourceTree = "<group>"; };
345F8B241A14E6A0009A81E3 /* CAMultiAudioNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CAMultiAudioNode.m; path = CAMultiAudio/CAMultiAudioNode.m; sourceTree = "<group>"; };
345F8B271A14EEE2009A81E3 /* CAMultiAudioDeviceInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioDeviceInput.h; path = CAMultiAudio/CAMultiAudioDeviceInput.h; sourceTree = "<group>"; };
345F8B281A14EEE2009A81E3 /* CAMultiAudioDeviceInput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CAMultiAudioDeviceInput.m; path = CAMultiAudio/CAMultiAudioDeviceInput.m; sourceTree = "<group>"; };
345F8B2B1A156E15009A81E3 /* CAMultiAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioDevice.h; path = CAMultiAudio/CAMultiAudioDevice.h; sourceTree = "<group>"; };
345F8B2C1A156E15009A81E3 /* CAMultiAudioDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CAMultiAudioDevice.m; path = CAMultiAudio/CAMultiAudioDevice.m; sourceTree = "<group>"; };
345F8B2F1A15716A009A81E3 /* CAMultiAudioDeviceOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioDeviceOutput.h; path = CAMultiAudio/CAMultiAudioDeviceOutput.h; sourceTree = "<group>"; };
345F8B301A15716A009A81E3 /* CAMultiAudioDeviceOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CAMultiAudioDeviceOutput.m; path = CAMultiAudio/CAMultiAudioDeviceOutput.m; sourceTree = "<group>"; };
345F8B341A157433009A81E3 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
345F8B371A15D850009A81E3 /* CAMultiAudioDefaultOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioDefaultOutput.h; path = CAMultiAudio/CAMultiAudioDefaultOutput.h; sourceTree = "<group>"; };
345F8B381A15D850009A81E3 /* CAMultiAudioDefaultOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CAMultiAudioDefaultOutput.m; path = CAMultiAudio/CAMultiAudioDefaultOutput.m; sourceTree = "<group>"; };
@ -923,10 +915,6 @@
345F8B781A183D1D009A81E3 /* CAMultiAudioEngine.m */,
345F8B731A17A785009A81E3 /* CAMultiAudioMixer.h */,
345F8B741A17A785009A81E3 /* CAMultiAudioMixer.m */,
345F8B271A14EEE2009A81E3 /* CAMultiAudioDeviceInput.h */,
345F8B281A14EEE2009A81E3 /* CAMultiAudioDeviceInput.m */,
345F8B2F1A15716A009A81E3 /* CAMultiAudioDeviceOutput.h */,
345F8B301A15716A009A81E3 /* CAMultiAudioDeviceOutput.m */,
345F8B3C1A15F3BD009A81E3 /* CAMultiAudioPCM.m */,
345F8B5F1A161552009A81E3 /* CAMultiAudioAVCapturePlayer.h */,
345F8B601A161552009A81E3 /* CAMultiAudioAVCapturePlayer.m */,
@ -1526,7 +1514,6 @@
346CF44C1A5C10C2008E5BFF /* CSInputLayer.m in Sources */,
345F8B711A177C1F009A81E3 /* CAMultiAudioConverter.m in Sources */,
34C19053189EE60400AB2430 /* CapturedFrameData.m in Sources */,
345F8B311A15716A009A81E3 /* CAMultiAudioDeviceOutput.m in Sources */,
345F8B6D1A176A5D009A81E3 /* CAMultiAudioUnit.m in Sources */,
34D2D5731A547628001004E5 /* CSTextCaptureBase.m in Sources */,
340FE4B515F3417E00E4CE4E /* AppDelegate.m in Sources */,
@ -1551,7 +1538,6 @@
347B14DC198492D900DC7DF0 /* InputPopupControllerViewController.m in Sources */,
34D6581819B387DE0012E32B /* AppDelegate+AppDelegate_ScriptingAdditions.m in Sources */,
340DC734197CF6E9003A0BB3 /* CSCaptureBase.m in Sources */,
345F8B291A14EEE2009A81E3 /* CAMultiAudioDeviceInput.m in Sources */,
34A64A2F165EFE4C00A68428 /* PreviewView.m in Sources */,
345F8B691A16C348009A81E3 /* CAMultiAudioGraph.m in Sources */,
3431FFE119786502000965FE /* InputSource.m in Sources */,
@ -1586,7 +1572,6 @@
34AFC33419B19CD00007C07B /* SourceCache.m in Sources */,
34B5FCE019BF1C3F00F67D19 /* CreateLayoutViewController.m in Sources */,
345F8B7A1A183D1D009A81E3 /* CAMultiAudioEngine.m in Sources */,
345F8B2A1A14EEE2009A81E3 /* CAMultiAudioDeviceInput.m in Sources */,
3451A1D31712C41000DF6A8B /* CaptureController.m in Sources */,
34FDD6F31A215268009A7413 /* CSPluginServices.m in Sources */,
3431FFE219786502000965FE /* InputSource.m in Sources */,
@ -1594,7 +1579,6 @@
3451A1D81712C5C400DF6A8B /* x264Compressor.m in Sources */,
345F8B3E1A15F3BD009A81E3 /* CAMultiAudioPCM.m in Sources */,
34CFE4A518F1992A00092C6A /* AVFAudioChannel.m in Sources */,
345F8B321A15716A009A81E3 /* CAMultiAudioDeviceOutput.m in Sources */,
3451A1D91712C5C700DF6A8B /* AppleVTCompressor.m in Sources */,
345F8B061A1060D6009A81E3 /* CSAacEncoder.m in Sources */,
34576C7919AFCA1B007BAD90 /* CSPluginLoader.m in Sources */,

View file

@ -8,12 +8,12 @@
#import "CAMultiAudioGraph.h"
#import "CAMultiAudioNode.h"
#import "CAMultiAudioDeviceInput.h"
#import "CAMultiAudioDeviceOutput.h"
#import "CAMultiAudioDefaultOutput.h"
#import "CAMultiAudioPCMPlayer.h"
#import "CAMultiAudioAVCapturePlayer.h"
#import "CAMultiAudioMixer.h"
#import "CAMultiAudioDevice.h"

View file

@ -1,23 +0,0 @@
//
// CAMultiAudioDeviceInput.h
// CocoaSplit
//
// Created by Zakk on 11/13/14.
// Copyright (c) 2014 Zakk. All rights reserved.
//
#import "CAMultiAudioDevice.h"
@interface CAMultiAudioDeviceInput : CAMultiAudioDevice
{
}
-(instancetype)initWithDeviceID:(NSString *)uid;
@end

View file

@ -1,30 +0,0 @@
//
// CAMultiAudioDeviceInput.m
// CocoaSplit
//
// Created by Zakk on 11/13/14.
// Copyright (c) 2014 Zakk. All rights reserved.
//
#import "CAMultiAudioDeviceInput.h"
@implementation CAMultiAudioDeviceInput
-(instancetype)initWithDeviceID:(NSString *)uid
{
if (self = [super initWithDeviceID:uid])
{
[self setInputForDevice];
}
return self;
}
@end

View file

@ -1,15 +0,0 @@
//
// CAMultiAudioDeviceOutput.h
// CocoaSplit
//
// Created by Zakk on 11/13/14.
// Copyright (c) 2014 Zakk. All rights reserved.
//
#import "CAMultiAudioDevice.h"
@interface CAMultiAudioDeviceOutput : CAMultiAudioDevice
-(instancetype)initWithDeviceID:(NSString *)uid;
@end

View file

@ -1,24 +0,0 @@
//
// CAMultiAudioDeviceOutput.m
// CocoaSplit
//
// Created by Zakk on 11/13/14.
// Copyright (c) 2014 Zakk. All rights reserved.
//
#import "CAMultiAudioDeviceOutput.h"
@implementation CAMultiAudioDeviceOutput
-(instancetype)initWithDeviceID:(NSString *)uid
{
if (self = [super initWithDeviceID:uid])
{
[self setOutputForDevice];
}
return self;
}
@end

View file

@ -10,7 +10,8 @@
#import "CSTextCaptureBase.h"
@interface CSTextCaptureViewControllerBase : NSViewController
@interface CSTextCaptureViewControllerBase : NSViewController <NSWindowDelegate>
@property (weak) CSTextCaptureBase *captureObj;
- (IBAction)openFontPanel:(id)sender;