Files
mobile-ffmpeg/ios/test-app/MobileFFmpegTest/AudioViewController.m
T

308 lines
12 KiB
Objective-C

//
// AudioViewController.m
//
// Copyright (c) 2018 Taner Sener
//
// This file is part of MobileFFmpeg.
//
// MobileFFmpeg is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// MobileFFmpeg 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
//
#import <mobileffmpeg/MobileFFmpegConfig.h>
#import <mobileffmpeg/MobileFFmpeg.h>
#import "AudioViewController.h"
#import "RCEasyTipView.h"
@interface AudioViewController ()
@property (strong, nonatomic) IBOutlet UILabel *header;
@property (strong, nonatomic) IBOutlet UIPickerView *audioCodecPicker;
@property (strong, nonatomic) IBOutlet UIButton *encodeButton;
@property (strong, nonatomic) IBOutlet UITextView *outputText;
@end
@implementation AudioViewController {
// Video codec data
NSArray *codecData;
NSInteger selectedCodec;
// Loading view
UIActivityIndicatorView* indicator;
// Tooltip view reference
RCEasyTipView *tooltip;
}
- (void)viewDidLoad {
[super viewDidLoad];
// AUDIO CODEC PICKER INIT
codecData = @[@"aac (audiotoolbox)", @"mp2 (twolame)", @"mp3 (liblame)", @"mp3 (libshine)", @"vorbis", @"opus", @"amr", @"ilbc", @"soxr", @"speex", @"wavpack"];
selectedCodec = 0;
self.audioCodecPicker.dataSource = self;
self.audioCodecPicker.delegate = self;
// STYLE UPDATE
[Util applyPickerViewStyle: self.audioCodecPicker];
[Util applyButtonStyle: self.encodeButton];
[Util applyOutputTextStyle: self.outputText];
[Util applyHeaderStyle: self.header];
// TOOLTIP INIT
RCEasyTipPreferences *preferences = [[RCEasyTipPreferences alloc] initWithDefaultPreferences];
[Util applyTooltipStyle: preferences];
preferences.drawing.arrowPostion = Top;
preferences.animating.showDuration = 1.0;
preferences.animating.dismissDuration = AUDIO_TEST_TOOLTIP_DURATION;
preferences.animating.dismissTransform = CGAffineTransformMakeTranslation(0, -15);
preferences.animating.showInitialTransform = CGAffineTransformMakeTranslation(0, -15);
tooltip = [[RCEasyTipView alloc] initWithPreferences:preferences];
tooltip.text = AUDIO_TEST_TOOLTIP_TEXT;
// BUTTON DISABLED UNTIL AUDIO SAMPLE IS CREATED
[self.encodeButton setEnabled:false];
[self createAudioSample];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
/**
* The number of columns of data
*/
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
/**
* The number of rows of data
*/
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return codecData.count;
}
/**
* The data to return for the row and component (column) that's being passed in
*/
- (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return codecData[row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
selectedCodec = row;
}
- (void)logCallback: (int)level :(NSString*)message {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendOutput: message];
});
}
- (IBAction)encodeClicked:(id)sender {
NSString *audioOutputFile = [self getAudioOutputFilePath];
[[NSFileManager defaultManager] removeItemAtPath:audioOutputFile error:NULL];
NSString *audioCodec = codecData[selectedCodec];
NSLog(@"Testing AUDIO encoding with \'%@\' codec\n", audioCodec);
NSString *ffmpegCommand = [self generateAudioEncodeScript];
[self loadProgressDialog:@"Encoding audio\n\n"];
[self clearOutput];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"FFmpeg process started with arguments\n\'%@\'\n", ffmpegCommand);
// EXECUTE
int result = [MobileFFmpeg execute: ffmpegCommand];
NSLog(@"FFmpeg process exited with rc %d\n", result);
dispatch_async(dispatch_get_main_queue(), ^{
if (result == RETURN_CODE_SUCCESS) {
NSLog(@"Encode completed successfully.\n");
[self dismissProgressDialogAndAlert:@"Success" and:@"Encode completed successfully."];
} else {
NSLog(@"Encode failed with rc=%d\n", result);
[self dismissProgressDialogAndAlert:@"Error" and:@"Encode failed. Please check log for the details."];
}
});
});
}
- (void)createAudioSample {
[MobileFFmpegConfig setLogDelegate:nil];
NSLog(@"Creating AUDIO sample before the test.\n");
NSString *audioSampleFile = [self getAudioSamplePath];
[[NSFileManager defaultManager] removeItemAtPath:audioSampleFile error:NULL];
NSString *ffmpegCommand = [NSString stringWithFormat:@"-y -f lavfi -i sine=frequency=1000:duration=5 -c:a pcm_s16le %@", audioSampleFile];
NSLog(@"Sample file is created with \'%@\'\n", ffmpegCommand);
int result = [MobileFFmpeg execute: ffmpegCommand];
if (result == RETURN_CODE_SUCCESS) {
[self.encodeButton setEnabled:true];
dispatch_async(dispatch_get_main_queue(), ^{
[self setActive];
});
NSLog(@"AUDIO sample created\n");
} else {
NSLog(@"Creating AUDIO sample failed with rc=%d\n", result);
[Util alert:self withTitle:@"Error" message:@"Creating AUDIO sample failed. Please check log for the details." andButtonText:@"OK"];
}
}
- (NSString*)getAudioOutputFilePath {
NSString *audioCodec = codecData[selectedCodec];
NSString *extension;
if ([audioCodec isEqualToString:@"aac (audiotoolbox)"]) {
extension = @"m4a";
} else if ([audioCodec isEqualToString:@"mp2 (twolame)"]) {
extension = @"mpg";
} else if ([audioCodec isEqualToString:@"mp3 (liblame)"] || [audioCodec isEqualToString:@"mp3 (libshine)"]) {
extension = @"mp3";
} else if ([audioCodec isEqualToString:@"vorbis"]) {
extension = @"ogg";
} else if ([audioCodec isEqualToString:@"opus"]) {
extension = @"opus";
} else if ([audioCodec isEqualToString:@"amr"]) {
extension = @"amr";
} else if ([audioCodec isEqualToString:@"ilbc"]) {
extension = @"lbc";
} else if ([audioCodec isEqualToString:@"speex"]) {
extension = @"spx";
} else if ([audioCodec isEqualToString:@"wavpack"]) {
extension = @"wv";
} else {
// soxr
extension = @"wav";
}
NSString* docFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return [[docFolder stringByAppendingPathComponent: @"audio."] stringByAppendingString: extension];
}
- (NSString*)getAudioSamplePath {
NSString* docFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return [docFolder stringByAppendingPathComponent: @"audio-sample.wav"];
}
- (void)loadProgressDialog:(NSString*) dialogMessage {
UIAlertController *pending = [UIAlertController alertControllerWithTitle:nil
message:dialogMessage
preferredStyle:UIAlertControllerStyleAlert];
indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.color = [UIColor blackColor];
indicator.translatesAutoresizingMaskIntoConstraints=NO;
[pending.view addSubview:indicator];
NSDictionary * views = @{@"pending" : pending.view, @"indicator" : indicator};
NSArray * constraintsVertical = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[indicator]-(20)-|" options:0 metrics:nil views:views];
NSArray * constraintsHorizontal = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[indicator]|" options:0 metrics:nil views:views];
NSArray * constraints = [constraintsVertical arrayByAddingObjectsFromArray:constraintsHorizontal];
[pending.view addConstraints:constraints];
[indicator startAnimating];
[self presentViewController:pending animated:YES completion:nil];
}
- (void)dismissProgressDialog {
[indicator stopAnimating];
[self dismissViewControllerAnimated:TRUE completion:nil];
}
- (void)dismissProgressDialogAndAlert: (NSString*)title and:(NSString*)message {
[indicator stopAnimating];
[self dismissViewControllerAnimated:TRUE completion:^{
[Util alert:self withTitle:title message:message andButtonText:@"OK"];
}];
}
- (void)setActive {
[MobileFFmpegConfig setLogDelegate:self];
[self hideTooltip];
[self showTooltip];
}
- (void)hideTooltip {
[tooltip dismissWithCompletion:nil];
}
- (void)showTooltip {
[tooltip showAnimated:YES forView:self.encodeButton withinSuperView:self.view];
}
- (void)clearOutput {
[[self outputText] setText:@""];
}
- (void)appendOutput:(NSString*) message {
self.outputText.text = [self.outputText.text stringByAppendingString:message];
if (self.outputText.text.length > 0 ) {
NSRange bottom = NSMakeRange(self.outputText.text.length - 1, 1);
[self.outputText scrollRangeToVisible:bottom];
}
}
- (NSString*)generateAudioEncodeScript {
NSString *audioCodec = codecData[selectedCodec];
NSString *audioSampleFile = [self getAudioSamplePath];
NSString *audioOutputFile = [self getAudioOutputFilePath];
if ([audioCodec isEqualToString:@"aac (audiotoolbox)"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a aac_at -b:a 192k %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"mp2 (twolame)"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a mp2 -b:a 192k %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"mp3 (liblame)"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a libmp3lame -qscale:a 2 %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"mp3 (libshine)"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a libshine -qscale:a 2 %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"vorbis"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a libvorbis -b:a 64k %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"opus"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a libopus -b:a 64k -vbr on -compression_level 10 %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"amr"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -ar 8000 -ab 12.2k %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"ilbc"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a ilbc -ar 8000 -b:a 15200 %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"speex"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a libspeex -ar 16000 %@", audioSampleFile, audioOutputFile];
} else if ([audioCodec isEqualToString:@"wavpack"]) {
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -c:a wavpack -b:a 64k %@", audioSampleFile, audioOutputFile];
} else {
// soxr
return [NSString stringWithFormat:@"-hide_banner -y -i %@ -af aresample=resampler=soxr -ar 44100 %@", audioSampleFile, audioOutputFile];
}
}
@end