Skip to content

Commit

Permalink
WIP - AVF record
Browse files Browse the repository at this point in the history
  • Loading branch information
warmenhoven committed Dec 11, 2023
1 parent 70f35b6 commit 4cbe99f
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 0 deletions.
4 changes: 4 additions & 0 deletions griffin/griffin_objc.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
#include "../input/drivers_joypad/mfi_joypad.m"
#endif

#ifdef HAVE_AVF
#include "../record/drivers/record_avf.m"
#endif

#ifdef HAVE_COREAUDIO3
#include "../audio/drivers/coreaudio3.m"
#endif
Expand Down
193 changes: 193 additions & 0 deletions record/drivers/record_avf.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/* RetroArch - A frontend for libretro.
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/

#import <AVFoundation/AVFoundation.h>

#import "../record_driver.h"

@interface AVFRecorder : NSObject

// Declare properties
@property (nonatomic, strong) AVAssetWriter *assetWriter;
@property (nonatomic, strong) AVAssetWriterInput *videoInput;
@property (nonatomic, strong) AVAssetWriterInput *audioInput;
@property (nonatomic, strong) AVAssetWriterInputPixelBufferAdaptor *videoPixelBufferAdaptor;
@property (nonatomic, strong) NSDictionary *audioSettings;

@end

@implementation AVFRecorder

- (void)setupAssetWriter {
NSError *error = nil;
// Create a URL for the output file
NSURL *outputFileURL = [NSURL fileURLWithPath:@"output.mov"];

// Create an AVAssetWriter with the desired output URL and file type
self.assetWriter = [AVAssetWriter assetWriterWithURL:outputFileURL fileType:AVFileTypeQuickTimeMovie error:&error];

// Configure video settings
NSDictionary *videoSettings = @{
AVVideoCodecKey: AVVideoCodecTypeH264,
AVVideoWidthKey: @(256),
AVVideoHeightKey: @(256)
};
self.videoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];

// Create a pixel buffer adaptor for video input
NSDictionary *sourcePixelBufferAttributes = @{
(NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32ARGB)
};
self.videoPixelBufferAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:self.videoInput sourcePixelBufferAttributes:sourcePixelBufferAttributes];

if ([self.assetWriter canAddInput:self.videoInput]) {
[self.assetWriter addInput:self.videoInput];
}

// Configure audio settings
self.audioSettings = @{
AVFormatIDKey: @(kAudioFormatLinearPCM),
AVSampleRateKey: @(2),
AVNumberOfChannelsKey: @(2),
AVLinearPCMBitDepthKey: @(16), // Example: 16-bit audio
AVLinearPCMIsBigEndianKey: @(NO),
AVLinearPCMIsFloatKey: @(NO),
};
self.audioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:self.audioSettings];

if ([self.assetWriter canAddInput:self.audioInput]) {
[self.assetWriter addInput:self.audioInput];
}
}

- (void)writePixelBuffer:(CVPixelBufferRef)pixelBuffer presentationTime:(CMTime)presentationTime {
if (!self.videoPixelBufferAdaptor.assetWriterInput.readyForMoreMediaData) {
return;
}

if (self.assetWriter.status == AVAssetWriterStatusUnknown) {
[self.assetWriter startWriting];
[self.assetWriter startSessionAtSourceTime:presentationTime];
}

[self.videoPixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:presentationTime];
}

- (void)writePCMData:(AudioBuffer)audioBuffer presentationTimeStamp:(CMTime)presentationTimeStamp {
if (!self.audioInput.isReadyForMoreMediaData) {
return;
}

if (self.assetWriter.status == AVAssetWriterStatusUnknown) {
[self.assetWriter startWriting];
[self.assetWriter startSessionAtSourceTime:presentationTimeStamp];
}

// CMItemCount frameCount = audioBuffer.mDataByteSize / [self.audioSettings[AVLinearPCMBitDepthKey] intValue];
CMSampleBufferRef audioBufferCopy = NULL;
CMSampleTimingInfo timing = {kCMTimeInvalid, presentationTimeStamp, kCMTimeInvalid};

OSStatus status = CMSampleBufferCreate(kCFAllocatorDefault,
NULL,
false,
NULL,
NULL,
NULL,
1,
1,
&timing,
0,
NULL,
&audioBufferCopy);

if (status == noErr) {
AudioBufferList audioBufferList;
audioBufferList.mNumberBuffers = 1;
audioBufferList.mBuffers[0] = audioBuffer;

status = CMSampleBufferSetDataBufferFromAudioBufferList(audioBufferCopy,
kCFAllocatorDefault,
kCFAllocatorDefault,
0,
&audioBufferList);
}

if (status == noErr) {
[self.audioInput appendSampleBuffer:audioBufferCopy];
CFRelease(audioBufferCopy);
}
}

- (void)endRecordingWithCompletion:(void (^)(void))completion {
[self.videoInput markAsFinished];
[self.audioInput markAsFinished];

[self.assetWriter finishWritingWithCompletionHandler:completion];}


@end

static void *avf_record_new(const struct record_params *params)
{
AVFRecorder *recorder = [[AVFRecorder alloc] init];
[recorder setupAssetWriter];
return (__bridge_retained void *)recorder;
}

static void avf_record_free(void *data)
{
__block AVFRecorder *recorder = (__bridge_transfer AVFRecorder *)data;
if (recorder == nil)
return;

[recorder endRecordingWithCompletion:^{
recorder = nil;
}];
}

static bool avf_record_push_video(void *data, const struct record_video_data *video_data)
{
AVFRecorder *recorder = (__bridge AVFRecorder *)data;
if (recorder == nil)
return false;

return true;
}

static bool avf_record_push_audio(void *data, const struct record_audio_data *audio_data)
{
AVFRecorder *recorder = (__bridge AVFRecorder *)data;
if (recorder == nil)
return false;

return true;
}

static bool avf_record_finalize(void *data)
{
AVFRecorder *recorder = (__bridge AVFRecorder *)data;
if (recorder == nil)
return false;

return true;
}

const record_driver_t record_avf = {
avf_record_new,
avf_record_free,
avf_record_push_video,
avf_record_push_audio,
avf_record_finalize,
"avfoundation",
};
3 changes: 3 additions & 0 deletions record/record_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ static const record_driver_t record_null = {
};

const record_driver_t *record_drivers[] = {
#ifdef HAVE_AVF
&record_avf,
#endif
#ifdef HAVE_FFMPEG
&record_ffmpeg,
#endif
Expand Down
1 change: 1 addition & 0 deletions record/record_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ struct recording
typedef struct recording recording_state_t;

extern const record_driver_t record_ffmpeg;
extern const record_driver_t record_avf;

/**
* config_get_record_driver_options:
Expand Down

0 comments on commit 4cbe99f

Please sign in to comment.