Repository: molon/MLAudioRecorder Branch: master Commit: e065adef0adc Files: 101 Total size: 642.7 KB Directory structure: gitextract_pi2h2uel/ ├── MLRecorder/ │ ├── AFNetwoking/ │ │ ├── AFNetworking/ │ │ │ ├── AFNetworking.h │ │ │ ├── NSURLConnection/ │ │ │ │ ├── AFHTTPRequestOperation.h │ │ │ │ ├── AFHTTPRequestOperation.m │ │ │ │ ├── AFHTTPRequestOperationManager.h │ │ │ │ ├── AFHTTPRequestOperationManager.m │ │ │ │ ├── AFURLConnectionOperation.h │ │ │ │ └── AFURLConnectionOperation.m │ │ │ ├── NSURLSession/ │ │ │ │ ├── AFHTTPSessionManager.h │ │ │ │ ├── AFHTTPSessionManager.m │ │ │ │ ├── AFURLSessionManager.h │ │ │ │ └── AFURLSessionManager.m │ │ │ ├── Reachability/ │ │ │ │ ├── AFNetworkReachabilityManager.h │ │ │ │ └── AFNetworkReachabilityManager.m │ │ │ ├── Security/ │ │ │ │ ├── AFSecurityPolicy.h │ │ │ │ └── AFSecurityPolicy.m │ │ │ └── Serialization/ │ │ │ ├── AFURLRequestSerialization.h │ │ │ ├── AFURLRequestSerialization.m │ │ │ ├── AFURLResponseSerialization.h │ │ │ └── AFURLResponseSerialization.m │ │ └── UIKit+AFNetworking/ │ │ ├── AFNetworkActivityIndicatorManager.h │ │ ├── AFNetworkActivityIndicatorManager.m │ │ ├── UIActivityIndicatorView+AFNetworking.h │ │ ├── UIActivityIndicatorView+AFNetworking.m │ │ ├── UIAlertView+AFNetworking.h │ │ ├── UIAlertView+AFNetworking.m │ │ ├── UIButton+AFNetworking.h │ │ ├── UIButton+AFNetworking.m │ │ ├── UIImageView+AFNetworking.h │ │ ├── UIImageView+AFNetworking.m │ │ ├── UIKit+AFNetworking.h │ │ ├── UIProgressView+AFNetworking.h │ │ ├── UIProgressView+AFNetworking.m │ │ ├── UIRefreshControl+AFNetworking.h │ │ ├── UIRefreshControl+AFNetworking.m │ │ ├── UIWebView+AFNetworking.h │ │ └── UIWebView+AFNetworking.m │ ├── AmrRecordInBufferWriter.h │ ├── AmrRecordInBufferWriter.m │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj/ │ │ └── Main.storyboard │ ├── CafRecordInBufferWriter.h │ ├── CafRecordInBufferWriter.m │ ├── Debug.h │ ├── Images.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage/ │ │ └── Contents.json │ ├── MLAudioPlayer/ │ │ ├── AmrPlayerReader.h │ │ ├── AmrPlayerReader.m │ │ ├── MLAudioPlayer.h │ │ └── MLAudioPlayer.m │ ├── MLAudioRealTimePlayer/ │ │ ├── MLAudioRealTimePlayer.h │ │ └── MLAudioRealTimePlayer.m │ ├── MLAudioRecorder/ │ │ ├── AmrRecordWriter.h │ │ ├── AmrRecordWriter.m │ │ ├── CafRecordWriter.h │ │ ├── CafRecordWriter.m │ │ ├── MLAudioMeterObserver.h │ │ ├── MLAudioMeterObserver.m │ │ ├── MLAudioRecorder.h │ │ ├── MLAudioRecorder.m │ │ ├── Mp3RecordWriter.h │ │ ├── Mp3RecordWriter.m │ │ ├── amr_en_de/ │ │ │ ├── include/ │ │ │ │ ├── opencore-amrnb/ │ │ │ │ │ ├── interf_dec.h │ │ │ │ │ └── interf_enc.h │ │ │ │ └── opencore-amrwb/ │ │ │ │ ├── dec_if.h │ │ │ │ └── if_rom.h │ │ │ └── lib/ │ │ │ ├── libopencore-amrnb.a │ │ │ └── libopencore-amrwb.a │ │ └── mp3_en_de/ │ │ └── lame.framework/ │ │ ├── Headers/ │ │ │ └── lame.h │ │ └── lame │ ├── MLPlayVoiceButton/ │ │ ├── MLAmrPlayer.h │ │ ├── MLAmrPlayer.m │ │ ├── MLDataCache/ │ │ │ ├── MLDataCache.h │ │ │ └── MLDataCache.m │ │ ├── MLDataResponseSerializer/ │ │ │ ├── MLDataResponseSerializer.h │ │ │ └── MLDataResponseSerializer.m │ │ ├── MLPlayVoiceButton.h │ │ └── MLPlayVoiceButton.m │ ├── MLRecorder-Info.plist │ ├── MLRecorder-Prefix.pch │ ├── PlayNetworkVoiceViewController.h │ ├── PlayNetworkVoiceViewController.m │ ├── RealTimeViewController.h │ ├── RealTimeViewController.m │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj/ │ │ └── InfoPlist.strings │ ├── main.m │ ├── test.amr │ └── test2.amr ├── MLRecorder.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata/ │ │ └── molon.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata/ │ └── molon.xcuserdatad/ │ └── xcschemes/ │ ├── MLRecorder.xcscheme │ └── xcschememanagement.plist ├── MLRecorderTests/ │ ├── MLRecorderTests-Info.plist │ ├── MLRecorderTests.m │ └── en.lproj/ │ └── InfoPlist.strings ├── README.md ├── record1.amr └── record2.amr ================================================ FILE CONTENTS ================================================ ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/AFNetworking.h ================================================ // AFNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #ifndef _AFNETWORKING_ #define _AFNETWORKING_ #import "AFURLRequestSerialization.h" #import "AFURLResponseSerialization.h" #import "AFSecurityPolicy.h" #import "AFNetworkReachabilityManager.h" #import "AFURLConnectionOperation.h" #import "AFHTTPRequestOperation.h" #import "AFHTTPRequestOperationManager.h" #if ( ( defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || \ ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 ) ) #import "AFURLSessionManager.h" #import "AFHTTPSessionManager.h" #endif #endif /* _AFNETWORKING_ */ ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLConnection/AFHTTPRequestOperation.h ================================================ // AFHTTPRequestOperation.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import "AFURLConnectionOperation.h" #import "AFURLResponseSerialization.h" /** `AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request. */ @interface AFHTTPRequestOperation : AFURLConnectionOperation ///------------------------------------------------ /// @name Getting HTTP URL Connection Information ///------------------------------------------------ /** The last HTTP response received by the operation's connection. */ @property (readonly, nonatomic, strong) NSHTTPURLResponse *response; /** Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an AFHTTPResponse serializer, which uses the raw data as its response object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. @warning `responseSerializer` must not be `nil`. Setting a response serializer will clear out any cached value */ @property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; /** An object constructed by the `responseSerializer` from the response and response data. Returns `nil` unless the operation `isFinished`, has a `response`, and has `responseData` with non-zero content length. If an error occurs during serialization, `nil` will be returned, and the `error` property will be populated with the serialization error. */ @property (readonly, nonatomic, strong) id responseObject; ///----------------------------------------------------------- /// @name Setting Completion Block Success / Failure Callbacks ///----------------------------------------------------------- /** Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed. This method should be overridden in subclasses in order to specify the response object passed into the success block. @param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request. @param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occurred during the request. */ - (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLConnection/AFHTTPRequestOperation.m ================================================ // AFHTTPRequestOperation.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFHTTPRequestOperation.h" static dispatch_queue_t http_request_operation_processing_queue() { static dispatch_queue_t af_http_request_operation_processing_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_http_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.http-request.processing", DISPATCH_QUEUE_CONCURRENT); }); return af_http_request_operation_processing_queue; } static dispatch_group_t http_request_operation_completion_group() { static dispatch_group_t af_http_request_operation_completion_group; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_http_request_operation_completion_group = dispatch_group_create(); }); return af_http_request_operation_completion_group; } #pragma mark - @interface AFURLConnectionOperation () @property (readwrite, nonatomic, strong) NSURLRequest *request; @property (readwrite, nonatomic, strong) NSURLResponse *response; @end @interface AFHTTPRequestOperation () @property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; @property (readwrite, nonatomic, strong) id responseObject; @property (readwrite, nonatomic, strong) NSError *responseSerializationError; @property (readwrite, nonatomic, strong) NSRecursiveLock *lock; @end @implementation AFHTTPRequestOperation @dynamic lock; - (instancetype)initWithRequest:(NSURLRequest *)urlRequest { self = [super initWithRequest:urlRequest]; if (!self) { return nil; } self.responseSerializer = [AFHTTPResponseSerializer serializer]; return self; } - (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { NSParameterAssert(responseSerializer); [self.lock lock]; _responseSerializer = responseSerializer; self.responseObject = nil; self.responseSerializationError = nil; [self.lock unlock]; } - (id)responseObject { [self.lock lock]; if (!_responseObject && [self isFinished] && !self.error) { NSError *error = nil; self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error]; if (error) { self.responseSerializationError = error; } } [self.lock unlock]; return _responseObject; } - (NSError *)error { if (_responseSerializationError) { return _responseSerializationError; } else { return [super error]; } } #pragma mark - AFHTTPRequestOperation - (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" #pragma clang diagnostic ignored "-Wgnu" self.completionBlock = ^{ if (self.completionGroup) { dispatch_group_enter(self.completionGroup); } dispatch_async(http_request_operation_processing_queue(), ^{ if (self.error) { if (failure) { dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ failure(self, self.error); }); } } else { id responseObject = self.responseObject; if (self.error) { if (failure) { dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ failure(self, self.error); }); } } else { if (success) { dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ success(self, responseObject); }); } } } if (self.completionGroup) { dispatch_group_leave(self.completionGroup); } }); }; #pragma clang diagnostic pop } #pragma mark - AFURLRequestOperation - (void)pause { u_int64_t offset = 0; if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) { offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue]; } else { offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length]; } NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy]; if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) { [mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"]; } [mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"]; self.request = mutableURLRequest; [super pause]; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) { return nil; } self.responseSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseSerializer))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFHTTPRequestOperation *operation = [[[self class] allocWithZone:zone] initWithRequest:self.request]; operation.responseSerializer = [self.responseSerializer copyWithZone:zone]; operation.completionQueue = self.completionQueue; operation.completionGroup = self.completionGroup; return operation; } @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLConnection/AFHTTPRequestOperationManager.h ================================================ // AFHTTPRequestOperationManager.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #import #if __IPHONE_OS_VERSION_MIN_REQUIRED #import #else #import #endif #import "AFHTTPRequestOperation.h" #import "AFURLResponseSerialization.h" #import "AFURLRequestSerialization.h" #import "AFSecurityPolicy.h" #import "AFNetworkReachabilityManager.h" /** `AFHTTPRequestOperationManager` encapsulates the common patterns of communicating with a web application over HTTP, including request creation, response serialization, network reachability monitoring, and security, as well as request operation management. ## Subclassing Notes Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. ## Methods to Override To change the behavior of all request operation construction for an `AFHTTPRequestOperationManager` subclass, override `HTTPRequestOperationWithRequest:success:failure`. ## Serialization Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` ## URL Construction Using Relative Paths For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. Below are a few examples of how `baseURL` and relative paths interact: NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. ## Network Reachability Monitoring Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. ## NSCoding & NSCopying Caveats `AFHTTPRequestOperationManager` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. There are a few minor caveats to keep in mind, however: - Archives and copies of HTTP clients will be initialized with an empty operation queue. - NSCoding cannot serialize / deserialize block properties, so an archive of an HTTP client will not include any reachability callback block that may be set. */ @interface AFHTTPRequestOperationManager : NSObject /** The URL used to monitor reachability, and construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods. */ @property (readonly, nonatomic, strong) NSURL *baseURL; /** Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. @warning `requestSerializer` must not be `nil`. */ @property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; /** Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to a JSON serializer, which serializes data from responses with a `application/json` MIME type, and falls back to the raw data object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. @warning `responseSerializer` must not be `nil`. */ @property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; /** The operation queue on which request operations are scheduled and run. */ @property (nonatomic, strong) NSOperationQueue *operationQueue; ///------------------------------- /// @name Managing URL Credentials ///------------------------------- /** Whether request operations should consult the credential storage for authenticating the connection. `YES` by default. @see AFURLConnectionOperation -shouldUseCredentialStorage */ @property (nonatomic, assign) BOOL shouldUseCredentialStorage; /** The credential used by request operations for authentication challenges. @see AFURLConnectionOperation -credential */ @property (nonatomic, strong) NSURLCredential *credential; ///------------------------------- /// @name Managing Security Policy ///------------------------------- /** The security policy used by created request operations to evaluate server trust for secure connections. `AFHTTPRequestOperationManager` uses the `defaultPolicy` unless otherwise specified. */ @property (nonatomic, strong) AFSecurityPolicy *securityPolicy; ///------------------------------------ /// @name Managing Network Reachability ///------------------------------------ /** The network reachability manager. `AFHTTPRequestOperationManager` uses the `sharedManager` by default. */ @property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; ///--------------------------------------------- /// @name Creating and Initializing HTTP Clients ///--------------------------------------------- /** Creates and returns an `AFHTTPRequestOperationManager` object. */ + (instancetype)manager; /** Initializes an `AFHTTPRequestOperationManager` object with the specified base URL. This is the designated initializer. @param url The base URL for the HTTP client. @return The newly-initialized HTTP client */ - (instancetype)initWithBaseURL:(NSURL *)url; ///--------------------------------------- /// @name Managing HTTP Request Operations ///--------------------------------------- /** Creates an `AFHTTPRequestOperation`, and sets the response serializers to that of the HTTP client. @param request The request object to be loaded asynchronously during execution of the operation. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred. */ - (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; ///--------------------------- /// @name Making HTTP Requests ///--------------------------- /** Creates and runs an `AFHTTPRequestOperation` with a `GET` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. @see -HTTPRequestOperationWithRequest:success:failure: */ - (AFHTTPRequestOperation *)GET:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; /** Creates and runs an `AFHTTPRequestOperation` with a `HEAD` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single arguments: the request operation. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. @see -HTTPRequestOperationWithRequest:success:failure: */ - (AFHTTPRequestOperation *)HEAD:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; /** Creates and runs an `AFHTTPRequestOperation` with a `POST` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. @see -HTTPRequestOperationWithRequest:success:failure: */ - (AFHTTPRequestOperation *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; /** Creates and runs an `AFHTTPRequestOperation` with a multipart `POST` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. @see -HTTPRequestOperationWithRequest:success:failure: */ - (AFHTTPRequestOperation *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id formData))block success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; /** Creates and runs an `AFHTTPRequestOperation` with a `PUT` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. @see -HTTPRequestOperationWithRequest:success:failure: */ - (AFHTTPRequestOperation *)PUT:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; /** Creates and runs an `AFHTTPRequestOperation` with a `PATCH` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. @see -HTTPRequestOperationWithRequest:success:failure: */ - (AFHTTPRequestOperation *)PATCH:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; /** Creates and runs an `AFHTTPRequestOperation` with a `DELETE` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. @see -HTTPRequestOperationWithRequest:success:failure: */ - (AFHTTPRequestOperation *)DELETE:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLConnection/AFHTTPRequestOperationManager.m ================================================ // AFHTTPRequestOperationManager.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import "AFHTTPRequestOperationManager.h" #import "AFHTTPRequestOperation.h" #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import #endif @interface AFHTTPRequestOperationManager () @property (readwrite, nonatomic, strong) NSURL *baseURL; @end @implementation AFHTTPRequestOperationManager + (instancetype)manager { return [[[self class] alloc] initWithBaseURL:nil]; } - (instancetype)init { return [self initWithBaseURL:nil]; } - (instancetype)initWithBaseURL:(NSURL *)url { self = [super init]; if (!self) { return nil; } // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { url = [url URLByAppendingPathComponent:@""]; } self.baseURL = url; self.requestSerializer = [AFHTTPRequestSerializer serializer]; self.responseSerializer = [AFJSONResponseSerializer serializer]; self.securityPolicy = [AFSecurityPolicy defaultPolicy]; self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; self.operationQueue = [[NSOperationQueue alloc] init]; self.shouldUseCredentialStorage = YES; return self; } - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.operationQueue]; } #pragma mark - #ifdef _SYSTEMCONFIGURATION_H #endif - (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { NSParameterAssert(requestSerializer); _requestSerializer = requestSerializer; } - (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { NSParameterAssert(responseSerializer); _responseSerializer = responseSerializer; } #pragma mark - - (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; operation.responseSerializer = self.responseSerializer; operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage; operation.credential = self.credential; operation.securityPolicy = self.securityPolicy; [operation setCompletionBlockWithSuccess:success failure:failure]; return operation; } #pragma mark - - (AFHTTPRequestOperation *)GET:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; [self.operationQueue addOperation:operation]; return operation; } - (AFHTTPRequestOperation *)HEAD:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *requestOperation, __unused id responseObject) { if (success) { success(requestOperation); } } failure:failure]; [self.operationQueue addOperation:operation]; return operation; } - (AFHTTPRequestOperation *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; [self.operationQueue addOperation:operation]; return operation; } - (AFHTTPRequestOperation *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id formData))block success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:nil]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; [self.operationQueue addOperation:operation]; return operation; } - (AFHTTPRequestOperation *)PUT:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PUT" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; [self.operationQueue addOperation:operation]; return operation; } - (AFHTTPRequestOperation *)PATCH:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PATCH" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; [self.operationQueue addOperation:operation]; return operation; } - (AFHTTPRequestOperation *)DELETE:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"DELETE" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; [self.operationQueue addOperation:operation]; return operation; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { NSURL *baseURL = [decoder decodeObjectForKey:NSStringFromSelector(@selector(baseURL))]; self = [self initWithBaseURL:baseURL]; if (!self) { return nil; } self.requestSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(requestSerializer))]; self.responseSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseSerializer))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFHTTPRequestOperationManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL]; HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; return HTTPClient; } @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLConnection/AFURLConnectionOperation.h ================================================ // AFURLConnectionOperation.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #import "AFSecurityPolicy.h" /** `AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods. ## Subclassing Notes This is the base class of all network request operations. You may wish to create your own subclass in order to implement additional `NSURLConnection` delegate methods (see "`NSURLConnection` Delegate Methods" below), or to provide additional properties and/or class constructors. If you are creating a subclass that communicates over the HTTP or HTTPS protocols, you may want to consider subclassing `AFHTTPRequestOperation` instead, as it supports specifying acceptable content types or status codes. ## NSURLConnection Delegate Methods `AFURLConnectionOperation` implements the following `NSURLConnection` delegate methods: - `connection:didReceiveResponse:` - `connection:didReceiveData:` - `connectionDidFinishLoading:` - `connection:didFailWithError:` - `connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:` - `connection:willCacheResponse:` - `connectionShouldUseCredentialStorage:` - `connection:needNewBodyStream:` - `connection:willSendRequestForAuthenticationChallenge:` If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. ## Callbacks and Completion Blocks The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (i.e. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this. Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/). ## SSL Pinning Relying on the CA trust model to validate SSL certificates exposes your app to security vulnerabilities, such as man-in-the-middle attacks. For applications that connect to known servers, SSL certificate pinning provides an increased level of security, by checking server certificate validity against those specified in the app bundle. SSL with certificate pinning is strongly recommended for any application that transmits sensitive information to an external webservice. Connections will be validated on all matching certificates with a `.cer` extension in the bundle root. ## NSCoding & NSCopying Conformance `AFURLConnectionOperation` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. However, because of the intrinsic limitations of capturing the exact state of an operation at a particular moment, there are some important caveats to keep in mind: ### NSCoding Caveats - Encoded operations do not include any block or stream properties. Be sure to set `completionBlock`, `outputStream`, and any callback blocks as necessary when using `-initWithCoder:` or `NSKeyedUnarchiver`. - Operations are paused on `encodeWithCoder:`. If the operation was encoded while paused or still executing, its archived state will return `YES` for `isReady`. Otherwise, the state of an operation when encoding will remain unchanged. ### NSCopying Caveats - `-copy` and `-copyWithZone:` return a new operation with the `NSURLRequest` of the original. So rather than an exact copy of the operation at that particular instant, the copying mechanism returns a completely new instance, which can be useful for retrying operations. - A copy of an operation will not include the `outputStream` of the original. - Operation copies do not include `completionBlock`, as it often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ operation when copied. */ @interface AFURLConnectionOperation : NSOperation ///------------------------------- /// @name Accessing Run Loop Modes ///------------------------------- /** The run loop modes in which the operation will run on the network thread. By default, this is a single-member set containing `NSRunLoopCommonModes`. */ @property (nonatomic, strong) NSSet *runLoopModes; ///----------------------------------------- /// @name Getting URL Connection Information ///----------------------------------------- /** The request used by the operation's connection. */ @property (readonly, nonatomic, strong) NSURLRequest *request; /** The last response received by the operation's connection. */ @property (readonly, nonatomic, strong) NSURLResponse *response; /** The error, if any, that occurred in the lifecycle of the request. */ @property (readonly, nonatomic, strong) NSError *error; ///---------------------------- /// @name Getting Response Data ///---------------------------- /** The data received during the request. */ @property (readonly, nonatomic, strong) NSData *responseData; /** The string representation of the response data. */ @property (readonly, nonatomic, copy) NSString *responseString; /** The string encoding of the response. If the response does not specify a valid string encoding, `responseStringEncoding` will return `NSUTF8StringEncoding`. */ @property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding; ///------------------------------- /// @name Managing URL Credentials ///------------------------------- /** Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default. This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`. */ @property (nonatomic, assign) BOOL shouldUseCredentialStorage; /** The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. */ @property (nonatomic, strong) NSURLCredential *credential; ///------------------------------- /// @name Managing Security Policy ///------------------------------- /** The security policy used to evaluate server trust for secure connections. */ @property (nonatomic, strong) AFSecurityPolicy *securityPolicy; ///------------------------ /// @name Accessing Streams ///------------------------ /** The input stream used to read data to be sent during the request. This property acts as a proxy to the `HTTPBodyStream` property of `request`. */ @property (nonatomic, strong) NSInputStream *inputStream; /** The output stream that is used to write data received until the request is finished. By default, data is accumulated into a buffer that is stored into `responseData` upon completion of the request. When `outputStream` is set, the data will not be accumulated into an internal buffer, and as a result, the `responseData` property of the completed request will be `nil`. The output stream will be scheduled in the network thread runloop upon being set. */ @property (nonatomic, strong) NSOutputStream *outputStream; ///--------------------------------- /// @name Managing Callback Queues ///--------------------------------- /** The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. */ @property (nonatomic, strong) dispatch_queue_t completionQueue; /** The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. */ @property (nonatomic, strong) dispatch_group_t completionGroup; ///--------------------------------------------- /// @name Managing Request Operation Information ///--------------------------------------------- /** The user info dictionary for the receiver. */ @property (nonatomic, strong) NSDictionary *userInfo; ///------------------------------------------------------ /// @name Initializing an AFURLConnectionOperation Object ///------------------------------------------------------ /** Initializes and returns a newly allocated operation object with a url connection configured with the specified url request. This is the designated initializer. @param urlRequest The request object to be used by the operation connection. */ - (instancetype)initWithRequest:(NSURLRequest *)urlRequest; ///---------------------------------- /// @name Pausing / Resuming Requests ///---------------------------------- /** Pauses the execution of the request operation. A paused operation returns `NO` for `-isReady`, `-isExecuting`, and `-isFinished`. As such, it will remain in an `NSOperationQueue` until it is either cancelled or resumed. Pausing a finished, cancelled, or paused operation has no effect. */ - (void)pause; /** Whether the request operation is currently paused. @return `YES` if the operation is currently paused, otherwise `NO`. */ - (BOOL)isPaused; /** Resumes the execution of the paused request operation. Pause/Resume behavior varies depending on the underlying implementation for the operation class. In its base implementation, resuming a paused requests restarts the original request. However, since HTTP defines a specification for how to request a specific content range, `AFHTTPRequestOperation` will resume downloading the request from where it left off, instead of restarting the original request. */ - (void)resume; ///---------------------------------------------- /// @name Configuring Backgrounding Task Behavior ///---------------------------------------------- /** Specifies that the operation should continue execution after the app has entered the background, and the expiration handler for that background task. @param handler A handler to be called shortly before the application’s remaining background time reaches 0. The handler is wrapped in a block that cancels the operation, and cleans up and marks the end of execution, unlike the `handler` parameter in `UIApplication -beginBackgroundTaskWithExpirationHandler:`, which expects this to be done in the handler itself. The handler is called synchronously on the main thread, thus blocking the application’s suspension momentarily while the application is notified. */ #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) - (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler; #endif ///--------------------------------- /// @name Setting Progress Callbacks ///--------------------------------- /** Sets a callback to be called when an undetermined number of bytes have been uploaded to the server. @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. */ - (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))block; /** Sets a callback to be called when an undetermined number of bytes have been downloaded from the server. @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. */ - (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead))block; ///------------------------------------------------- /// @name Setting NSURLConnection Delegate Callbacks ///------------------------------------------------- /** Sets a block to be executed when the connection will authenticate a challenge in order to download its request, as handled by the `NSURLConnectionDelegate` method `connection:willSendRequestForAuthenticationChallenge:`. @param block A block object to be executed when the connection will authenticate a challenge in order to download its request. The block has no return type and takes two arguments: the URL connection object, and the challenge that must be authenticated. This block must invoke one of the challenge-responder methods (NSURLAuthenticationChallengeSender protocol). If `allowsInvalidSSLCertificate` is set to YES, `connection:willSendRequestForAuthenticationChallenge:` will attempt to have the challenge sender use credentials with invalid SSL certificates. */ - (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block; /** Sets a block to be executed when the server redirects the request from one URL to another URL, or when the request URL changed by the `NSURLProtocol` subclass handling the request in order to standardize its format, as handled by the `NSURLConnectionDataDelegate` method `connection:willSendRequest:redirectResponse:`. @param block A block object to be executed when the request URL was changed. The block returns an `NSURLRequest` object, the URL request to redirect, and takes three arguments: the URL connection object, the the proposed redirected request, and the URL response that caused the redirect. */ - (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block; /** Sets a block to be executed to modify the response a connection will cache, if any, as handled by the `NSURLConnectionDelegate` method `connection:willCacheResponse:`. @param block A block object to be executed to determine what response a connection will cache, if any. The block returns an `NSCachedURLResponse` object, the cached response to store in memory or `nil` to prevent the response from being cached, and takes two arguments: the URL connection object, and the cached response provided for the request. */ - (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block; /// /** */ + (NSArray *)batchOfRequestOperations:(NSArray *)operations progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock completionBlock:(void (^)(NSArray *operations))completionBlock; @end ///---------------- /// @name Constants ///---------------- /** ## User info dictionary keys These keys may exist in the user info dictionary, in addition to those defined for NSError. - `NSString * const AFNetworkingOperationFailingURLRequestErrorKey` - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` ### Constants `AFNetworkingOperationFailingURLRequestErrorKey` The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFNetworkingErrorDomain`. `AFNetworkingOperationFailingURLResponseErrorKey` The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFNetworkingErrorDomain`. ## Error Domains The following error domain is predefined. - `NSString * const AFNetworkingErrorDomain` ### Constants `AFNetworkingErrorDomain` AFNetworking errors. Error codes for `AFNetworkingErrorDomain` correspond to codes in `NSURLErrorDomain`. */ extern NSString * const AFNetworkingErrorDomain; extern NSString * const AFNetworkingOperationFailingURLRequestErrorKey; extern NSString * const AFNetworkingOperationFailingURLResponseErrorKey; ///-------------------- /// @name Notifications ///-------------------- /** Posted when an operation begins executing. */ extern NSString * const AFNetworkingOperationDidStartNotification; /** Posted when an operation finishes. */ extern NSString * const AFNetworkingOperationDidFinishNotification; ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLConnection/AFURLConnectionOperation.m ================================================ // AFURLConnectionOperation.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFURLConnectionOperation.h" #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import #endif #if !__has_feature(objc_arc) #error AFNetworking must be built with ARC. // You can turn on ARC for only AFNetworking files by adding -fobjc-arc to the build phase for each of its files. #endif typedef NS_ENUM(NSInteger, AFOperationState) { AFOperationPausedState = -1, AFOperationReadyState = 1, AFOperationExecutingState = 2, AFOperationFinishedState = 3, }; #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) typedef UIBackgroundTaskIdentifier AFBackgroundTaskIdentifier; #else typedef id AFBackgroundTaskIdentifier; #endif static dispatch_group_t url_request_operation_completion_group() { static dispatch_group_t af_url_request_operation_completion_group; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_url_request_operation_completion_group = dispatch_group_create(); }); return af_url_request_operation_completion_group; } static dispatch_queue_t url_request_operation_completion_queue() { static dispatch_queue_t af_url_request_operation_completion_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT ); }); return af_url_request_operation_completion_queue; } static NSString * const kAFNetworkingLockName = @"com.alamofire.networking.operation.lock"; NSString * const AFNetworkingErrorDomain = @"AFNetworkingErrorDomain"; NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"AFNetworkingOperationFailingURLRequestErrorKey"; NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"AFNetworkingOperationFailingURLResponseErrorKey"; NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start"; NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish"; typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, NSInteger totalBytes, NSInteger totalBytesExpected); typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge); typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse); typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse); static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { switch (state) { case AFOperationReadyState: return @"isReady"; case AFOperationExecutingState: return @"isExecuting"; case AFOperationFinishedState: return @"isFinished"; case AFOperationPausedState: return @"isPaused"; default: { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunreachable-code" return @"state"; #pragma clang diagnostic pop } } } static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) { switch (fromState) { case AFOperationReadyState: switch (toState) { case AFOperationPausedState: case AFOperationExecutingState: return YES; case AFOperationFinishedState: return isCancelled; default: return NO; } case AFOperationExecutingState: switch (toState) { case AFOperationPausedState: case AFOperationFinishedState: return YES; default: return NO; } case AFOperationFinishedState: return NO; case AFOperationPausedState: return toState == AFOperationReadyState; default: return YES; } } @interface AFURLConnectionOperation () @property (readwrite, nonatomic, assign) AFOperationState state; @property (readwrite, nonatomic, strong) NSRecursiveLock *lock; @property (readwrite, nonatomic, strong) NSURLConnection *connection; @property (readwrite, nonatomic, strong) NSURLRequest *request; @property (readwrite, nonatomic, strong) NSURLResponse *response; @property (readwrite, nonatomic, strong) NSError *error; @property (readwrite, nonatomic, strong) NSData *responseData; @property (readwrite, nonatomic, copy) NSString *responseString; @property (readwrite, nonatomic, assign) NSStringEncoding responseStringEncoding; @property (readwrite, nonatomic, assign) NSInteger totalBytesRead; @property (readwrite, nonatomic, assign) AFBackgroundTaskIdentifier backgroundTaskIdentifier; @property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock uploadProgress; @property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock downloadProgress; @property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationChallengeBlock authenticationChallenge; @property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse; @property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse; - (void)operationDidStart; - (void)finish; - (void)cancelConnection; @end @implementation AFURLConnectionOperation @synthesize outputStream = _outputStream; + (void)networkRequestThreadEntryPoint:(id)__unused object { @autoreleasepool { [[NSThread currentThread] setName:@"AFNetworking"]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; } } + (NSThread *)networkRequestThread { static NSThread *_networkRequestThread = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; [_networkRequestThread start]; }); return _networkRequestThread; } - (instancetype)initWithRequest:(NSURLRequest *)urlRequest { NSParameterAssert(urlRequest); self = [super init]; if (!self) { return nil; } self.lock = [[NSRecursiveLock alloc] init]; self.lock.name = kAFNetworkingLockName; self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes]; self.request = urlRequest; self.shouldUseCredentialStorage = YES; self.state = AFOperationReadyState; self.securityPolicy = [AFSecurityPolicy defaultPolicy]; return self; } - (void)dealloc { if (_outputStream) { [_outputStream close]; _outputStream = nil; } #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) if (_backgroundTaskIdentifier) { [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier]; _backgroundTaskIdentifier = UIBackgroundTaskInvalid; } #endif } - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response]; } - (void)setCompletionBlock:(void (^)(void))block { [self.lock lock]; if (!block) { [super setCompletionBlock:nil]; } else { __weak __typeof(self)weakSelf = self; [super setCompletionBlock:^ { __strong __typeof(weakSelf)strongSelf = weakSelf; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group(); dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue(); #pragma clang diagnostic pop dispatch_group_async(group, queue, ^{ block(); }); dispatch_group_notify(group, url_request_operation_completion_queue(), ^{ [strongSelf setCompletionBlock:nil]; }); }]; } [self.lock unlock]; } - (NSInputStream *)inputStream { return self.request.HTTPBodyStream; } - (void)setInputStream:(NSInputStream *)inputStream { NSMutableURLRequest *mutableRequest = [self.request mutableCopy]; mutableRequest.HTTPBodyStream = inputStream; self.request = mutableRequest; } - (NSOutputStream *)outputStream { if (!_outputStream) { self.outputStream = [NSOutputStream outputStreamToMemory]; } return _outputStream; } - (void)setOutputStream:(NSOutputStream *)outputStream { [self.lock lock]; if (outputStream != _outputStream) { if (_outputStream) { [_outputStream close]; } _outputStream = outputStream; } [self.lock unlock]; } #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) - (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler { [self.lock lock]; if (!self.backgroundTaskIdentifier) { UIApplication *application = [UIApplication sharedApplication]; __weak __typeof(self)weakSelf = self; self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ __strong __typeof(weakSelf)strongSelf = weakSelf; if (handler) { handler(); } if (strongSelf) { [strongSelf cancel]; [application endBackgroundTask:strongSelf.backgroundTaskIdentifier]; strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid; } }]; } [self.lock unlock]; } #endif - (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))block { self.uploadProgress = block; } - (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead))block { self.downloadProgress = block; } - (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block { self.authenticationChallenge = block; } - (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block { self.cacheResponse = block; } - (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block { self.redirectResponse = block; } - (void)setState:(AFOperationState)state { if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) { return; } [self.lock lock]; NSString *oldStateKey = AFKeyPathFromOperationState(self.state); NSString *newStateKey = AFKeyPathFromOperationState(state); [self willChangeValueForKey:newStateKey]; [self willChangeValueForKey:oldStateKey]; _state = state; [self didChangeValueForKey:oldStateKey]; [self didChangeValueForKey:newStateKey]; [self.lock unlock]; } - (NSString *)responseString { [self.lock lock]; if (!_responseString && self.response && self.responseData) { self.responseString = [[NSString alloc] initWithData:self.responseData encoding:self.responseStringEncoding]; } [self.lock unlock]; return _responseString; } - (NSStringEncoding)responseStringEncoding { [self.lock lock]; if (!_responseStringEncoding && self.response) { NSStringEncoding stringEncoding = NSUTF8StringEncoding; if (self.response.textEncodingName) { CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName); if (IANAEncoding != kCFStringEncodingInvalidId) { stringEncoding = CFStringConvertEncodingToNSStringEncoding(IANAEncoding); } } self.responseStringEncoding = stringEncoding; } [self.lock unlock]; return _responseStringEncoding; } - (void)pause { if ([self isPaused] || [self isFinished] || [self isCancelled]) { return; } [self.lock lock]; if ([self isExecuting]) { [self performSelector:@selector(operationDidPause) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; dispatch_async(dispatch_get_main_queue(), ^{ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; }); } self.state = AFOperationPausedState; [self.lock unlock]; } - (void)operationDidPause { [self.lock lock]; [self.connection cancel]; [self.lock unlock]; } - (BOOL)isPaused { return self.state == AFOperationPausedState; } - (void)resume { if (![self isPaused]) { return; } [self.lock lock]; self.state = AFOperationReadyState; [self start]; [self.lock unlock]; } #pragma mark - NSOperation - (BOOL)isReady { return self.state == AFOperationReadyState && [super isReady]; } - (BOOL)isExecuting { return self.state == AFOperationExecutingState; } - (BOOL)isFinished { return self.state == AFOperationFinishedState; } - (BOOL)isConcurrent { return YES; } - (void)start { [self.lock lock]; if ([self isCancelled]) { [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } else if ([self isReady]) { self.state = AFOperationExecutingState; [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } [self.lock unlock]; } - (void)operationDidStart { [self.lock lock]; if (![self isCancelled]) { self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; for (NSString *runLoopMode in self.runLoopModes) { [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode]; [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode]; } [self.connection start]; } [self.lock unlock]; dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self]; }); } - (void)finish { [self.lock lock]; self.state = AFOperationFinishedState; [self.lock unlock]; dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; }); } - (void)cancel { [self.lock lock]; if (![self isFinished] && ![self isCancelled]) { [super cancel]; if ([self isExecuting]) { [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } } [self.lock unlock]; } - (void)cancelConnection { NSDictionary *userInfo = nil; if ([self.request URL]) { userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey]; } NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo]; if (![self isFinished]) { if (self.connection) { [self.connection cancel]; [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error]; } else { // Accomodate race condition where `self.connection` has not yet been set before cancellation self.error = error; [self finish]; } } } #pragma mark - + (NSArray *)batchOfRequestOperations:(NSArray *)operations progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock completionBlock:(void (^)(NSArray *operations))completionBlock { if (!operations || [operations count] == 0) { return @[[NSBlockOperation blockOperationWithBlock:^{ dispatch_async(dispatch_get_main_queue(), ^{ if (completionBlock) { completionBlock(@[]); } }); }]]; } __block dispatch_group_t group = dispatch_group_create(); NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{ dispatch_group_notify(group, dispatch_get_main_queue(), ^{ if (completionBlock) { completionBlock(operations); } }); }]; for (AFURLConnectionOperation *operation in operations) { operation.completionGroup = group; void (^originalCompletionBlock)(void) = [operation.completionBlock copy]; __weak __typeof(operation)weakOperation = operation; operation.completionBlock = ^{ __strong __typeof(weakOperation)strongOperation = weakOperation; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue(); #pragma clang diagnostic pop dispatch_group_async(group, queue, ^{ if (originalCompletionBlock) { originalCompletionBlock(); } NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx, BOOL __unused *stop) { return [op isFinished]; }] count]; if (progressBlock) { progressBlock(numberOfFinishedOperations, [operations count]); } dispatch_group_leave(group); }); }; dispatch_group_enter(group); [batchedOperation addDependency:operation]; } return [operations arrayByAddingObject:batchedOperation]; } #pragma mark - NSURLConnectionDelegate - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if (self.authenticationChallenge) { self.authenticationChallenge(connection, challenge); return; } if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; } else { [[challenge sender] cancelAuthenticationChallenge:challenge]; } } else { if ([challenge previousFailureCount] == 0) { if (self.credential) { [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge]; } else { [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; } } else { [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; } } } - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection { return self.shouldUseCredentialStorage; } - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse { if (self.redirectResponse) { return self.redirectResponse(connection, request, redirectResponse); } else { return request; } } - (void)connection:(NSURLConnection __unused *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { if (self.uploadProgress) { dispatch_async(dispatch_get_main_queue(), ^{ self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); }); } } - (void)connection:(NSURLConnection __unused *)connection didReceiveResponse:(NSURLResponse *)response { self.response = response; [self.outputStream open]; } - (void)connection:(NSURLConnection __unused *)connection didReceiveData:(NSData *)data { NSUInteger length = [data length]; while (YES) { NSInteger totalNumberOfBytesWritten = 0; if ([self.outputStream hasSpaceAvailable]) { const uint8_t *dataBuffer = (uint8_t *)[data bytes]; NSInteger numberOfBytesWritten = 0; while (totalNumberOfBytesWritten < (NSInteger)length) { numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)]; if (numberOfBytesWritten == -1) { break; } totalNumberOfBytesWritten += numberOfBytesWritten; } break; } if (self.outputStream.streamError) { [self.connection cancel]; [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError]; return; } } dispatch_async(dispatch_get_main_queue(), ^{ self.totalBytesRead += (NSInteger)length; if (self.downloadProgress) { self.downloadProgress(length, self.totalBytesRead, (NSInteger)self.response.expectedContentLength); } }); } - (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection { self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; [self.outputStream close]; [self finish]; self.connection = nil; } - (void)connection:(NSURLConnection __unused *)connection didFailWithError:(NSError *)error { self.error = error; [self.outputStream close]; [self finish]; self.connection = nil; } - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { if (self.cacheResponse) { return self.cacheResponse(connection, cachedResponse); } else { if ([self isCancelled]) { return nil; } return cachedResponse; } } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { NSURLRequest *request = [decoder decodeObjectForKey:NSStringFromSelector(@selector(request))]; self = [self initWithRequest:request]; if (!self) { return nil; } self.state = (AFOperationState)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(state))]; self.response = [decoder decodeObjectForKey:NSStringFromSelector(@selector(response))]; self.error = [decoder decodeObjectForKey:NSStringFromSelector(@selector(error))]; self.responseData = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseData))]; self.totalBytesRead = [decoder decodeIntegerForKey:NSStringFromSelector(@selector(totalBytesRead))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [self pause]; [coder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))]; switch (self.state) { case AFOperationExecutingState: case AFOperationPausedState: [coder encodeInteger:AFOperationReadyState forKey:NSStringFromSelector(@selector(state))]; break; default: [coder encodeInteger:self.state forKey:NSStringFromSelector(@selector(state))]; break; } [coder encodeObject:self.response forKey:NSStringFromSelector(@selector(response))]; [coder encodeObject:self.error forKey:NSStringFromSelector(@selector(error))]; [coder encodeObject:self.responseData forKey:NSStringFromSelector(@selector(responseData))]; [coder encodeInt64:self.totalBytesRead forKey:NSStringFromSelector(@selector(totalBytesRead))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFURLConnectionOperation *operation = [(AFURLConnectionOperation *)[[self class] allocWithZone:zone] initWithRequest:self.request]; operation.uploadProgress = self.uploadProgress; operation.downloadProgress = self.downloadProgress; operation.authenticationChallenge = self.authenticationChallenge; operation.cacheResponse = self.cacheResponse; operation.redirectResponse = self.redirectResponse; operation.completionQueue = self.completionQueue; operation.completionGroup = self.completionGroup; return operation; } @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLSession/AFHTTPSessionManager.h ================================================ // AFHTTPSessionManager.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #import #if __IPHONE_OS_VERSION_MIN_REQUIRED #import #else #import #endif #import "AFURLSessionManager.h" /** `AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths. ## Subclassing Notes Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. ## Methods to Override To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`. ## Serialization Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` ## URL Construction Using Relative Paths For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. Below are a few examples of how `baseURL` and relative paths interact: NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. */ #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) @interface AFHTTPSessionManager : AFURLSessionManager /** The URL used to monitor reachability, and construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods. */ @property (readonly, nonatomic, strong) NSURL *baseURL; /** Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. @warning `requestSerializer` must not be `nil`. */ @property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; /** Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. @warning `responseSerializer` must not be `nil`. */ @property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; ///--------------------- /// @name Initialization ///--------------------- /** Creates and returns an `AFHTTPSessionManager` object. */ + (instancetype)manager; /** Initializes an `AFHTTPSessionManager` object with the specified base URL. @param url The base URL for the HTTP client. @return The newly-initialized HTTP client */ - (instancetype)initWithBaseURL:(NSURL *)url; /** Initializes an `AFHTTPSessionManager` object with the specified base URL. This is the designated initializer. @param url The base URL for the HTTP client. @param configuration The configuration used to create the managed session. @return The newly-initialized HTTP client */ - (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration; ///--------------------------- /// @name Making HTTP Requests ///--------------------------- /** Creates and runs an `NSURLSessionDataTask` with a `GET` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. @see -dataTaskWithRequest:completionHandler: */ - (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; /** Creates and runs an `NSURLSessionDataTask` with a `HEAD` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. @see -dataTaskWithRequest:completionHandler: */ - (NSURLSessionDataTask *)HEAD:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; /** Creates and runs an `NSURLSessionDataTask` with a `POST` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. @see -dataTaskWithRequest:completionHandler: */ - (NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; /** Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. @see -dataTaskWithRequest:completionHandler: */ - (NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id formData))block success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; /** Creates and runs an `NSURLSessionDataTask` with a `PUT` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. @see -dataTaskWithRequest:completionHandler: */ - (NSURLSessionDataTask *)PUT:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; /** Creates and runs an `NSURLSessionDataTask` with a `PATCH` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. @see -dataTaskWithRequest:completionHandler: */ - (NSURLSessionDataTask *)PATCH:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; /** Creates and runs an `NSURLSessionDataTask` with a `DELETE` request. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded according to the client request serializer. @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. @see -dataTaskWithRequest:completionHandler: */ - (NSURLSessionDataTask *)DELETE:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLSession/AFHTTPSessionManager.m ================================================ // AFHTTPSessionManager.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFHTTPSessionManager.h" #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) #import "AFHTTPRequestOperation.h" #import #import #ifdef _SYSTEMCONFIGURATION_H #import #import #import #import #import #endif #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import #endif @interface AFHTTPSessionManager () @property (readwrite, nonatomic, strong) NSURL *baseURL; @end @implementation AFHTTPSessionManager + (instancetype)manager { return [[[self class] alloc] initWithBaseURL:nil]; } - (instancetype)init { return [self initWithBaseURL:nil]; } - (instancetype)initWithBaseURL:(NSURL *)url { return [self initWithBaseURL:url sessionConfiguration:nil]; } - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { return [self initWithBaseURL:nil sessionConfiguration:configuration]; } - (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration { self = [super initWithSessionConfiguration:configuration]; if (!self) { return nil; } // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { url = [url URLByAppendingPathComponent:@""]; } self.baseURL = url; self.requestSerializer = [AFHTTPRequestSerializer serializer]; self.responseSerializer = [AFJSONResponseSerializer serializer]; return self; } - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue]; } #pragma mark - #ifdef _SYSTEMCONFIGURATION_H #endif - (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { NSParameterAssert(requestSerializer); _requestSerializer = requestSerializer; } - (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { NSParameterAssert(responseSerializer); [super setResponseSerializer:responseSerializer]; } #pragma mark - - (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { failure(task, error); } } else { if (success) { success(task, responseObject); } } }]; [task resume]; return task; } - (NSURLSessionDataTask *)HEAD:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id __unused responseObject, NSError *error) { if (error) { if (failure) { failure(task, error); } } else { if (success) { success(task); } } }]; [task resume]; return task; } - (NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { failure(task, error); } } else { if (success) { success(task, responseObject); } } }]; [task resume]; return task; } - (NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id formData))block success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:nil]; __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { failure(task, error); } } else { if (success) { success(task, responseObject); } } }]; [task resume]; return task; } - (NSURLSessionDataTask *)PUT:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PUT" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { failure(task, error); } } else { if (success) { success(task, responseObject); } } }]; [task resume]; return task; } - (NSURLSessionDataTask *)PATCH:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PATCH" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { failure(task, error); } } else { if (success) { success(task, responseObject); } } }]; [task resume]; return task; } - (NSURLSessionDataTask *)DELETE:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"DELETE" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { failure(task, error); } } else { if (success) { success(task, responseObject); } } }]; [task resume]; return task; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { NSURL *baseURL = [decoder decodeObjectForKey:NSStringFromSelector(@selector(baseURL))]; NSURLSessionConfiguration *configuration = [decoder decodeObjectForKey:@"sessionConfiguration"]; if (!configuration) { NSString *configurationIdentifier = [decoder decodeObjectForKey:@"identifier"]; if (configurationIdentifier) { configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier]; } } self = [self initWithBaseURL:baseURL sessionConfiguration:configuration]; if (!self) { return nil; } self.requestSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(requestSerializer))]; self.responseSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseSerializer))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) { [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; } else { [coder encodeObject:self.session.configuration.identifier forKey:@"identifier"]; } [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration]; HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; return HTTPClient; } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLSession/AFURLSessionManager.h ================================================ // AFURLSessionManager.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import "AFURLResponseSerialization.h" #import "AFURLRequestSerialization.h" #import "AFSecurityPolicy.h" #import "AFNetworkReachabilityManager.h" /** `AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. ## Subclassing Notes This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead. ## NSURLSession & NSURLSessionTask Delegate Methods `AFURLSessionManager` implements the following delegate methods: ### `NSURLSessionDelegate` - `URLSession:didBecomeInvalidWithError:` - `URLSession:didReceiveChallenge:completionHandler:` ### `NSURLSessionTaskDelegate` - `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:` - `URLSession:task:didReceiveChallenge:completionHandler:` - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:` - `URLSession:task:didCompleteWithError:` ### `NSURLSessionDataDelegate` - `URLSession:dataTask:didReceiveResponse:completionHandler:` - `URLSession:dataTask:didBecomeDownloadTask:` - `URLSession:dataTask:didReceiveData:` - `URLSession:dataTask:willCacheResponse:completionHandler:` - `URLSessionDidFinishEventsForBackgroundURLSession:` ### `NSURLSessionDownloadDelegate` - `URLSession:downloadTask:didFinishDownloadingToURL:` - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:` - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:` If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. ## Network Reachability Monitoring Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. ## NSCoding Caveats - Encoded managers do not include any block properties. Be sure to set delegate callback blocks when using `-initWithCoder:` or `NSKeyedUnarchiver`. ## NSCopying Caveats - `-copy` and `-copyWithZone:` return a new manager with a new `NSURLSession` created from the configuration of the original. - Operation copies do not include any delegate callback blocks, as they often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ session manager when copied. */ #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) @interface AFURLSessionManager : NSObject /** The managed session. */ @property (readonly, nonatomic, strong) NSURLSession *session; /** The operation queue on which delegate callbacks are run. */ @property (readonly, nonatomic, strong) NSOperationQueue *operationQueue; /** Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. @warning `responseSerializer` must not be `nil`. */ @property (nonatomic, strong) id responseSerializer; ///------------------------------- /// @name Managing Security Policy ///------------------------------- /** The security policy used by created request operations to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. */ @property (nonatomic, strong) AFSecurityPolicy *securityPolicy; ///-------------------------------------- /// @name Monitoring Network Reachability ///-------------------------------------- /** The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default. */ @property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; ///---------------------------- /// @name Getting Session Tasks ///---------------------------- /** The data, upload, and download tasks currently run by the managed session. */ @property (readonly, nonatomic, strong) NSArray *tasks; /** The data tasks currently run by the managed session. */ @property (readonly, nonatomic, strong) NSArray *dataTasks; /** The upload tasks currently run by the managed session. */ @property (readonly, nonatomic, strong) NSArray *uploadTasks; /** The download tasks currently run by the managed session. */ @property (readonly, nonatomic, strong) NSArray *downloadTasks; ///--------------------------------- /// @name Managing Callback Queues ///--------------------------------- /** The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. */ @property (nonatomic, strong) dispatch_queue_t completionQueue; /** The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. */ @property (nonatomic, strong) dispatch_group_t completionGroup; ///--------------------- /// @name Initialization ///--------------------- /** Creates and returns a manager for a session created with the specified configuration. This is the designated initializer. @param configuration The configuration used to create the managed session. @return A manager for a newly-created session. */ - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration; /** Invalidates the managed session, optionally canceling pending tasks. @param cancelPendingTasks Whether or not to cancel pending tasks. */ - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks; ///------------------------- /// @name Running Data Tasks ///------------------------- /** Creates an `NSURLSessionDataTask` with the specified request. @param request The HTTP request for the request. @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. */ - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; ///--------------------------- /// @name Running Upload Tasks ///--------------------------- /** Creates an `NSURLSessionUploadTask` with the specified request for a local file. @param request The HTTP request for the request. @param fileURL A URL to the local file to be uploaded. @param progress A progress object monitoring the current upload progress. @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. */ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; /** Creates an `NSURLSessionUploadTask` with the specified request for an HTTP body. @param request The HTTP request for the request. @param bodyData A data object containing the HTTP body to be uploaded. @param progress A progress object monitoring the current upload progress. @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. */ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; /** Creates an `NSURLSessionUploadTask` with the specified streaming request. @param request The HTTP request for the request. @param progress A progress object monitoring the current upload progress. @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. */ - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; ///----------------------------- /// @name Running Download Tasks ///----------------------------- /** Creates an `NSURLSessionDownloadTask` with the specified request. @param request The HTTP request for the request. @param progress A progress object monitoring the current download progress. @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. */ - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request progress:(NSProgress * __autoreleasing *)progress destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler; /** Creates an `NSURLSessionDownloadTask` with the specified resume data. @param resumeData The data used to resume downloading. @param progress A progress object monitoring the current download progress. @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. */ - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData progress:(NSProgress * __autoreleasing *)progress destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler; ///----------------------------------------- /// @name Setting Session Delegate Callbacks ///----------------------------------------- /** Sets a block to be executed when the managed session becomes invalid, as handled by the `NSURLSessionDelegate` method `URLSession:didBecomeInvalidWithError:`. @param block A block object to be executed when the managed session becomes invalid. The block has no return value, and takes two arguments: the session, and the error related to the cause of invalidation. */ - (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block; /** Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`. @param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. */ - (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block; ///-------------------------------------- /// @name Setting Task Delegate Callbacks ///-------------------------------------- /** Sets a block to be executed when a task requires a new request body stream to send to the remote server, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:needNewBodyStream:`. @param block A block object to be executed when a task requires a new request body stream. */ - (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block; /** Sets a block to be executed when an HTTP request is attempting to perform a redirection to a different URL, as handled by the `NSURLSessionTaskDelegate` method `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`. @param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response. */ - (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; /** Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`. @param block A block object to be executed when a session task has received a request specific authentication challenge. The block returns the disposition of the authentication challenge, and takes four arguments: the session, the task, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. */ - (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block; /** Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`. @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes five arguments: the session, the task, the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. */ - (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block; /** Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`. @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task. */ - (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block; ///------------------------------------------- /// @name Setting Data Task Delegate Callbacks ///------------------------------------------- /** Sets a block to be executed when a data task has received a response, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveResponse:completionHandler:`. @param block A block object to be executed when a data task has received a response. The block returns the disposition of the session response, and takes three arguments: the session, the data task, and the received response. */ - (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block; /** Sets a block to be executed when a data task has become a download task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didBecomeDownloadTask:`. @param block A block object to be executed when a data task has become a download task. The block has no return value, and takes three arguments: the session, the data task, and the download task it has become. */ - (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block; /** Sets a block to be executed when a data task receives data, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveData:`. @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the session, the data task, and the data received. This block may be called multiple times, and will execute on the session manager operation queue. */ - (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block; /** Sets a block to be executed to determine the caching behavior of a data task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:willCacheResponse:completionHandler:`. @param block A block object to be executed to determine the caching behavior of a data task. The block returns the response to cache, and takes three arguments: the session, the data task, and the proposed cached URL response. */ - (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block; /** Sets a block to be executed once all messages enqueued for a session have been delivered, as handled by the `NSURLSessionDataDelegate` method `URLSessionDidFinishEventsForBackgroundURLSession:`. @param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session. */ - (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block; ///----------------------------------------------- /// @name Setting Download Task Delegate Callbacks ///----------------------------------------------- /** Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`. @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error. */ - (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block; /** Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`. @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue. */ - (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block; /** Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`. @param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded. */ - (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block; @end #endif ///-------------------- /// @name Notifications ///-------------------- /** Posted when a task begins executing. @deprecated Use `AFNetworkingTaskDidResumeNotification` instead. */ extern NSString * const AFNetworkingTaskDidStartNotification DEPRECATED_ATTRIBUTE; /** Posted when a task resumes. */ extern NSString * const AFNetworkingTaskDidResumeNotification; /** Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. @deprecated Use `AFNetworkingTaskDidCompleteNotification` instead. */ extern NSString * const AFNetworkingTaskDidFinishNotification DEPRECATED_ATTRIBUTE; /** Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. */ extern NSString * const AFNetworkingTaskDidCompleteNotification; /** Posted when a task suspends its execution. */ extern NSString * const AFNetworkingTaskDidSuspendNotification; /** Posted when a session is invalidated. */ extern NSString * const AFURLSessionDidInvalidateNotification; /** Posted when a session download task encountered an error when moving the temporary download file to a specified destination. */ extern NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification; /** The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task. @deprecated Use `AFNetworkingTaskDidCompleteResponseDataKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishResponseDataKey DEPRECATED_ATTRIBUTE; /** The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task. */ extern NSString * const AFNetworkingTaskDidCompleteResponseDataKey; /** The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized. @deprecated Use `AFNetworkingTaskDidCompleteSerializedResponseKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishSerializedResponseKey DEPRECATED_ATTRIBUTE; /** The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized. */ extern NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey; /** The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer. @deprecated Use `AFNetworkingTaskDidCompleteResponseSerializerKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishResponseSerializerKey DEPRECATED_ATTRIBUTE; /** The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer. */ extern NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey; /** The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk. @deprecated Use `AFNetworkingTaskDidCompleteAssetPathKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishAssetPathKey DEPRECATED_ATTRIBUTE; /** The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk. */ extern NSString * const AFNetworkingTaskDidCompleteAssetPathKey; /** Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists. @deprecated Use `AFNetworkingTaskDidCompleteErrorKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishErrorKey DEPRECATED_ATTRIBUTE; /** Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists. */ extern NSString * const AFNetworkingTaskDidCompleteErrorKey; ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/NSURLSession/AFURLSessionManager.m ================================================ // AFURLSessionManager.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFURLSessionManager.h" #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) static dispatch_queue_t url_session_manager_processing_queue() { static dispatch_queue_t af_url_session_manager_processing_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); }); return af_url_session_manager_processing_queue; } static dispatch_group_t url_session_manager_completion_group() { static dispatch_group_t af_url_session_manager_completion_group; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_url_session_manager_completion_group = dispatch_group_create(); }); return af_url_session_manager_completion_group; } NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume"; NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete"; NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend"; NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate"; NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error"; NSString * const AFNetworkingTaskDidStartNotification = @"com.alamofire.networking.task.resume"; // Deprecated NSString * const AFNetworkingTaskDidFinishNotification = @"com.alamofire.networking.task.complete"; // Deprecated NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error"; NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; NSString * const AFNetworkingTaskDidFinishSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; // Deprecated NSString * const AFNetworkingTaskDidFinishResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; // Deprecated NSString * const AFNetworkingTaskDidFinishResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; // Deprecated NSString * const AFNetworkingTaskDidFinishErrorKey = @"com.alamofire.networking.task.complete.error"; // Deprecated NSString * const AFNetworkingTaskDidFinishAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; // Deprecated static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock"; static void * AFTaskStateChangedContext = &AFTaskStateChangedContext; typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error); typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error); typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response); typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location); typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes); typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error); #pragma mark - @interface AFURLSessionManagerTaskDelegate : NSObject @property (nonatomic, weak) AFURLSessionManager *manager; @property (nonatomic, strong) id responseSerializer; @property (nonatomic, strong) NSMutableData *mutableData; @property (nonatomic, strong) NSProgress *uploadProgress; @property (nonatomic, strong) NSProgress *downloadProgress; @property (nonatomic, copy) NSURL *downloadFileURL; @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; @property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; + (instancetype)delegateForManager:(AFURLSessionManager *)manager completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; @end @implementation AFURLSessionManagerTaskDelegate + (instancetype)delegateForManager:(AFURLSessionManager *)manager completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { AFURLSessionManagerTaskDelegate *delegate = [[self alloc] init]; delegate.manager = manager; delegate.responseSerializer = manager.responseSerializer; delegate.completionHandler = completionHandler; return delegate; } - (instancetype)init { self = [super init]; if (!self) { return nil; } self.mutableData = [NSMutableData data]; self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; return self; } #pragma mark - NSURLSessionTaskDelegate - (void)URLSession:(__unused NSURLSession *)session task:(__unused NSURLSessionTask *)task didSendBodyData:(__unused int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { int64_t totalUnitCount = totalBytesExpectedToSend; if(totalUnitCount == NSURLSessionTransferSizeUnknown) { NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; if(contentLength) { totalUnitCount = (int64_t) [contentLength longLongValue]; } } self.uploadProgress.totalUnitCount = totalUnitCount; self.uploadProgress.completedUnitCount = totalBytesSent; } - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" __strong AFURLSessionManager *manager = self.manager; __block id responseObject = nil; __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = self.responseSerializer; if (self.downloadFileURL) { userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL; } else if (self.mutableData) { userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = [NSData dataWithData:self.mutableData]; } if (error) { userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ if (self.completionHandler) { self.completionHandler(task.response, responseObject, error); } dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); }); } else { dispatch_async(url_session_manager_processing_queue(), ^{ NSError *serializationError = nil; responseObject = [self.responseSerializer responseObjectForResponse:task.response data:[NSData dataWithData:self.mutableData] error:&serializationError]; if (self.downloadFileURL) { responseObject = self.downloadFileURL; } if (responseObject) { userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; } if (serializationError) { userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; } dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ if (self.completionHandler) { self.completionHandler(task.response, responseObject, serializationError); } dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); }); }); } #pragma clang diagnostic pop } #pragma mark - NSURLSessionDataTaskDelegate - (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [self.mutableData appendData:data]; self.downloadProgress.completedUnitCount += (int64_t)[data length]; } #pragma mark - NSURLSessionDownloadTaskDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSError *fileManagerError = nil; self.downloadFileURL = nil; if (self.downloadTaskDidFinishDownloading) { self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); if (self.downloadFileURL) { [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]; if (fileManagerError) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; } } } } - (void)URLSession:(__unused NSURLSession *)session downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask didWriteData:(__unused int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite; self.downloadProgress.completedUnitCount = totalBytesWritten; } - (void)URLSession:(__unused NSURLSession *)session downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { self.downloadProgress.totalUnitCount = expectedTotalBytes; self.downloadProgress.completedUnitCount = fileOffset; } @end #pragma mark - @interface AFURLSessionManager () @property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; @property (readwrite, nonatomic, strong) NSURLSession *session; @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; @property (readwrite, nonatomic, strong) NSLock *lock; @property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; @property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; @property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; @property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; @property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; @property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; @property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete; @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; @property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; @property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession; @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; @end @implementation AFURLSessionManager - (instancetype)init { return [self initWithSessionConfiguration:nil]; } - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { self = [super init]; if (!self) { return nil; } if (!configuration) { configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; } self.operationQueue = [[NSOperationQueue alloc] init]; self.operationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; self.responseSerializer = [AFJSONResponseSerializer serializer]; self.sessionConfiguration = configuration; self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; self.securityPolicy = [AFSecurityPolicy defaultPolicy]; self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; self.lock = [[NSLock alloc] init]; self.lock.name = AFURLSessionManagerLockName; return self; } - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue]; } #pragma mark - - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task { NSParameterAssert(task); AFURLSessionManagerTaskDelegate *delegate = nil; [self.lock lock]; delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; [self.lock unlock]; return delegate; } - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task { NSParameterAssert(task); NSParameterAssert(delegate); [self.lock lock]; [task addObserver:self forKeyPath:NSStringFromSelector(@selector(state)) options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:AFTaskStateChangedContext]; self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; [self.lock unlock]; } - (void)removeDelegateForTask:(NSURLSessionTask *)task { NSParameterAssert(task); [self.lock lock]; [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(state)) context:AFTaskStateChangedContext]; [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; [self.lock unlock]; } - (void)removeAllDelegates { [self.lock lock]; [self.mutableTaskDelegatesKeyedByTaskIdentifier removeAllObjects]; [self.lock unlock]; } #pragma mark - - (NSArray *)tasksForKeyPath:(NSString *)keyPath { __block NSArray *tasks = nil; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) { tasks = dataTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) { tasks = uploadTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) { tasks = downloadTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) { tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; } dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); return tasks; } - (NSArray *)tasks { return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; } - (NSArray *)dataTasks { return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; } - (NSArray *)uploadTasks { return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; } - (NSArray *)downloadTasks { return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; } #pragma mark - - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks { if (cancelPendingTasks) { [self.session invalidateAndCancel]; } else { [self.session finishTasksAndInvalidate]; } } #pragma mark - - (void)setResponseSerializer:(id )responseSerializer { NSParameterAssert(responseSerializer); _responseSerializer = responseSerializer; } #pragma mark - - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request]; AFURLSessionManagerTaskDelegate *delegate = [AFURLSessionManagerTaskDelegate delegateForManager:self completionHandler:completionHandler]; [self setDelegate:delegate forTask:dataTask]; return dataTask; } #pragma mark - - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; return [self uploadTaskWithTask:uploadTask progress:progress completionHandler:completionHandler]; } - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; return [self uploadTaskWithTask:uploadTask progress:progress completionHandler:completionHandler]; } - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request]; return [self uploadTaskWithTask:uploadTask progress:progress completionHandler:completionHandler]; } - (NSURLSessionUploadTask *)uploadTaskWithTask:(NSURLSessionUploadTask *)uploadTask progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { NSParameterAssert(uploadTask); AFURLSessionManagerTaskDelegate *delegate = [AFURLSessionManagerTaskDelegate delegateForManager:self completionHandler:completionHandler]; int64_t totalUnitCount = uploadTask.countOfBytesExpectedToSend; if(totalUnitCount == NSURLSessionTransferSizeUnknown) { NSString *contentLength = [uploadTask.originalRequest valueForHTTPHeaderField:@"Content-Length"]; if(contentLength) { totalUnitCount = (int64_t) [contentLength longLongValue]; } } delegate.uploadProgress = [NSProgress progressWithTotalUnitCount:totalUnitCount]; delegate.uploadProgress.pausingHandler = ^{ [uploadTask suspend]; }; delegate.uploadProgress.cancellationHandler = ^{ [uploadTask cancel]; }; if (progress) { *progress = delegate.uploadProgress; } [self setDelegate:delegate forTask:uploadTask]; return uploadTask; } #pragma mark - - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request progress:(NSProgress * __autoreleasing *)progress destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler { NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request]; return [self downloadTaskWithTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; } - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData progress:(NSProgress * __autoreleasing *)progress destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler { NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData]; return [self downloadTaskWithTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; } - (NSURLSessionDownloadTask *)downloadTaskWithTask:(NSURLSessionDownloadTask *)downloadTask progress:(NSProgress * __autoreleasing *)progress destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler { NSParameterAssert(downloadTask); AFURLSessionManagerTaskDelegate *delegate = [AFURLSessionManagerTaskDelegate delegateForManager:self completionHandler:completionHandler]; delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { if (destination) { return destination(location, task.response); } return location; }; if (progress) { *progress = delegate.downloadProgress; } [self setDelegate:delegate forTask:downloadTask]; return downloadTask; } #pragma mark - - (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block { self.sessionDidBecomeInvalid = block; } - (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { self.sessionDidReceiveAuthenticationChallenge = block; } #pragma mark - - (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block { self.taskNeedNewBodyStream = block; } - (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block { self.taskWillPerformHTTPRedirection = block; } - (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { self.taskDidReceiveAuthenticationChallenge = block; } - (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block { self.taskDidSendBodyData = block; } - (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block { self.taskDidComplete = block; } #pragma mark - - (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block { self.dataTaskDidReceiveResponse = block; } - (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block { self.dataTaskDidBecomeDownloadTask = block; } - (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block { self.dataTaskDidReceiveData = block; } - (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block { self.dataTaskWillCacheResponse = block; } - (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { self.didFinishEventsForBackgroundURLSession = block; } #pragma mark - - (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block { self.downloadTaskDidFinishDownloading = block; } - (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block { self.downloadTaskDidWriteData = block; } - (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block { self.downloadTaskDidResume = block; } #pragma mark - NSURLSessionDelegate - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { if (self.sessionDidBecomeInvalid) { self.sessionDidBecomeInvalid(session, error); } [self removeAllDelegates]; [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; } - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; __block NSURLCredential *credential = nil; if (self.sessionDidReceiveAuthenticationChallenge) { disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); } else { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; if (credential) { disposition = NSURLSessionAuthChallengeUseCredential; } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; } } else { disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; } } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; } } if (completionHandler) { completionHandler(disposition, credential); } } #pragma mark - NSURLSessionTaskDelegate - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler { NSURLRequest *redirectRequest = request; if (self.taskWillPerformHTTPRedirection) { redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); } if (completionHandler) { completionHandler(redirectRequest); } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; __block NSURLCredential *credential = nil; if (self.taskDidReceiveAuthenticationChallenge) { disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); } else { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { disposition = NSURLSessionAuthChallengeUseCredential; credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; } else { disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; } } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; } } if (completionHandler) { completionHandler(disposition, credential); } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler { NSInputStream *inputStream = nil; if (self.taskNeedNewBodyStream) { inputStream = self.taskNeedNewBodyStream(session, task); } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { inputStream = [task.originalRequest.HTTPBodyStream copy]; } if (completionHandler) { completionHandler(inputStream); } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend]; if (self.taskDidSendBodyData) { self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend); } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; // delegate may be nil when completing a task in the background if (delegate) { [delegate URLSession:session task:task didCompleteWithError:error]; if (self.taskDidComplete) { self.taskDidComplete(session, task, error); } [self removeDelegateForTask:task]; } } #pragma mark - NSURLSessionDataDelegate - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; if (self.dataTaskDidReceiveResponse) { disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); } if (completionHandler) { completionHandler(disposition); } } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; if (delegate) { [self removeDelegateForTask:dataTask]; [self setDelegate:delegate forTask:downloadTask]; } if (self.dataTaskDidBecomeDownloadTask) { self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); } } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; [delegate URLSession:session dataTask:dataTask didReceiveData:data]; if (self.dataTaskDidReceiveData) { self.dataTaskDidReceiveData(session, dataTask, data); } } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { NSCachedURLResponse *cachedResponse = proposedResponse; if (self.dataTaskWillCacheResponse) { cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse); } if (completionHandler) { completionHandler(cachedResponse); } } - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { if (self.didFinishEventsForBackgroundURLSession) { self.didFinishEventsForBackgroundURLSession(session); } } #pragma mark - NSURLSessionDownloadDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; if (delegate) { [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; } else if (self.downloadTaskDidFinishDownloading) { NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); if (fileURL) { NSError *error = nil; [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; if (error) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; } } } } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; if (self.downloadTaskDidWriteData) { self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); } } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { if (self.downloadTaskDidResume) { self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); } } #pragma mark - NSKeyValueObserving - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (context == AFTaskStateChangedContext && [keyPath isEqualToString:@"state"]) { NSString *notificationName = nil; switch ([(NSURLSessionTask *)object state]) { case NSURLSessionTaskStateRunning: notificationName = AFNetworkingTaskDidResumeNotification; break; case NSURLSessionTaskStateSuspended: notificationName = AFNetworkingTaskDidSuspendNotification; break; case NSURLSessionTaskStateCompleted: // AFNetworkingTaskDidFinishNotification posted by task completion handlers default: break; } if (notificationName) { dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:object]; }); } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { NSURLSessionConfiguration *configuration = [decoder decodeObjectForKey:@"sessionConfiguration"]; self = [self initWithSessionConfiguration:configuration]; if (!self) { return nil; } return self; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration]; } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/Reachability/AFNetworkReachabilityManager.h ================================================ // AFNetworkReachabilityManager.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #import #import #import #import #import typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { AFNetworkReachabilityStatusUnknown = -1, AFNetworkReachabilityStatusNotReachable = 0, AFNetworkReachabilityStatusReachableViaWWAN = 1, AFNetworkReachabilityStatusReachableViaWiFi = 2, }; /** `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. See Apple's Reachability Sample Code (https://developer.apple.com/library/ios/samplecode/reachability/) @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. */ @interface AFNetworkReachabilityManager : NSObject /** The current network reachability status. */ @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; /** Whether or not the network is currently reachable. */ @property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable; /** Whether or not the network is currently reachable via WWAN. */ @property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN; /** Whether or not the network is currently reachable via WiFi. */ @property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; ///--------------------- /// @name Initialization ///--------------------- /** Returns the shared network reachability manager. */ + (instancetype)sharedManager; /** Creates and returns a network reachability manager for the specified domain. @param domain The domain used to evaluate network reachability. @return An initialized network reachability manager, actively monitoring the specified domain. */ + (instancetype)managerForDomain:(NSString *)domain; /** Creates and returns a network reachability manager for the socket address. @param address The socket address used to evaluate network reachability. @return An initialized network reachability manager, actively monitoring the specified socket address. */ + (instancetype)managerForAddress:(const struct sockaddr_in *)address; /** Initializes an instance of a network reachability manager from the specified reachability object. @param reachability The reachability object to monitor. @return An initialized network reachability manager, actively monitoring the specified reachability. */ - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability; ///-------------------------------------------------- /// @name Starting & Stopping Reachability Monitoring ///-------------------------------------------------- /** Starts monitoring for changes in network reachability status. */ - (void)startMonitoring; /** Stops monitoring for changes in network reachability status. */ - (void)stopMonitoring; ///------------------------------------------------- /// @name Getting Localized Reachability Description ///------------------------------------------------- /** Returns a localized string representation of the current network reachability status. */ - (NSString *)localizedNetworkReachabilityStatusString; ///--------------------------------------------------- /// @name Setting Network Reachability Change Callback ///--------------------------------------------------- /** Sets a callback to be executed when the network availability of the `baseURL` host changes. @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`. */ - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block; @end ///---------------- /// @name Constants ///---------------- /** ## Network Reachability The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses. enum { AFNetworkReachabilityStatusUnknown, AFNetworkReachabilityStatusNotReachable, AFNetworkReachabilityStatusReachableViaWWAN, AFNetworkReachabilityStatusReachableViaWiFi, } `AFNetworkReachabilityStatusUnknown` The `baseURL` host reachability is not known. `AFNetworkReachabilityStatusNotReachable` The `baseURL` host cannot be reached. `AFNetworkReachabilityStatusReachableViaWWAN` The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS. `AFNetworkReachabilityStatusReachableViaWiFi` The `baseURL` host can be reached via a Wi-Fi connection. ### Keys for Notification UserInfo Dictionary Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification. `AFNetworkingReachabilityNotificationStatusItem` A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. */ ///-------------------- /// @name Notifications ///-------------------- /** Posted when network reachability changes. This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability. @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (`Prefix.pch`). */ extern NSString * const AFNetworkingReachabilityDidChangeNotification; extern NSString * const AFNetworkingReachabilityNotificationStatusItem; ///-------------------- /// @name Functions ///-------------------- /** Returns a localized string representation of an `AFNetworkReachabilityStatus` value. */ extern NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status); ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/Reachability/AFNetworkReachabilityManager.m ================================================ // AFNetworkReachabilityManager.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFNetworkReachabilityManager.h" NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem"; typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status); typedef NS_ENUM(NSUInteger, AFNetworkReachabilityAssociation) { AFNetworkReachabilityForAddress = 1, AFNetworkReachabilityForAddressPair = 2, AFNetworkReachabilityForName = 3, }; NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) { switch (status) { case AFNetworkReachabilityStatusNotReachable: return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil); case AFNetworkReachabilityStatusReachableViaWWAN: return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil); case AFNetworkReachabilityStatusReachableViaWiFi: return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil); case AFNetworkReachabilityStatusUnknown: default: return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil); } } static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; if (isNetworkReachable == NO) { status = AFNetworkReachabilityStatusNotReachable; } #if TARGET_OS_IPHONE else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { status = AFNetworkReachabilityStatusReachableViaWWAN; } #endif else { status = AFNetworkReachabilityStatusReachableViaWiFi; } return status; } static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); AFNetworkReachabilityStatusBlock block = (__bridge AFNetworkReachabilityStatusBlock)info; if (block) { block(status); } dispatch_async(dispatch_get_main_queue(), ^{ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }]; }); } static const void * AFNetworkReachabilityRetainCallback(const void *info) { return Block_copy(info); } static void AFNetworkReachabilityReleaseCallback(const void *info) { if (info) { Block_release(info); } } @interface AFNetworkReachabilityManager () @property (readwrite, nonatomic, assign) SCNetworkReachabilityRef networkReachability; @property (readwrite, nonatomic, assign) AFNetworkReachabilityAssociation networkReachabilityAssociation; @property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; @property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; @end @implementation AFNetworkReachabilityManager + (instancetype)sharedManager { static AFNetworkReachabilityManager *_sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_len = sizeof(address); address.sin_family = AF_INET; _sharedManager = [self managerForAddress:&address]; }); return _sharedManager; } + (instancetype)managerForDomain:(NSString *)domain { SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; manager.networkReachabilityAssociation = AFNetworkReachabilityForName; return manager; } + (instancetype)managerForAddress:(const struct sockaddr_in *)address { SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; manager.networkReachabilityAssociation = AFNetworkReachabilityForAddress; return manager; } - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { self = [super init]; if (!self) { return nil; } self.networkReachability = reachability; self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; return self; } - (void)dealloc { [self stopMonitoring]; if (_networkReachability) { CFRelease(_networkReachability); _networkReachability = NULL; } } #pragma mark - - (BOOL)isReachable { return [self isReachableViaWWAN] || [self isReachableViaWiFi]; } - (BOOL)isReachableViaWWAN { return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; } - (BOOL)isReachableViaWiFi { return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; } #pragma mark - - (void)startMonitoring { [self stopMonitoring]; if (!self.networkReachability) { return; } __weak __typeof(self)weakSelf = self; AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; if (strongSelf.networkReachabilityStatusBlock) { strongSelf.networkReachabilityStatusBlock(status); } }; SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); switch (self.networkReachabilityAssociation) { case AFNetworkReachabilityForName: break; case AFNetworkReachabilityForAddress: case AFNetworkReachabilityForAddressPair: default: { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; SCNetworkReachabilityGetFlags(self.networkReachability, &flags); AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); dispatch_async(dispatch_get_main_queue(), ^{ callback(status); }); }); } break; } } - (void)stopMonitoring { if (!self.networkReachability) { return; } SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); } #pragma mark - - (NSString *)localizedNetworkReachabilityStatusString { return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus); } #pragma mark - - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { self.networkReachabilityStatusBlock = block; } #pragma mark - NSKeyValueObserving + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { return [NSSet setWithObject:@"networkReachabilityStatus"]; } return [super keyPathsForValuesAffectingValueForKey:key]; } @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/Security/AFSecurityPolicy.h ================================================ // AFSecurity.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { AFSSLPinningModeNone, AFSSLPinningModePublicKey, AFSSLPinningModeCertificate, }; /** `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. */ @interface AFSecurityPolicy : NSObject /** The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. */ @property (nonatomic, assign) AFSSLPinningMode SSLPinningMode; /** Whether to evaluate an entire SSL certificate chain, or just the leaf certificate. Defaults to `YES`. */ @property (nonatomic, assign) BOOL validatesCertificateChain; /** The certificates used to evaluate server trust according to the SSL pinning mode. By default, this property is set to any (`.cer`) certificates included in the app bundle. */ @property (nonatomic, strong) NSArray *pinnedCertificates; /** Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`. */ @property (nonatomic, assign) BOOL allowInvalidCertificates; /** Whether or not to validate the domain name in the certificates CN field. Defaults to `YES` for `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate`, otherwise `NO`. */ @property (nonatomic, assign) BOOL validatesDomainName; ///----------------------------------------- /// @name Getting Specific Security Policies ///----------------------------------------- /** Returns the shared default security policy, which does not accept invalid certificates, and does not validate against pinned certificates or public keys. @return The default security policy. */ + (instancetype)defaultPolicy; ///--------------------- /// @name Initialization ///--------------------- /** Creates and returns a security policy with the specified pinning mode. @param pinningMode The SSL pinning mode. @return A new security policy. */ + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; ///------------------------------ /// @name Evaluating Server Trust ///------------------------------ /** Whether or not the specified server trust should be accepted, based on the security policy. This method should be used when responding to an authentication challenge from a server. @param serverTrust The X.509 certificate trust of the server. @return Whether or not to trust the server. @warning This method has been deprecated in favor of `-evaluateServerTrust:forDomain:`. */ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust DEPRECATED_ATTRIBUTE; /** Whether or not the specified server trust should be accepted, based on the security policy. This method should be used when responding to an authentication challenge from a server. @param serverTrust The X.509 certificate trust of the server. @param domain The domain of serverTrust. If `nil`, the domain will not be validated. @return Whether or not to trust the server. */ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain; @end ///---------------- /// @name Constants ///---------------- /** ## SSL Pinning Modes The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes. enum { AFSSLPinningModeNone, AFSSLPinningModePublicKey, AFSSLPinningModeCertificate, } `AFSSLPinningModeNone` Do not used pinned certificates to validate servers. `AFSSLPinningModePublicKey` Validate host certificates against public keys of pinned certificates. `AFSSLPinningModeCertificate` Validate host certificates against pinned certificates. */ ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/Security/AFSecurityPolicy.m ================================================ // AFSecurity.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFSecurityPolicy.h" #if !defined(__IPHONE_OS_VERSION_MIN_REQUIRED) static NSData * AFSecKeyGetData(SecKeyRef key) { CFDataRef data = NULL; #if defined(NS_BLOCK_ASSERTIONS) SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data); #else OSStatus status = SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data); NSCAssert(status == errSecSuccess, @"SecItemExport error: %ld", (long int)status); #endif NSCParameterAssert(data); return (__bridge_transfer NSData *)data; } #endif static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) return [(__bridge id)key1 isEqual:(__bridge id)key2]; #else return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)]; #endif } static id AFPublicKeyForCertificate(NSData *certificate) { SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); NSCParameterAssert(allowedCertificate); SecCertificateRef allowedCertificates[] = {allowedCertificate}; CFArrayRef tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); SecPolicyRef policy = SecPolicyCreateBasicX509(); SecTrustRef allowedTrust = NULL; #if defined(NS_BLOCK_ASSERTIONS) SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust); #else OSStatus status = SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust); NSCAssert(status == errSecSuccess, @"SecTrustCreateWithCertificates error: %ld", (long int)status); #endif SecTrustResultType result = 0; #if defined(NS_BLOCK_ASSERTIONS) SecTrustEvaluate(allowedTrust, &result); #else status = SecTrustEvaluate(allowedTrust, &result); NSCAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status); #endif SecKeyRef allowedPublicKey = SecTrustCopyPublicKey(allowedTrust); NSCParameterAssert(allowedPublicKey); CFRelease(allowedTrust); CFRelease(policy); CFRelease(tempCertificates); CFRelease(allowedCertificate); return (__bridge_transfer id)allowedPublicKey; } static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { SecTrustResultType result = 0; #if defined(NS_BLOCK_ASSERTIONS) SecTrustEvaluate(serverTrust, &result); #else OSStatus status = SecTrustEvaluate(serverTrust, &result); NSCAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status); #endif return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); } static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; for (CFIndex i = 0; i < certificateCount; i++) { SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; } return [NSArray arrayWithArray:trustChain]; } static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { SecPolicyRef policy = SecPolicyCreateBasicX509(); CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; for (CFIndex i = 0; i < certificateCount; i++) { SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); SecCertificateRef someCertificates[] = {certificate}; CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); SecTrustRef trust = NULL; #if defined(NS_BLOCK_ASSERTIONS) SecTrustCreateWithCertificates(certificates, policy, &trust); SecTrustResultType result; SecTrustEvaluate(trust, &result); #else OSStatus status = SecTrustCreateWithCertificates(certificates, policy, &trust); NSCAssert(status == errSecSuccess, @"SecTrustCreateWithCertificates error: %ld", (long int)status); SecTrustResultType result; status = SecTrustEvaluate(trust, &result); NSCAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status); #endif [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; CFRelease(trust); CFRelease(certificates); } CFRelease(policy); return [NSArray arrayWithArray:trustChain]; } static NSComparisonResult AFDomainComponentCompare(NSString *component1, NSString *component2) { if ([component1 isEqualToString:@"*"] || [component2 isEqualToString:@"*"]) { return NSOrderedSame; } return [component1 compare:component2]; } static BOOL AFCertificateHostMatchesDomain(NSString *certificateHost, NSString *domain) { certificateHost = [certificateHost lowercaseString]; domain = [domain lowercaseString]; if ([certificateHost isEqualToString:domain]) { return YES; } NSArray *certificateHostComponents = [certificateHost componentsSeparatedByString:@"."]; NSArray *domainComponents = [domain componentsSeparatedByString:@"."]; if ([certificateHostComponents count] != [domainComponents count]) { return NO; } BOOL certificateHostMatchesDomain = ([certificateHostComponents indexOfObjectPassingTest:^BOOL(NSString *certificateHostComponent, NSUInteger idx, __unused BOOL *stop) { NSString *domainComponent = [domainComponents objectAtIndex:idx]; return AFDomainComponentCompare(certificateHostComponent, domainComponent) != NSOrderedSame; }] == NSNotFound); return certificateHostMatchesDomain; } #pragma mark - @interface AFSecurityPolicy() @property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys; @end @implementation AFSecurityPolicy + (NSArray *)defaultPinnedCertificates { static NSArray *_defaultPinnedCertificates = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSBundle *bundle = [NSBundle bundleForClass:[self class]]; NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]]; for (NSString *path in paths) { NSData *certificateData = [NSData dataWithContentsOfFile:path]; [certificates addObject:certificateData]; } _defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates]; }); return _defaultPinnedCertificates; } + (instancetype)defaultPolicy { AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = AFSSLPinningModeNone; return securityPolicy; } + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = pinningMode; securityPolicy.validatesDomainName = YES; [securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]]; return securityPolicy; } - (id)init { self = [super init]; if (!self) { return nil; } self.validatesCertificateChain = YES; return self; } #pragma mark - - (void)setPinnedCertificates:(NSArray *)pinnedCertificates { _pinnedCertificates = pinnedCertificates; if (self.pinnedCertificates) { NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:[self.pinnedCertificates count]]; for (NSData *certificate in self.pinnedCertificates) { [mutablePinnedPublicKeys addObject:AFPublicKeyForCertificate(certificate)]; } self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys]; } else { self.pinnedPublicKeys = nil; } } #pragma mark - - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust { return [self evaluateServerTrust:serverTrust forDomain:nil]; } - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { BOOL shouldTrustServer = NO; if (self.SSLPinningMode == AFSSLPinningModeNone && self.allowInvalidCertificates) { return YES; } if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { return NO; } NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); switch (self.SSLPinningMode) { case AFSSLPinningModeNone: return YES; case AFSSLPinningModeCertificate: { if (!self.validatesCertificateChain) { shouldTrustServer = [self.pinnedCertificates containsObject:[serverCertificates firstObject]]; break; } NSUInteger trustedCertificateCount = 0; for (NSData *trustChainCertificate in serverCertificates) { if ([self.pinnedCertificates containsObject:trustChainCertificate]) { trustedCertificateCount++; } } shouldTrustServer = trustedCertificateCount > 0 && ((self.validatesCertificateChain && trustedCertificateCount == [serverCertificates count]) || (!self.validatesCertificateChain && trustedCertificateCount >= 1)); } break; case AFSSLPinningModePublicKey: { NSUInteger trustedPublicKeyCount = 0; NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); if (!self.validatesCertificateChain && [publicKeys count] > 0) { publicKeys = @[[publicKeys firstObject]]; } for (id trustChainPublicKey in publicKeys) { for (id pinnedPublicKey in self.pinnedPublicKeys) { if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { trustedPublicKeyCount += 1; } } } shouldTrustServer = trustedPublicKeyCount > 0 && ((self.validatesCertificateChain && trustedPublicKeyCount == [serverCertificates count]) || (!self.validatesCertificateChain && trustedPublicKeyCount >= 1)); } break; } if (shouldTrustServer && domain && self.validatesDomainName) { NSData *serverCertificate = [serverCertificates firstObject]; SecCertificateRef matchingCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)serverCertificate); NSParameterAssert(matchingCertificate); shouldTrustServer = AFCertificateHostMatchesDomain((__bridge_transfer NSString *)SecCertificateCopySubjectSummary(matchingCertificate), domain); CFRelease(matchingCertificate); } return shouldTrustServer; } #pragma mark - NSKeyValueObserving + (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys { return [NSSet setWithObject:@"pinnedCertificates"]; } @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/Serialization/AFURLRequestSerialization.h ================================================ // AFSerialization.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import #endif /** The `AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary. For example, a JSON request serializer may set the HTTP body of the request to a JSON representation, and set the `Content-Type` HTTP header field value to `application/json`. */ @protocol AFURLRequestSerialization /** Returns a request with the specified parameters encoded into a copy of the original request. @param request The original request. @param parameters The parameters to be encoded. @param error The error that occurred while attempting to encode the request parameters. @return A serialized request. */ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError * __autoreleasing *)error; @end #pragma mark - /** */ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { AFHTTPRequestQueryStringDefaultStyle = 0, }; @protocol AFMultipartFormData; /** `AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior. */ @interface AFHTTPRequestSerializer : NSObject /** The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default. */ @property (nonatomic, assign) NSStringEncoding stringEncoding; /** Whether created requests can use the device’s cellular radio (if present). `YES` by default. @see NSMutableURLRequest -setAllowsCellularAccess: */ @property (nonatomic, assign) BOOL allowsCellularAccess; /** The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default. @see NSMutableURLRequest -setCachePolicy: */ @property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; /** Whether created requests should use the default cookie handling. `YES` by default. @see NSMutableURLRequest -setHTTPShouldHandleCookies: */ @property (nonatomic, assign) BOOL HTTPShouldHandleCookies; /** Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default @see NSMutableURLRequest -setHTTPShouldUsePipelining: */ @property (nonatomic, assign) BOOL HTTPShouldUsePipelining; /** The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default. @see NSMutableURLRequest -setNetworkServiceType: */ @property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType; /** The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds. @see NSMutableURLRequest -setTimeoutInterval: */ @property (nonatomic, assign) NSTimeInterval timeoutInterval; ///--------------------------------------- /// @name Configuring HTTP Request Headers ///--------------------------------------- /** Default HTTP header field values to be applied to serialized requests. */ @property (readonly, nonatomic, strong) NSDictionary *HTTPRequestHeaders; /** Creates and returns a serializer with default configuration. */ + (instancetype)serializer; /** Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header. @param field The HTTP header to set a default value for @param value The value set as default for the specified header, or `nil` */ - (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field; /** Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header. @param username The HTTP basic auth username @param password The HTTP basic auth password */ - (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username password:(NSString *)password; /** Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a token-based authentication value, such as an OAuth access token. This overwrites any existing value for this header. @param token The authentication token */ - (void)setAuthorizationHeaderFieldWithToken:(NSString *)token; /** Clears any existing value for the "Authorization" HTTP header. */ - (void)clearAuthorizationHeader; ///------------------------------------------------------- /// @name Configuring Query String Parameter Serialization ///------------------------------------------------------- /** HTTP methods for which serialized requests will encode parameters as a query string. `GET`, `HEAD`, and `DELETE` by default. */ @property (nonatomic, strong) NSSet *HTTPMethodsEncodingParametersInURI; /** Set the method of query string serialization according to one of the pre-defined styles. @param style The serialization style. @see AFHTTPRequestQueryStringSerializationStyle */ - (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style; /** Set the a custom method of query string serialization according to the specified block. @param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request. */ - (void)setQueryStringSerializationWithBlock:(NSString * (^)(NSURLRequest *request, NSDictionary *parameters, NSError * __autoreleasing *error))block; ///------------------------------- /// @name Creating Request Objects ///------------------------------- /** @deprecated This method has been deprecated. Use -requestWithMethod:URLString:parameters:error: instead. */ - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters DEPRECATED_ATTRIBUTE; /** Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string. If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body. @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`. @param URLString The URL string used to create the request URL. @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body. @param error The error that occured while constructing the request. @return An `NSMutableURLRequest` object. */ - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters error:(NSError * __autoreleasing *)error; /** @deprecated This method has been deprecated. Use -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error: instead. */ - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id formData))block DEPRECATED_ATTRIBUTE; /** Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2 Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream. @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`. @param URLString The URL string used to create the request URL. @param parameters The parameters to be encoded and set in the request HTTP body. @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. @param error The error that occured while constructing the request. @return An `NSMutableURLRequest` object */ - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id formData))block error:(NSError * __autoreleasing *)error; /** Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished. @param request The multipart form request. @param fileURL The file URL to write multipart form contents to. @param handler A handler block to execute. @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request. @see https://github.com/AFNetworking/AFNetworking/issues/1398 */ - (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request writingStreamContentsToFile:(NSURL *)fileURL completionHandler:(void (^)(NSError *error))handler; @end #pragma mark - extern NSUInteger const kAFUploadStream3GSuggestedPacketSize; extern NSTimeInterval const kAFUploadStream3GSuggestedDelay; /** The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`. */ @protocol AFMultipartFormData /** Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary. The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively. @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. @param name The name to be associated with the specified data. This parameter must not be `nil`. @param error If an error occurs, upon return contains an `NSError` object that describes the problem. @return `YES` if the file data was successfully appended, otherwise `NO`. */ - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError * __autoreleasing *)error; /** Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. @param name The name to be associated with the specified data. This parameter must not be `nil`. @param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`. @param mimeType The declared MIME type of the file data. This parameter must not be `nil`. @param error If an error occurs, upon return contains an `NSError` object that describes the problem. @return `YES` if the file data was successfully appended otherwise `NO`. */ - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType error:(NSError * __autoreleasing *)error; /** Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary. @param inputStream The input stream to be appended to the form data @param name The name to be associated with the specified input stream. This parameter must not be `nil`. @param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`. @param length The length of the specified input stream in bytes. @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. */ - (void)appendPartWithInputStream:(NSInputStream *)inputStream name:(NSString *)name fileName:(NSString *)fileName length:(NSUInteger)length mimeType:(NSString *)mimeType; /** Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. @param data The data to be encoded and appended to the form data. @param name The name to be associated with the specified data. This parameter must not be `nil`. @param fileName The filename to be associated with the specified data. This parameter must not be `nil`. @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. */ - (void)appendPartWithFileData:(NSData *)data name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType; /** Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary. @param data The data to be encoded and appended to the form data. @param name The name to be associated with the specified data. This parameter must not be `nil`. */ - (void)appendPartWithFormData:(NSData *)data name:(NSString *)name; /** Appends HTTP headers, followed by the encoded data and the multipart form boundary. @param headers The HTTP headers to be appended to the form data. @param body The data to be encoded and appended to the form data. */ - (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body; /** Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream. When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth. @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb. @param delay Duration of delay each time a packet is read. By default, no delay is set. */ - (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay; @end ///---------------- /// @name Constants ///---------------- /** ## Throttling Bandwidth for HTTP Request Input Streams @see -throttleBandwidthWithPacketSize:delay: `kAFUploadStream3GSuggestedPacketSize` Maximum packet size, in number of bytes. Equal to 16kb. `kAFUploadStream3GSuggestedDelay` Duration of delay each time a packet is read. Equal to 0.2 seconds. */ #pragma mark - @interface AFJSONRequestSerializer : AFHTTPRequestSerializer /** Options for writing the request JSON data from Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONWritingOptions". `0` by default. */ @property (nonatomic, assign) NSJSONWritingOptions writingOptions; /** Creates and returns a JSON serializer with specified reading and writing options. @param writingOptions The specified JSON writing options. */ + (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions; @end @interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer /** The property list format. Possible values are described in "NSPropertyListFormat". */ @property (nonatomic, assign) NSPropertyListFormat format; /** @warning The `writeOptions` property is currently unused. */ @property (nonatomic, assign) NSPropertyListWriteOptions writeOptions; /** Creates and returns a property list serializer with a specified format, read options, and write options. @param format The property list format. @param writeOptions The property list write options. @warning The `writeOptions` property is currently unused. */ + (instancetype)serializerWithFormat:(NSPropertyListFormat)format writeOptions:(NSPropertyListWriteOptions)writeOptions; @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/Serialization/AFURLRequestSerialization.m ================================================ // AFSerialization.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFURLRequestSerialization.h" extern NSString * const AFNetworkingErrorDomain; typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error); static NSString * AFBase64EncodedStringFromString(NSString *string) { NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; NSUInteger length = [data length]; NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; uint8_t *input = (uint8_t *)[data bytes]; uint8_t *output = (uint8_t *)[mutableData mutableBytes]; for (NSUInteger i = 0; i < length; i += 3) { NSUInteger value = 0; for (NSUInteger j = i; j < (i + 3); j++) { value <<= 8; if (j < length) { value |= (0xFF & input[j]); } } static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; NSUInteger idx = (i / 3) * 4; output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F]; output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F]; output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '='; output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '='; } return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding]; } static NSString * const kAFCharactersToBeEscapedInQueryString = @":/?&=;+!@#$()',*"; static NSString * AFPercentEscapedQueryStringKeyFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { static NSString * const kAFCharactersToLeaveUnescapedInQueryStringPairKey = @"[]."; return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescapedInQueryStringPairKey, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding)); } static NSString * AFPercentEscapedQueryStringValueFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding)); } #pragma mark - @interface AFQueryStringPair : NSObject @property (readwrite, nonatomic, strong) id field; @property (readwrite, nonatomic, strong) id value; - (id)initWithField:(id)field value:(id)value; - (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding; @end @implementation AFQueryStringPair - (id)initWithField:(id)field value:(id)value { self = [super init]; if (!self) { return nil; } self.field = field; self.value = value; return self; } - (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding { if (!self.value || [self.value isEqual:[NSNull null]]) { return AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding); } else { return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding), AFPercentEscapedQueryStringValueFromStringWithEncoding([self.value description], stringEncoding)]; } } @end #pragma mark - extern NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary); extern NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value); static NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) { NSMutableArray *mutablePairs = [NSMutableArray array]; for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { [mutablePairs addObject:[pair URLEncodedStringValueWithEncoding:stringEncoding]]; } return [mutablePairs componentsJoinedByString:@"&"]; } NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) { return AFQueryStringPairsFromKeyAndValue(nil, dictionary); } NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) { NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)]; if ([value isKindOfClass:[NSDictionary class]]) { NSDictionary *dictionary = value; // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { id nestedValue = [dictionary objectForKey:nestedKey]; if (nestedValue) { [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; } } } else if ([value isKindOfClass:[NSArray class]]) { NSArray *array = value; for (id nestedValue in array) { [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; } } else if ([value isKindOfClass:[NSSet class]]) { NSSet *set = value; for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)]; } } else { [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]]; } return mutableQueryStringComponents; } #pragma mark - @interface AFStreamingMultipartFormData : NSObject - (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest stringEncoding:(NSStringEncoding)encoding; - (NSMutableURLRequest *)requestByFinalizingMultipartFormData; @end #pragma mark - @interface AFHTTPRequestSerializer () @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders; @property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; @property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization; @end @implementation AFHTTPRequestSerializer + (instancetype)serializer { return [[self alloc] init]; } - (instancetype)init { self = [super init]; if (!self) { return nil; } self.stringEncoding = NSUTF8StringEncoding; self.allowsCellularAccess = YES; self.cachePolicy = NSURLRequestUseProtocolCachePolicy; self.HTTPShouldHandleCookies = YES; self.HTTPShouldUsePipelining = NO; self.networkServiceType = NSURLNetworkServiceTypeDefault; self.timeoutInterval = 60; self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 NSMutableArray *acceptLanguagesComponents = [NSMutableArray array]; [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { float q = 1.0f - (idx * 0.1f); [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]]; *stop = q <= 0.5f; }]; [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"]; NSString *userAgent = nil; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], (__bridge id)CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey) ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] ? [[UIScreen mainScreen] scale] : 1.0f)]; #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]]; #endif #pragma clang diagnostic pop if (userAgent) { if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { NSMutableString *mutableUserAgent = [userAgent mutableCopy]; CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false); userAgent = mutableUserAgent; } [self setValue:userAgent forHTTPHeaderField:@"User-Agent"]; } // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; return self; } #pragma mark - - (NSDictionary *)HTTPRequestHeaders { return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; } - (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { [self.mutableHTTPRequestHeaders setValue:value forKey:field]; } - (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username password:(NSString *)password { NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", username, password]; [self setValue:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)] forHTTPHeaderField:@"Authorization"]; } - (void)setAuthorizationHeaderFieldWithToken:(NSString *)token { [self setValue:[NSString stringWithFormat:@"Token token=\"%@\"", token] forHTTPHeaderField:@"Authorization"]; } - (void)clearAuthorizationHeader { [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"]; } #pragma mark - - (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style { self.queryStringSerializationStyle = style; self.queryStringSerialization = nil; } - (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, NSDictionary *, NSError *__autoreleasing *))block { self.queryStringSerialization = block; } #pragma mark - - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters { return [self requestWithMethod:method URLString:URLString parameters:parameters error:nil]; } - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing *)error { NSParameterAssert(method); NSParameterAssert(URLString); NSURL *url = [NSURL URLWithString:URLString]; NSParameterAssert(url); NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; mutableRequest.HTTPMethod = method; mutableRequest.allowsCellularAccess = self.allowsCellularAccess; mutableRequest.cachePolicy = self.cachePolicy; mutableRequest.HTTPShouldHandleCookies = self.HTTPShouldHandleCookies; mutableRequest.HTTPShouldUsePipelining = self.HTTPShouldUsePipelining; mutableRequest.networkServiceType = self.networkServiceType; mutableRequest.timeoutInterval = self.timeoutInterval; mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]; return mutableRequest; } - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id formData))block { return [self multipartFormRequestWithMethod:method URLString:URLString parameters:parameters constructingBodyWithBlock:block error:nil]; } - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id formData))block error:(NSError *__autoreleasing *)error { NSParameterAssert(method); NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]); NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error]; __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding]; if (parameters) { for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { NSData *data = nil; if ([pair.value isKindOfClass:[NSData class]]) { data = pair.value; } else if ([pair.value isEqual:[NSNull null]]) { data = [NSData data]; } else { data = [[pair.value description] dataUsingEncoding:self.stringEncoding]; } if (data) { [formData appendPartWithFormData:data name:[pair.field description]]; } } } if (block) { block(formData); } return [formData requestByFinalizingMultipartFormData]; } - (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request writingStreamContentsToFile:(NSURL *)fileURL completionHandler:(void (^)(NSError *error))handler; { if (!request.HTTPBodyStream) { return [request mutableCopy]; } NSParameterAssert([fileURL isFileURL]); NSInputStream *inputStream = request.HTTPBodyStream; NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO]; __block NSError *error = nil; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inputStream open]; [outputStream open]; while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) { uint8_t buffer[1024]; NSInteger bytesRead = [inputStream read:buffer maxLength:1024]; if (inputStream.streamError || bytesRead < 0) { error = inputStream.streamError; break; } NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead]; if (outputStream.streamError || bytesWritten < 0) { error = outputStream.streamError; break; } if (bytesRead == 0 && bytesWritten == 0) { break; } } [outputStream close]; [inputStream close]; if (handler) { dispatch_async(dispatch_get_main_queue(), ^{ handler(error); }); } }); NSMutableURLRequest *mutableRequest = [request mutableCopy]; mutableRequest.HTTPBodyStream = nil; return mutableRequest; } #pragma mark - AFURLRequestSerialization - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error { NSParameterAssert(request); NSMutableURLRequest *mutableRequest = [request mutableCopy]; [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { if (![request valueForHTTPHeaderField:field]) { [mutableRequest setValue:value forHTTPHeaderField:field]; } }]; if (!parameters) { return mutableRequest; } NSString *query = nil; if (self.queryStringSerialization) { query = self.queryStringSerialization(request, parameters, error); } else { switch (self.queryStringSerializationStyle) { case AFHTTPRequestQueryStringDefaultStyle: query = AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding); break; } } if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; } else { NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(self.stringEncoding)); [mutableRequest setValue:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; } return mutableRequest; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [self init]; if (!self) { return nil; } self.mutableHTTPRequestHeaders = [decoder decodeObjectForKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))]; self.queryStringSerializationStyle = (AFHTTPRequestQueryStringSerializationStyle)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(queryStringSerializationStyle))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))]; [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone]; serializer.queryStringSerializationStyle = self.queryStringSerializationStyle; serializer.queryStringSerialization = self.queryStringSerialization; return serializer; } @end #pragma mark - static NSString * AFCreateMultipartFormBoundary() { return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()]; } static NSString * const kAFMultipartFormCRLF = @"\r\n"; static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) { return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF]; } static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) { return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; } static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) { return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; } static inline NSString * AFContentTypeForPathExtension(NSString *extension) { #ifdef __UTTYPE__ NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); if (!contentType) { return @"application/octet-stream"; } else { return contentType; } #else #pragma unused (extension) return @"application/octet-stream"; #endif } NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16; NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2; @interface AFHTTPBodyPart : NSObject @property (nonatomic, assign) NSStringEncoding stringEncoding; @property (nonatomic, strong) NSDictionary *headers; @property (nonatomic, copy) NSString *boundary; @property (nonatomic, strong) id body; @property (nonatomic, assign) NSUInteger bodyContentLength; @property (nonatomic, strong) NSInputStream *inputStream; @property (nonatomic, assign) BOOL hasInitialBoundary; @property (nonatomic, assign) BOOL hasFinalBoundary; @property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable; @property (readonly, nonatomic, assign) NSUInteger contentLength; - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length; @end @interface AFMultipartBodyStream : NSInputStream @property (nonatomic, assign) NSUInteger numberOfBytesInPacket; @property (nonatomic, assign) NSTimeInterval delay; @property (nonatomic, strong) NSInputStream *inputStream; @property (nonatomic, readonly, assign) NSUInteger contentLength; @property (nonatomic, readonly, assign, getter = isEmpty) BOOL empty; - (id)initWithStringEncoding:(NSStringEncoding)encoding; - (void)setInitialAndFinalBoundaries; - (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart; @end #pragma mark - @interface AFStreamingMultipartFormData () @property (readwrite, nonatomic, copy) NSMutableURLRequest *request; @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; @property (readwrite, nonatomic, copy) NSString *boundary; @property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream; @end @implementation AFStreamingMultipartFormData - (id)initWithURLRequest:(NSMutableURLRequest *)urlRequest stringEncoding:(NSStringEncoding)encoding { self = [super init]; if (!self) { return nil; } self.request = urlRequest; self.stringEncoding = encoding; self.boundary = AFCreateMultipartFormBoundary(); self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; return self; } - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError * __autoreleasing *)error { NSParameterAssert(fileURL); NSParameterAssert(name); NSString *fileName = [fileURL lastPathComponent]; NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]); return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error]; } - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType error:(NSError * __autoreleasing *)error { NSParameterAssert(fileURL); NSParameterAssert(name); NSParameterAssert(fileName); NSParameterAssert(mimeType); if (![fileURL isFileURL]) { NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil) forKey:NSLocalizedFailureReasonErrorKey]; if (error) { *error = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; } return NO; } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) { NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil) forKey:NSLocalizedFailureReasonErrorKey]; if (error) { *error = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; } return NO; } NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error]; if (!fileAttributes) { return NO; } NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = mutableHeaders; bodyPart.boundary = self.boundary; bodyPart.body = fileURL; bodyPart.bodyContentLength = [[fileAttributes objectForKey:NSFileSize] unsignedIntegerValue]; [self.bodyStream appendHTTPBodyPart:bodyPart]; return YES; } - (void)appendPartWithInputStream:(NSInputStream *)inputStream name:(NSString *)name fileName:(NSString *)fileName length:(NSUInteger)length mimeType:(NSString *)mimeType { NSParameterAssert(name); NSParameterAssert(fileName); NSParameterAssert(mimeType); NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = mutableHeaders; bodyPart.boundary = self.boundary; bodyPart.body = inputStream; bodyPart.bodyContentLength = length; [self.bodyStream appendHTTPBodyPart:bodyPart]; } - (void)appendPartWithFileData:(NSData *)data name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType { NSParameterAssert(name); NSParameterAssert(fileName); NSParameterAssert(mimeType); NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; [self appendPartWithHeaders:mutableHeaders body:data]; } - (void)appendPartWithFormData:(NSData *)data name:(NSString *)name { NSParameterAssert(name); NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]; [self appendPartWithHeaders:mutableHeaders body:data]; } - (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body { NSParameterAssert(body); AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = headers; bodyPart.boundary = self.boundary; bodyPart.bodyContentLength = [body length]; bodyPart.body = body; [self.bodyStream appendHTTPBodyPart:bodyPart]; } - (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay { self.bodyStream.numberOfBytesInPacket = numberOfBytes; self.bodyStream.delay = delay; } - (NSMutableURLRequest *)requestByFinalizingMultipartFormData { if ([self.bodyStream isEmpty]) { return self.request; } // Reset the initial and final boundaries to ensure correct Content-Length [self.bodyStream setInitialAndFinalBoundaries]; [self.request setHTTPBodyStream:self.bodyStream]; [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"]; [self.request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"]; return self.request; } @end #pragma mark - @interface AFMultipartBodyStream () @property (readwrite, nonatomic, assign) NSStreamStatus streamStatus; @property (readwrite, nonatomic, strong) NSError *streamError; @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; @property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts; @property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator; @property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart; @property (readwrite, nonatomic, strong) NSOutputStream *outputStream; @property (readwrite, nonatomic, strong) NSMutableData *buffer; @end @implementation AFMultipartBodyStream - (id)initWithStringEncoding:(NSStringEncoding)encoding { self = [super init]; if (!self) { return nil; } self.stringEncoding = encoding; self.HTTPBodyParts = [NSMutableArray array]; self.numberOfBytesInPacket = NSIntegerMax; return self; } - (void)setInitialAndFinalBoundaries { if ([self.HTTPBodyParts count] > 0) { for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { bodyPart.hasInitialBoundary = NO; bodyPart.hasFinalBoundary = NO; } [[self.HTTPBodyParts objectAtIndex:0] setHasInitialBoundary:YES]; [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES]; } } - (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart { [self.HTTPBodyParts addObject:bodyPart]; } - (BOOL)isEmpty { return [self.HTTPBodyParts count] == 0; } #pragma mark - NSInputStream - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length { if ([self streamStatus] == NSStreamStatusClosed) { return 0; } NSInteger totalNumberOfBytesRead = 0; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) { if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) { if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) { break; } } else { NSUInteger maxLength = length - (NSUInteger)totalNumberOfBytesRead; NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength]; if (numberOfBytesRead == -1) { self.streamError = self.currentHTTPBodyPart.inputStream.streamError; break; } else { totalNumberOfBytesRead += numberOfBytesRead; if (self.delay > 0.0f) { [NSThread sleepForTimeInterval:self.delay]; } } } } #pragma clang diagnostic pop return totalNumberOfBytesRead; } - (BOOL)getBuffer:(__unused uint8_t **)buffer length:(__unused NSUInteger *)len { return NO; } - (BOOL)hasBytesAvailable { return [self streamStatus] == NSStreamStatusOpen; } #pragma mark - NSStream - (void)open { if (self.streamStatus == NSStreamStatusOpen) { return; } self.streamStatus = NSStreamStatusOpen; [self setInitialAndFinalBoundaries]; self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator]; } - (void)close { self.streamStatus = NSStreamStatusClosed; } - (id)propertyForKey:(__unused NSString *)key { return nil; } - (BOOL)setProperty:(__unused id)property forKey:(__unused NSString *)key { return NO; } - (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop forMode:(__unused NSString *)mode {} - (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop forMode:(__unused NSString *)mode {} - (NSUInteger)contentLength { NSUInteger length = 0; for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { length += [bodyPart contentLength]; } return length; } #pragma mark - Undocumented CFReadStream Bridged Methods - (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop forMode:(__unused CFStringRef)aMode {} - (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop forMode:(__unused CFStringRef)aMode {} - (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags callback:(__unused CFReadStreamClientCallBack)inCallback context:(__unused CFStreamClientContext *)inContext { return NO; } #pragma mark - NSCopying -(id)copyWithZone:(NSZone *)zone { AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding]; for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]]; } [bodyStreamCopy setInitialAndFinalBoundaries]; return bodyStreamCopy; } @end #pragma mark - typedef enum { AFEncapsulationBoundaryPhase = 1, AFHeaderPhase = 2, AFBodyPhase = 3, AFFinalBoundaryPhase = 4, } AFHTTPBodyPartReadPhase; @interface AFHTTPBodyPart () { AFHTTPBodyPartReadPhase _phase; NSInputStream *_inputStream; NSUInteger _phaseReadOffset; } - (BOOL)transitionToNextPhase; - (NSInteger)readData:(NSData *)data intoBuffer:(uint8_t *)buffer maxLength:(NSUInteger)length; @end @implementation AFHTTPBodyPart - (id)init { self = [super init]; if (!self) { return nil; } [self transitionToNextPhase]; return self; } - (void)dealloc { if (_inputStream) { [_inputStream close]; _inputStream = nil; } } - (NSInputStream *)inputStream { if (!_inputStream) { if ([self.body isKindOfClass:[NSData class]]) { _inputStream = [NSInputStream inputStreamWithData:self.body]; } else if ([self.body isKindOfClass:[NSURL class]]) { _inputStream = [NSInputStream inputStreamWithURL:self.body]; } else if ([self.body isKindOfClass:[NSInputStream class]]) { _inputStream = self.body; } } return _inputStream; } - (NSString *)stringForHeaders { NSMutableString *headerString = [NSMutableString string]; for (NSString *field in [self.headers allKeys]) { [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]]; } [headerString appendString:kAFMultipartFormCRLF]; return [NSString stringWithString:headerString]; } - (NSUInteger)contentLength { NSUInteger length = 0; NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; length += [encapsulationBoundaryData length]; NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; length += [headersData length]; length += _bodyContentLength; NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); length += [closingBoundaryData length]; return length; } - (BOOL)hasBytesAvailable { // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer if (_phase == AFFinalBoundaryPhase) { return YES; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcovered-switch-default" switch (self.inputStream.streamStatus) { case NSStreamStatusNotOpen: case NSStreamStatusOpening: case NSStreamStatusOpen: case NSStreamStatusReading: case NSStreamStatusWriting: return YES; case NSStreamStatusAtEnd: case NSStreamStatusClosed: case NSStreamStatusError: default: return NO; } #pragma clang diagnostic pop } - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length { NSInteger totalNumberOfBytesRead = 0; if (_phase == AFEncapsulationBoundaryPhase) { NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; } if (_phase == AFHeaderPhase) { NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; } if (_phase == AFBodyPhase) { NSInteger numberOfBytesRead = 0; numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; if (numberOfBytesRead == -1) { return -1; } else { totalNumberOfBytesRead += numberOfBytesRead; if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) { [self transitionToNextPhase]; } } } if (_phase == AFFinalBoundaryPhase) { NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; } return totalNumberOfBytesRead; } - (NSInteger)readData:(NSData *)data intoBuffer:(uint8_t *)buffer maxLength:(NSUInteger)length { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" NSRange range = NSMakeRange(_phaseReadOffset, MIN([data length] - _phaseReadOffset, length)); [data getBytes:buffer range:range]; #pragma clang diagnostic pop _phaseReadOffset += range.length; if (_phaseReadOffset >= [data length]) { [self transitionToNextPhase]; } return (NSInteger)range.length; } - (BOOL)transitionToNextPhase { if (![[NSThread currentThread] isMainThread]) { [self performSelectorOnMainThread:@selector(transitionToNextPhase) withObject:nil waitUntilDone:YES]; return YES; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcovered-switch-default" switch (_phase) { case AFEncapsulationBoundaryPhase: _phase = AFHeaderPhase; break; case AFHeaderPhase: [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; [self.inputStream open]; _phase = AFBodyPhase; break; case AFBodyPhase: [self.inputStream close]; _phase = AFFinalBoundaryPhase; break; case AFFinalBoundaryPhase: default: _phase = AFEncapsulationBoundaryPhase; break; } _phaseReadOffset = 0; #pragma clang diagnostic pop return YES; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init]; bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = self.headers; bodyPart.bodyContentLength = self.bodyContentLength; bodyPart.body = self.body; return bodyPart; } @end #pragma mark - @implementation AFJSONRequestSerializer + (instancetype)serializer { return [self serializerWithWritingOptions:0]; } + (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions { AFJSONRequestSerializer *serializer = [[self alloc] init]; serializer.writingOptions = writingOptions; return serializer; } #pragma mark - AFURLRequestSerialization - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error { NSParameterAssert(request); if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { return [super requestBySerializingRequest:request withParameters:parameters error:error]; } NSMutableURLRequest *mutableRequest = [request mutableCopy]; [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { if (![request valueForHTTPHeaderField:field]) { [mutableRequest setValue:value forHTTPHeaderField:field]; } }]; if (!parameters) { return mutableRequest; } NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); [mutableRequest setValue:[NSString stringWithFormat:@"application/json; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]]; return mutableRequest; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) { return nil; } self.writingOptions = (NSJSONWritingOptions)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(writingOptions))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeInteger:self.writingOptions forKey:NSStringFromSelector(@selector(writingOptions))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFJSONRequestSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.writingOptions = self.writingOptions; return serializer; } @end #pragma mark - @implementation AFPropertyListRequestSerializer + (instancetype)serializer { return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0]; } + (instancetype)serializerWithFormat:(NSPropertyListFormat)format writeOptions:(NSPropertyListWriteOptions)writeOptions { AFPropertyListRequestSerializer *serializer = [[self alloc] init]; serializer.format = format; serializer.writeOptions = writeOptions; return serializer; } #pragma mark - AFURLRequestSerializer - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error { NSParameterAssert(request); if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { return [super requestBySerializingRequest:request withParameters:parameters error:error]; } NSMutableURLRequest *mutableRequest = [request mutableCopy]; [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { if (![request valueForHTTPHeaderField:field]) { [mutableRequest setValue:value forHTTPHeaderField:field]; } }]; NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); [mutableRequest setValue:[NSString stringWithFormat:@"application/x-plist; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]]; return mutableRequest; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) { return nil; } self.format = (NSPropertyListFormat)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(format))]; self.writeOptions = (NSPropertyListWriteOptions)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(writeOptions))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeInteger:self.format forKey:NSStringFromSelector(@selector(format))]; [coder encodeInteger:(NSInteger)self.writeOptions forKey:NSStringFromSelector(@selector(writeOptions))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFPropertyListRequestSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.format = self.format; serializer.writeOptions = self.writeOptions; return serializer; } @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/Serialization/AFURLResponseSerialization.h ================================================ // AFSerialization.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import /** The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object. */ @protocol AFURLResponseSerialization /** The response object decoded from the data associated with a specified response. @param response The response to be processed. @param data The response data to be decoded. @param error The error that occurred while attempting to decode the response data. @return The object decoded from the specified response data. */ - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error; @end #pragma mark - /** `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior. */ @interface AFHTTPResponseSerializer : NSObject /** The string encoding used to serialize parameters. */ @property (nonatomic, assign) NSStringEncoding stringEncoding; /** Creates and returns a serializer with default configuration. */ + (instancetype)serializer; ///----------------------------------------- /// @name Configuring Response Serialization ///----------------------------------------- /** The acceptable HTTP status codes for responses. When non-`nil`, responses with status codes not contained by the set will result in an error during validation. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html */ @property (nonatomic, strong) NSIndexSet *acceptableStatusCodes; /** The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation. */ @property (nonatomic, strong) NSSet *acceptableContentTypes; /** Validates the specified response and data. In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks. @param response The response to be validated. @param data The data associated with the response. @param error The error that occurred while attempting to validate the response. @return `YES` if the response is valid, otherwise `NO`. */ - (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error; @end #pragma mark - /** `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses. By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: - `application/json` - `text/json` - `text/javascript` */ @interface AFJSONResponseSerializer : AFHTTPResponseSerializer /** Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. */ @property (nonatomic, assign) NSJSONReadingOptions readingOptions; /** Creates and returns a JSON serializer with specified reading and writing options. @param readingOptions The specified JSON reading options. */ + (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions; @end #pragma mark - /** `AFXMLParserSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. By default, `AFXMLParserSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: - `application/xml` - `text/xml` */ @interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer @end #pragma mark - #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED /** `AFXMLDocumentSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. By default, `AFXMLDocumentSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: - `application/xml` - `text/xml` */ @interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer /** Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. */ @property (nonatomic, assign) NSUInteger options; /** Creates and returns an XML document serializer with the specified options. @param mask The XML document options. */ + (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask; @end #endif #pragma mark - /** `AFPropertyListSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. By default, `AFPropertyListSerializer` accepts the following MIME types: - `application/x-plist` */ @interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer /** The property list format. Possible values are described in "NSPropertyListFormat". */ @property (nonatomic, assign) NSPropertyListFormat format; /** The property list reading options. Possible values are described in "NSPropertyListMutabilityOptions." */ @property (nonatomic, assign) NSPropertyListReadOptions readOptions; /** Creates and returns a property list serializer with a specified format, read options, and write options. @param format The property list format. @param readOptions The property list reading options. */ + (instancetype)serializerWithFormat:(NSPropertyListFormat)format readOptions:(NSPropertyListReadOptions)readOptions; @end #pragma mark - /** `AFImageSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. By default, `AFImageSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: - `image/tiff` - `image/jpeg` - `image/gif` - `image/png` - `image/ico` - `image/x-icon` - `image/bmp` - `image/x-bmp` - `image/x-xbitmap` - `image/x-win-bitmap` */ @interface AFImageResponseSerializer : AFHTTPResponseSerializer #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) /** The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance. */ @property (nonatomic, assign) CGFloat imageScale; /** Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default. */ @property (nonatomic, assign) BOOL automaticallyInflatesResponseImage; #endif @end #pragma mark - /** `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. */ @interface AFCompoundResponseSerializer : AFHTTPResponseSerializer /** The component response serializers. */ @property (readonly, nonatomic, strong) NSArray *responseSerializers; /** Creates and returns a compound serializer comprised of the specified response serializers. @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. */ + (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers; @end ================================================ FILE: MLRecorder/AFNetwoking/AFNetworking/Serialization/AFURLResponseSerialization.m ================================================ // AFSerialization.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFURLResponseSerialization.h" extern NSString * const AFNetworkingErrorDomain; extern NSString * const AFNetworkingOperationFailingURLResponseErrorKey; #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) #import #endif static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { if (!error) { return underlyingError; } if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) { return error; } NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy]; mutableUserInfo[NSUnderlyingErrorKey] = underlyingError; return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo]; } static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) { if (error.code == code) { return YES; } else if (error.userInfo[NSUnderlyingErrorKey]) { return AFErrorOrUnderlyingErrorHasCode(error.userInfo[NSUnderlyingErrorKey], code); } return NO; } @implementation AFHTTPResponseSerializer + (instancetype)serializer { return [[self alloc] init]; } - (instancetype)init { self = [super init]; if (!self) { return nil; } self.stringEncoding = NSUTF8StringEncoding; self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; self.acceptableContentTypes = nil; return self; } #pragma mark - - (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error { BOOL responseIsValid = YES; NSError *validationError = nil; if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) { if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) { if ([data length] > 0) { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], NSURLErrorFailingURLErrorKey:[response URL], AFNetworkingOperationFailingURLResponseErrorKey: response }; validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo], validationError); } responseIsValid = NO; } if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode]) { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%lu)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (unsigned long)response.statusCode], NSURLErrorFailingURLErrorKey:[response URL], AFNetworkingOperationFailingURLResponseErrorKey: response }; validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo], validationError); responseIsValid = NO; } } if (error && !responseIsValid) { *error = validationError; } return responseIsValid; } #pragma mark - AFURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { [self validateResponse:(NSHTTPURLResponse *)response data:data error:error]; return data; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [self init]; if (!self) { return nil; } self.acceptableStatusCodes = [decoder decodeObjectForKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; self.acceptableContentTypes = [decoder decodeObjectForKey:NSStringFromSelector(@selector(acceptableContentTypes))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone]; serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone]; return serializer; } @end #pragma mark - @implementation AFJSONResponseSerializer + (instancetype)serializer { return [self serializerWithReadingOptions:0]; } + (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions { AFJSONResponseSerializer *serializer = [[self alloc] init]; serializer.readingOptions = readingOptions; return serializer; } - (instancetype)init { self = [super init]; if (!self) { return nil; } self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html",nil]; return self; } #pragma mark - AFURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { if (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) { return nil; } } // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization. // See https://github.com/rails/rails/issues/1742 NSStringEncoding stringEncoding = self.stringEncoding; if (response.textEncodingName) { CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); if (encoding != kCFStringEncodingInvalidId) { stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding); } } id responseObject = nil; NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding]; if (responseString && ![responseString isEqualToString:@" "]) { // Workaround for a bug in NSJSONSerialization when Unicode character escape codes are used instead of the actual character // See http://stackoverflow.com/a/12843465/157142 data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; NSError *serializationError = nil; if (data) { if ([data length] > 0) { responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; } else { return nil; } } else { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Data failed decoding as a UTF-8 string", nil, @"AFNetworking"), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Could not decode string: %@", nil, @"AFNetworking"), responseString] }; serializationError = [NSError errorWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo]; } if (error) { *error = AFErrorWithUnderlyingError(serializationError, *error); } } return responseObject; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) { return nil; } self.readingOptions = (NSJSONReadingOptions)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(readingOptions))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeInteger:self.readingOptions forKey:NSStringFromSelector(@selector(readingOptions))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.readingOptions = self.readingOptions; return serializer; } @end #pragma mark - @implementation AFXMLParserResponseSerializer + (instancetype)serializer { AFXMLParserResponseSerializer *serializer = [[self alloc] init]; return serializer; } - (instancetype)init { self = [super init]; if (!self) { return nil; } self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; return self; } #pragma mark - AFURLResponseSerialization - (id)responseObjectForResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { if (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) { return nil; } } return [[NSXMLParser alloc] initWithData:data]; } @end #pragma mark - #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED @implementation AFXMLDocumentResponseSerializer + (instancetype)serializer { return [self serializerWithXMLDocumentOptions:0]; } + (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask { AFXMLDocumentResponseSerializer *serializer = [[self alloc] init]; serializer.options = mask; return serializer; } - (instancetype)init { self = [super init]; if (!self) { return nil; } self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; return self; } #pragma mark - AFURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { if (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) { return nil; } } NSError *serializationError = nil; NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError]; if (error) { *error = AFErrorWithUnderlyingError(serializationError, *error); } return document; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) { return nil; } self.options = [decoder decodeIntegerForKey:NSStringFromSelector(@selector(options))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeInteger:self.options forKey:NSStringFromSelector(@selector(options))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFXMLDocumentResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.options = self.options; return serializer; } @end #endif #pragma mark - @implementation AFPropertyListResponseSerializer + (instancetype)serializer { return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0]; } + (instancetype)serializerWithFormat:(NSPropertyListFormat)format readOptions:(NSPropertyListReadOptions)readOptions { AFPropertyListResponseSerializer *serializer = [[self alloc] init]; serializer.format = format; serializer.readOptions = readOptions; return serializer; } - (instancetype)init { self = [super init]; if (!self) { return nil; } self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil]; return self; } #pragma mark - AFURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { if (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) { return nil; } } id responseObject; NSError *serializationError = nil; if (data) { responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError]; } if (error) { *error = AFErrorWithUnderlyingError(serializationError, *error); } return responseObject; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) { return nil; } self.format = (NSPropertyListFormat)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(format))]; self.readOptions = (NSPropertyListReadOptions)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(readOptions))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeInteger:self.format forKey:NSStringFromSelector(@selector(format))]; [coder encodeInteger:(NSInteger)self.readOptions forKey:NSStringFromSelector(@selector(readOptions))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.format = self.format; serializer.readOptions = self.readOptions; return serializer; } @end #pragma mark - #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) { UIImage *image = [[UIImage alloc] initWithData:data]; return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; } static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) { if (!data || [data length] == 0) { return nil; } CGImageRef imageRef = NULL; CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); if ([response.MIMEType isEqualToString:@"image/png"]) { imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) { imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so if so, fall back to AFImageWithDataAtScale if (imageRef) { CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { CGImageRelease(imageRef); imageRef = NULL; } } } CGDataProviderRelease(dataProvider); UIImage *image = AFImageWithDataAtScale(data, scale); if (!imageRef) { if (image.images || !image) { return image; } imageRef = CGImageCreateCopy([image CGImage]); if (!imageRef) { return nil; } } size_t width = CGImageGetWidth(imageRef); size_t height = CGImageGetHeight(imageRef); size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); if (width * height > 1024 * 1024 || bitsPerComponent > 8) { CGImageRelease(imageRef); return image; } size_t bytesPerRow = 0; // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); if (colorSpaceModel == kCGColorSpaceModelRGB) { uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); if (alpha == kCGImageAlphaNone) { bitmapInfo &= ~kCGBitmapAlphaInfoMask; bitmapInfo |= kCGImageAlphaNoneSkipFirst; } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) { bitmapInfo &= ~kCGBitmapAlphaInfoMask; bitmapInfo |= kCGImageAlphaPremultipliedFirst; } } CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); CGColorSpaceRelease(colorSpace); if (!context) { CGImageRelease(imageRef); return image; } CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef); CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context); CGContextRelease(context); UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation]; CGImageRelease(inflatedImageRef); CGImageRelease(imageRef); return inflatedImage; } #endif @implementation AFImageResponseSerializer - (instancetype)init { self = [super init]; if (!self) { return nil; } self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil]; #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) self.imageScale = [[UIScreen mainScreen] scale]; self.automaticallyInflatesResponseImage = YES; #endif return self; } #pragma mark - AFURLResponseSerializer - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { if (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) { return nil; } } #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) if (self.automaticallyInflatesResponseImage) { return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); } else { return AFImageWithDataAtScale(data, self.imageScale); } #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) // Ensure that the image is set to it's correct pixel width and height NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data]; NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])]; [image addRepresentation:bitimage]; return image; #endif return nil; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) { return nil; } #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) self.imageScale = [[decoder decodeObjectForKey:NSStringFromSelector(@selector(imageScale))] floatValue]; self.automaticallyInflatesResponseImage = [decoder decodeBoolForKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; #endif return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) [coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))]; [coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; #endif } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) serializer.imageScale = self.imageScale; serializer.automaticallyInflatesResponseImage = self.automaticallyInflatesResponseImage; #endif return serializer; } @end #pragma mark - @interface AFCompoundResponseSerializer () @property (readwrite, nonatomic, strong) NSArray *responseSerializers; @end @implementation AFCompoundResponseSerializer + (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers { AFCompoundResponseSerializer *serializer = [[self alloc] init]; serializer.responseSerializers = responseSerializers; return serializer; } #pragma mark - AFURLResponseSerialization - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { for (id serializer in self.responseSerializers) { if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) { continue; } NSError *serializerError = nil; id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError]; if (responseObject) { if (error) { *error = AFErrorWithUnderlyingError(serializerError, *error); } return responseObject; } } return [super responseObjectForResponse:response data:data error:error]; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) { return nil; } self.responseSerializers = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseSerializers))]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeObject:self.responseSerializers forKey:NSStringFromSelector(@selector(responseSerializers))]; } #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.responseSerializers = self.responseSerializers; return serializer; } @end ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h ================================================ // AFNetworkActivityIndicatorManager.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import /** `AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a network request operation has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero. You should enable the shared instance of `AFNetworkActivityIndicatorManager` when your application finishes launching. In `AppDelegate application:didFinishLaunchingWithOptions:` you can do so with the following code: [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES]; By setting `isNetworkActivityIndicatorVisible` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself. See the Apple Human Interface Guidelines section about the Network Activity Indicator for more information: http://developer.apple.com/library/iOS/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW44 */ @interface AFNetworkActivityIndicatorManager : NSObject /** A Boolean value indicating whether the manager is enabled. If YES, the manager will change status bar network activity indicator according to network operation notifications it receives. The default value is NO. */ @property (nonatomic, assign, getter = isEnabled) BOOL enabled; /** A Boolean value indicating whether the network activity indicator is currently displayed in the status bar. */ @property (readonly, nonatomic, assign) BOOL isNetworkActivityIndicatorVisible; /** Returns the shared network activity indicator manager object for the system. @return The systemwide network activity indicator manager. */ + (instancetype)sharedManager; /** Increments the number of active network requests. If this number was zero before incrementing, this will start animating the status bar network activity indicator. */ - (void)incrementActivityCount; /** Decrements the number of active network requests. If this number becomes zero before decrementing, this will stop animating the status bar network activity indicator. */ - (void)decrementActivityCount; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m ================================================ // AFNetworkActivityIndicatorManager.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AFNetworkActivityIndicatorManager.h" #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) #import "AFHTTPRequestOperation.h" #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 #import "AFURLSessionManager.h" #endif static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.17; static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) { if ([[notification object] isKindOfClass:[AFURLConnectionOperation class]]) { return [(AFURLConnectionOperation *)[notification object] request]; } #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 if ([[notification object] respondsToSelector:@selector(originalRequest)]) { return [(NSURLSessionTask *)[notification object] originalRequest]; } #endif return nil; } @interface AFNetworkActivityIndicatorManager () @property (readwrite, nonatomic, assign) NSInteger activityCount; @property (readwrite, nonatomic, strong) NSTimer *activityIndicatorVisibilityTimer; @property (readonly, nonatomic, getter = isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; - (void)updateNetworkActivityIndicatorVisibility; - (void)updateNetworkActivityIndicatorVisibilityDelayed; @end @implementation AFNetworkActivityIndicatorManager @dynamic networkActivityIndicatorVisible; + (instancetype)sharedManager { static AFNetworkActivityIndicatorManager *_sharedManager = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _sharedManager = [[self alloc] init]; }); return _sharedManager; } + (NSSet *)keyPathsForValuesAffectingIsNetworkActivityIndicatorVisible { return [NSSet setWithObject:@"activityCount"]; } - (id)init { self = [super init]; if (!self) { return nil; } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingOperationDidStartNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil]; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingTaskDidResumeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidSuspendNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidCompleteNotification object:nil]; #endif return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [_activityIndicatorVisibilityTimer invalidate]; } - (void)updateNetworkActivityIndicatorVisibilityDelayed { if (self.enabled) { // Delay hiding of activity indicator for a short interval, to avoid flickering if (![self isNetworkActivityIndicatorVisible]) { [self.activityIndicatorVisibilityTimer invalidate]; self.activityIndicatorVisibilityTimer = [NSTimer timerWithTimeInterval:kAFNetworkActivityIndicatorInvisibilityDelay target:self selector:@selector(updateNetworkActivityIndicatorVisibility) userInfo:nil repeats:NO]; [[NSRunLoop mainRunLoop] addTimer:self.activityIndicatorVisibilityTimer forMode:NSRunLoopCommonModes]; } else { [self performSelectorOnMainThread:@selector(updateNetworkActivityIndicatorVisibility) withObject:nil waitUntilDone:NO modes:@[NSRunLoopCommonModes]]; } } } - (BOOL)isNetworkActivityIndicatorVisible { return self.activityCount > 0; } - (void)updateNetworkActivityIndicatorVisibility { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]]; } - (void)setActivityCount:(NSInteger)activityCount { @synchronized(self) { _activityCount = activityCount; } dispatch_async(dispatch_get_main_queue(), ^{ [self updateNetworkActivityIndicatorVisibilityDelayed]; }); } - (void)incrementActivityCount { [self willChangeValueForKey:@"activityCount"]; @synchronized(self) { _activityCount++; } [self didChangeValueForKey:@"activityCount"]; dispatch_async(dispatch_get_main_queue(), ^{ [self updateNetworkActivityIndicatorVisibilityDelayed]; }); } - (void)decrementActivityCount { [self willChangeValueForKey:@"activityCount"]; @synchronized(self) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" _activityCount = MAX(_activityCount - 1, 0); #pragma clang diagnostic pop } [self didChangeValueForKey:@"activityCount"]; dispatch_async(dispatch_get_main_queue(), ^{ [self updateNetworkActivityIndicatorVisibilityDelayed]; }); } - (void)networkRequestDidStart:(NSNotification *)notification { if ([AFNetworkRequestFromNotification(notification) URL]) { [self incrementActivityCount]; } } - (void)networkRequestDidFinish:(NSNotification *)notification { if ([AFNetworkRequestFromNotification(notification) URL]) { [self decrementActivityCount]; } } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h ================================================ // UIActivityIndicatorView+AFNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import @class AFURLConnectionOperation; /** This category adds methods to the UIKit framework's `UIActivityIndicatorView` class. The methods in this category provide support for automatically starting and stopping animation depending on the loading state of a request operation or session task. */ @interface UIActivityIndicatorView (AFNetworking) ///---------------------------------- /// @name Animating for Session Tasks ///---------------------------------- /** Binds the animating state to the state of the specified task. @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. */ #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 - (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task; #endif ///--------------------------------------- /// @name Animating for Request Operations ///--------------------------------------- /** Binds the animating state to the execution state of the specified operation. @param operation The operation. If `nil`, automatic updating from any previously specified operation will be disabled. */ - (void)setAnimatingWithStateOfOperation:(AFURLConnectionOperation *)operation; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m ================================================ // UIActivityIndicatorView+AFNetworking.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "UIActivityIndicatorView+AFNetworking.h" #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import "AFHTTPRequestOperation.h" #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 #import "AFURLSessionManager.h" #endif @implementation UIActivityIndicatorView (AFNetworking) #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 - (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; if (task) { if (task.state != NSURLSessionTaskStateCompleted) { if (task.state == NSURLSessionTaskStateRunning) { [self startAnimating]; } else { [self stopAnimating]; } [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task]; [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task]; [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task]; } } } #endif #pragma mark - - (void)setAnimatingWithStateOfOperation:(AFURLConnectionOperation *)operation { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self name:AFNetworkingOperationDidStartNotification object:nil]; [notificationCenter removeObserver:self name:AFNetworkingOperationDidFinishNotification object:nil]; if (operation) { if (![operation isFinished]) { if ([operation isExecuting]) { [self startAnimating]; } else { [self stopAnimating]; } [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingOperationDidStartNotification object:operation]; [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingOperationDidFinishNotification object:operation]; } } } #pragma mark - - (void)af_startAnimating { dispatch_async(dispatch_get_main_queue(), ^{ [self startAnimating]; }); } - (void)af_stopAnimating { dispatch_async(dispatch_get_main_queue(), ^{ [self stopAnimating]; }); } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIAlertView+AFNetworking.h ================================================ // UIAlertView+AFNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import @class AFURLConnectionOperation; /** This category adds methods to the UIKit framework's `UIAlertView` class. The methods in this category provide support for automatically showing an alert if a session task or request operation finishes with an error. Alert title and message are filled from the corresponding `localizedDescription` & `localizedRecoverySuggestion` or `localizedFailureReason` of the error. */ @interface UIAlertView (AFNetworking) ///------------------------------------- /// @name Showing Alert for Session Task ///------------------------------------- /** Shows an alert view with the error of the specified session task, if any. @param task The session task. @param delegate The alert view delegate. */ #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task delegate:(id)delegate; #endif /** Shows an alert view with the error of the specified session task, if any, with a custom cancel button title and other button titles. @param task The session task. @param delegate The alert view delegate. @param cancelButtonTitle The title of the cancel button or nil if there is no cancel button. Using this argument is equivalent to setting the cancel button index to the value returned by invoking addButtonWithTitle: specifying this title. @param otherButtonTitles The title of another button. Using this argument is equivalent to invoking addButtonWithTitle: with this title to add more buttons. Too many buttons can cause the alert view to scroll. For guidelines on the best ways to use an alert in an app, see "Temporary Views". Titles of additional buttons to add to the receiver, terminated with `nil`. */ #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION; #endif ///------------------------------------------ /// @name Showing Alert for Request Operation ///------------------------------------------ /** Shows an alert view with the error of the specified request operation, if any. @param operation The request operation. @param delegate The alert view delegate. */ + (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation delegate:(id)delegate; /** Shows an alert view with the error of the specified request operation, if any, with a custom cancel button title and other button titles. @param operation The request operation. @param delegate The alert view delegate. @param cancelButtonTitle The title of the cancel button or nil if there is no cancel button. Using this argument is equivalent to setting the cancel button index to the value returned by invoking addButtonWithTitle: specifying this title. @param otherButtonTitles The title of another button. Using this argument is equivalent to invoking addButtonWithTitle: with this title to add more buttons. Too many buttons can cause the alert view to scroll. For guidelines on the best ways to use an alert in an app, see "Temporary Views". Titles of additional buttons to add to the receiver, terminated with `nil`. */ + (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIAlertView+AFNetworking.m ================================================ // UIAlertView+AFNetworking.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "UIAlertView+AFNetworking.h" #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import "AFURLConnectionOperation.h" #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 #import "AFURLSessionManager.h" #endif static void AFGetAlertViewTitleAndMessageFromError(NSError *error, NSString * __autoreleasing *title, NSString * __autoreleasing *message) { if (error.localizedDescription && (error.localizedRecoverySuggestion || error.localizedFailureReason)) { *title = error.localizedDescription; if (error.localizedRecoverySuggestion) { *message = error.localizedRecoverySuggestion; } else { *message = error.localizedFailureReason; } } else if (error.localizedDescription) { *title = NSLocalizedStringFromTable(@"Error", @"AFNetworking", @"Fallback Error Description"); *message = error.localizedDescription; } else { *title = NSLocalizedStringFromTable(@"Error", @"AFNetworking", @"Fallback Error Description"); *message = [NSString stringWithFormat:NSLocalizedStringFromTable(@"%@ Error: %lu", @"AFNetworking", @"Fallback Error Failure Reason Format"), error.domain, (NSUInteger)error.code]; } } @implementation UIAlertView (AFNetworking) #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task delegate:(id)delegate { [self showAlertViewForTaskWithErrorOnCompletion:task delegate:delegate cancelButtonTitle:NSLocalizedStringFromTable(@"Dismiss", @"AFNetworking", @"UIAlertView Cancel Button Title") otherButtonTitles:nil, nil]; } + (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION { __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingTaskDidCompleteNotification object:task queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { NSError *error = notification.userInfo[AFNetworkingTaskDidCompleteErrorKey]; if (error) { NSString *title, *message; AFGetAlertViewTitleAndMessageFromError(error, &title, &message); [[[UIAlertView alloc] initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil] show]; } [[NSNotificationCenter defaultCenter] removeObserver:observer name:AFNetworkingTaskDidCompleteNotification object:notification.object]; }]; } #endif #pragma mark - + (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation delegate:(id)delegate { [self showAlertViewForRequestOperationWithErrorOnCompletion:operation delegate:delegate cancelButtonTitle:NSLocalizedStringFromTable(@"Dismiss", @"AFNetworking", @"UIAlert View Cancel Button Title") otherButtonTitles:nil, nil]; } + (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION { __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingOperationDidFinishNotification object:operation queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { if (notification.object && [notification.object isKindOfClass:[AFURLConnectionOperation class]]) { NSError *error = [(AFURLConnectionOperation *)notification.object error]; if (error) { NSString *title, *message; AFGetAlertViewTitleAndMessageFromError(error, &title, &message); [[[UIAlertView alloc] initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil] show]; } } [[NSNotificationCenter defaultCenter] removeObserver:observer name:AFNetworkingOperationDidFinishNotification object:notification.object]; }]; } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIButton+AFNetworking.h ================================================ // UIButton+AFNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import /** This category adds methods to the UIKit framework's `UIButton` class. The methods in this category provide support for loading remote images and background images asynchronously from a URL. */ @interface UIButton (AFNetworking) ///-------------------- /// @name Setting Image ///-------------------- /** Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. @param state The control state. @param url The URL used for the image request. */ - (void)setImageForState:(UIControlState)state withURL:(NSURL *)url; /** Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. @param state The control state. @param url The URL used for the image request. @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. */ - (void)setImageForState:(UIControlState)state withURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage; /** Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setImage:forState:` is applied. @param state The control state. @param urlRequest The URL request used for the image request. @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. @param success A block to be executed when the image request operation finishes successfully. This block has no return value and takes two arguments: the server response and the image. If the image was returned from cache, the request and response parameters will be `nil`. @param failure A block object to be executed when the image request operation finishes unsuccessfully, or that finishes successfully. This block has no return value and takes a single argument: the error that occurred. */ - (void)setImageForState:(UIControlState)state withURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage success:(void (^)(NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSError *error))failure; ///------------------------------- /// @name Setting Background Image ///------------------------------- /** Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous background image request for the receiver will be cancelled. If the background image is cached locally, the background image is set immediately, otherwise the specified placeholder background image will be set immediately, and then the remote background image will be set once the request is finished. @param state The control state. @param url The URL used for the background image request. */ - (void)setBackgroundImageForState:(UIControlState)state withURL:(NSURL *)url; /** Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. @param state The control state. @param url The URL used for the background image request. @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. */ - (void)setBackgroundImageForState:(UIControlState)state withURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage; /** Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setBackgroundImage:forState:` is applied. @param state The control state. @param urlRequest The URL request used for the image request. @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. */ - (void)setBackgroundImageForState:(UIControlState)state withURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage success:(void (^)(NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSError *error))failure; ///------------------------------ /// @name Canceling Image Loading ///------------------------------ /** Cancels any executing image operation for the receiver, if one exists. */ - (void)cancelImageRequestOperation; /** Cancels any executing background image operation for the receiver, if one exists. */ - (void)cancelBackgroundImageRequestOperation; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIButton+AFNetworking.m ================================================ // UIButton+AFNetworking.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "UIButton+AFNetworking.h" #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import "AFHTTPRequestOperation.h" @interface UIButton (_AFNetworking) @property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFHTTPRequestOperation *af_imageRequestOperation; @property (readwrite, nonatomic, strong, setter = af_setBackgroundImageRequestOperation:) AFHTTPRequestOperation *af_backgroundImageRequestOperation; @end @implementation UIButton (_AFNetworking) + (NSOperationQueue *)af_sharedImageRequestOperationQueue { static NSOperationQueue *_af_sharedImageRequestOperationQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _af_sharedImageRequestOperationQueue = [[NSOperationQueue alloc] init]; _af_sharedImageRequestOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; }); return _af_sharedImageRequestOperationQueue; } - (AFHTTPRequestOperation *)af_imageRequestOperation { return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_imageRequestOperation)); } - (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation { objc_setAssociatedObject(self, @selector(af_imageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (AFHTTPRequestOperation *)af_backgroundImageRequestOperation { return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_backgroundImageRequestOperation)); } - (void)af_setBackgroundImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation { objc_setAssociatedObject(self, @selector(af_backgroundImageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end #pragma mark - @implementation UIButton (AFNetworking) - (void)setImageForState:(UIControlState)state withURL:(NSURL *)url { [self setImageForState:state withURL:url placeholderImage:nil]; } - (void)setImageForState:(UIControlState)state withURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; [self setImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; } - (void)setImageForState:(UIControlState)state withURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage success:(void (^)(NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSError *error))failure { [self cancelImageRequestOperation]; [self setImage:placeholderImage forState:state]; __weak __typeof(self)weakSelf = self; self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; self.af_imageRequestOperation.responseSerializer = [AFImageResponseSerializer serializer]; [self.af_imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[urlRequest URL] isEqual:[operation.request URL]]) { if (success) { success(operation.response, responseObject); } else if (responseObject) { [strongSelf setImage:responseObject forState:state]; } } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if ([[urlRequest URL] isEqual:[operation.response URL]]) { if (failure) { failure(error); } } }]; [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation]; } #pragma mark - - (void)setBackgroundImageForState:(UIControlState)state withURL:(NSURL *)url { [self setBackgroundImageForState:state withURL:url placeholderImage:nil]; } - (void)setBackgroundImageForState:(UIControlState)state withURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; [self setBackgroundImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; } - (void)setBackgroundImageForState:(UIControlState)state withURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage success:(void (^)(NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSError *error))failure { [self cancelBackgroundImageRequestOperation]; [self setBackgroundImage:placeholderImage forState:state]; __weak __typeof(self)weakSelf = self; self.af_backgroundImageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; self.af_backgroundImageRequestOperation.responseSerializer = [AFImageResponseSerializer serializer]; [self.af_backgroundImageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[urlRequest URL] isEqual:[operation.request URL]]) { if (success) { success(operation.response, responseObject); } else if (responseObject) { [strongSelf setBackgroundImage:responseObject forState:state]; } } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if ([[urlRequest URL] isEqual:[operation.response URL]]) { if (failure) { failure(error); } } }]; [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_backgroundImageRequestOperation]; } #pragma mark - - (void)cancelImageRequestOperation { [self.af_imageRequestOperation cancel]; self.af_imageRequestOperation = nil; } - (void)cancelBackgroundImageRequestOperation { [self.af_backgroundImageRequestOperation cancel]; self.af_backgroundImageRequestOperation = nil; } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIImageView+AFNetworking.h ================================================ // UIImageView+AFNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import #import "AFURLResponseSerialization.h" @protocol AFImageCache; /** This category adds methods to the UIKit framework's `UIImageView` class. The methods in this category provide support for loading remote images asynchronously from a URL. */ @interface UIImageView (AFNetworking) ///---------------------------- /// @name Accessing Image Cache ///---------------------------- /** The image cache used to improve image loadiing performance on scroll views. By default, this is an `NSCache` subclass conforming to the `AFImageCache` protocol, which listens for notification warnings and evicts objects accordingly. */ + (id )sharedImageCache; /** Set the cache used for image loading. @param imageCache The image cache. */ + (void)setSharedImageCache:(id )imageCache; ///------------------------------------ /// @name Accessing Response Serializer ///------------------------------------ /** The response serializer used to create an image representation from the server response and response data. By default, this is an instance of `AFImageResponseSerializer`. @discussion Subclasses of `AFImageResponseSerializer` could be used to perform post-processing, such as color correction, face detection, or other effects. See https://github.com/AFNetworking/AFCoreImageSerializer */ @property (nonatomic, strong) AFImageResponseSerializer * imageResponseSerializer; ///-------------------- /// @name Setting Image ///-------------------- /** Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` @param url The URL used for the image request. */ - (void)setImageWithURL:(NSURL *)url; /** Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` @param url The URL used for the image request. @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. */ - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage; /** Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied. @param urlRequest The URL request used for the image request. @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. @param success A block to be executed when the image request operation finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the request and response parameters will be `nil`. @param failure A block object to be executed when the image request operation finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. */ - (void)setImageWithURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure; /** Cancels any executing image operation for the receiver, if one exists. */ - (void)cancelImageRequestOperation; @end #pragma mark - /** The `AFImageCache` protocol is adopted by an object used to cache images loaded by the AFNetworking category on `UIImageView`. */ @protocol AFImageCache /** Returns a cached image for the specififed request, if available. @param request The image request. @return The cached image. */ - (UIImage *)cachedImageForRequest:(NSURLRequest *)request; /** Caches a particular image for the specified request. @param image The image to cache. @param request The request to be used as a cache key. */ - (void)cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIImageView+AFNetworking.m ================================================ // UIImageView+AFNetworking.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "UIImageView+AFNetworking.h" #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import "AFHTTPRequestOperation.h" @interface AFImageCache : NSCache @end #pragma mark - @interface UIImageView (_AFNetworking) @property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFHTTPRequestOperation *af_imageRequestOperation; @end @implementation UIImageView (_AFNetworking) + (NSOperationQueue *)af_sharedImageRequestOperationQueue { static NSOperationQueue *_af_sharedImageRequestOperationQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _af_sharedImageRequestOperationQueue = [[NSOperationQueue alloc] init]; _af_sharedImageRequestOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; }); return _af_sharedImageRequestOperationQueue; } - (AFHTTPRequestOperation *)af_imageRequestOperation { return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_imageRequestOperation)); } - (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation { objc_setAssociatedObject(self, @selector(af_imageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end #pragma mark - @implementation UIImageView (AFNetworking) @dynamic imageResponseSerializer; + (id )sharedImageCache { static AFImageCache *_af_defaultImageCache = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _af_defaultImageCache = [[AFImageCache alloc] init]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * __unused notification) { [_af_defaultImageCache removeAllObjects]; }]; }); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" return objc_getAssociatedObject(self, @selector(sharedImageCache)) ?: _af_defaultImageCache; #pragma clang diagnostic pop } + (void)setSharedImageCache:(id)imageCache { objc_setAssociatedObject(self, @selector(sharedImageCache), imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (id )imageResponseSerializer { static id _af_defaultImageResponseSerializer = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _af_defaultImageResponseSerializer = [AFImageResponseSerializer serializer]; }); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" return objc_getAssociatedObject(self, @selector(imageResponseSerializer)) ?: _af_defaultImageResponseSerializer; #pragma clang diagnostic pop } - (void)setImageResponseSerializer:(id )serializer { objc_setAssociatedObject(self, @selector(imageResponseSerializer), serializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - - (void)setImageWithURL:(NSURL *)url { [self setImageWithURL:url placeholderImage:nil]; } - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; } - (void)setImageWithURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure { [self cancelImageRequestOperation]; UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; if (cachedImage) { if (success) { success(nil, nil, cachedImage); } else { self.image = cachedImage; } self.af_imageRequestOperation = nil; } else { if (placeholderImage) { self.image = placeholderImage; } __weak __typeof(self)weakSelf = self; self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; self.af_imageRequestOperation.responseSerializer = self.imageResponseSerializer; [self.af_imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[urlRequest URL] isEqual:[operation.request URL]]) { if (success) { success(urlRequest, operation.response, responseObject); } else if (responseObject) { strongSelf.image = responseObject; } } [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if ([[urlRequest URL] isEqual:[operation.request URL]]) { if (failure) { failure(urlRequest, operation.response, error); } } }]; [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation]; } } - (void)cancelImageRequestOperation { [self.af_imageRequestOperation cancel]; self.af_imageRequestOperation = nil; } @end #pragma mark - static inline NSString * AFImageCacheKeyFromURLRequest(NSURLRequest *request) { return [[request URL] absoluteString]; } @implementation AFImageCache - (UIImage *)cachedImageForRequest:(NSURLRequest *)request { switch ([request cachePolicy]) { case NSURLRequestReloadIgnoringCacheData: case NSURLRequestReloadIgnoringLocalAndRemoteCacheData: return nil; default: break; } return [self objectForKey:AFImageCacheKeyFromURLRequest(request)]; } - (void)cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request { if (image && request) { [self setObject:image forKey:AFImageCacheKeyFromURLRequest(request)]; } } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIKit+AFNetworking.h ================================================ // UIKit+AFNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #ifndef _UIKIT_AFNETWORKING_ #define _UIKIT_AFNETWORKING_ #import "AFNetworkActivityIndicatorManager.h" #import "UIActivityIndicatorView+AFNetworking.h" #import "UIAlertView+AFNetworking.h" #import "UIButton+AFNetworking.h" #import "UIImageView+AFNetworking.h" #import "UIKit+AFNetworking.h" #import "UIProgressView+AFNetworking.h" #import "UIWebView+AFNetworking.h" #endif /* _UIKIT_AFNETWORKING_ */ ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIProgressView+AFNetworking.h ================================================ // UIProgressView+AFNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import @class AFURLConnectionOperation; /** This category adds methods to the UIKit framework's `UIProgressView` class. The methods in this category provide support for binding the progress to the upload and download progress of a session task or request operation. */ @interface UIProgressView (AFNetworking) ///------------------------------------ /// @name Setting Session Task Progress ///------------------------------------ /** Binds the progress to the upload progress of the specified session task. @param task The session task. @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. */ #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 - (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task animated:(BOOL)animated; #endif /** Binds the progress to the download progress of the specified session task. @param task The session task. @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. */ #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 - (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task animated:(BOOL)animated; #endif ///------------------------------------ /// @name Setting Session Task Progress ///------------------------------------ /** Binds the progress to the upload progress of the specified request operation. @param operation The request operation. @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. */ - (void)setProgressWithUploadProgressOfOperation:(AFURLConnectionOperation *)operation animated:(BOOL)animated; /** Binds the progress to the download progress of the specified request operation. @param operation The request operation. @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. */ - (void)setProgressWithDownloadProgressOfOperation:(AFURLConnectionOperation *)operation animated:(BOOL)animated; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIProgressView+AFNetworking.m ================================================ // UIProgressView+AFNetworking.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "UIProgressView+AFNetworking.h" #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import "AFURLConnectionOperation.h" #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 #import "AFURLSessionManager.h" #endif static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext; static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext; @interface AFURLConnectionOperation (_UIProgressView) @property (readwrite, nonatomic, copy) void (^uploadProgress)(NSUInteger bytes, NSInteger totalBytes, NSInteger totalBytesExpected); @property (readwrite, nonatomic, assign, setter = af_setUploadProgressAnimated:) BOOL af_uploadProgressAnimated; @property (readwrite, nonatomic, copy) void (^downloadProgress)(NSUInteger bytes, NSInteger totalBytes, NSInteger totalBytesExpected); @property (readwrite, nonatomic, assign, setter = af_setDownloadProgressAnimated:) BOOL af_downloadProgressAnimated; @end @implementation AFURLConnectionOperation (_UIProgressView) @dynamic uploadProgress; // Implemented in AFURLConnectionOperation @dynamic af_uploadProgressAnimated; @dynamic downloadProgress; // Implemented in AFURLConnectionOperation @dynamic af_downloadProgressAnimated; @end #pragma mark - @implementation UIProgressView (AFNetworking) - (BOOL)af_uploadProgressAnimated { return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue]; } - (void)af_setUploadProgressAnimated:(BOOL)animated { objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)af_downloadProgressAnimated { return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue]; } - (void)af_setDownloadProgressAnimated:(BOOL)animated { objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 - (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task animated:(BOOL)animated { [task addObserver:self forKeyPath:@"state" options:0 context:AFTaskCountOfBytesSentContext]; [task addObserver:self forKeyPath:@"countOfBytesSent" options:0 context:AFTaskCountOfBytesSentContext]; [self af_setUploadProgressAnimated:animated]; } - (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task animated:(BOOL)animated { [task addObserver:self forKeyPath:@"state" options:0 context:AFTaskCountOfBytesReceivedContext]; [task addObserver:self forKeyPath:@"countOfBytesReceived" options:0 context:AFTaskCountOfBytesReceivedContext]; [self af_setDownloadProgressAnimated:animated]; } #endif #pragma mark - - (void)setProgressWithUploadProgressOfOperation:(AFURLConnectionOperation *)operation animated:(BOOL)animated { __weak __typeof(self)weakSelf = self; void (^original)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) = [operation.uploadProgress copy]; [operation setUploadProgressBlock:^(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) { if (original) { original(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); } dispatch_async(dispatch_get_main_queue(), ^{ if (totalBytesExpectedToWrite > 0) { __strong __typeof(weakSelf)strongSelf = weakSelf; [strongSelf setProgress:(totalBytesWritten / (totalBytesExpectedToWrite * 1.0f)) animated:animated]; } }); }]; } - (void)setProgressWithDownloadProgressOfOperation:(AFURLConnectionOperation *)operation animated:(BOOL)animated { __weak __typeof(self)weakSelf = self; void (^original)(NSUInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead) = [operation.downloadProgress copy]; [operation setDownloadProgressBlock:^(NSUInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead) { if (original) { original(bytesRead, totalBytesRead, totalBytesExpectedToRead); } dispatch_async(dispatch_get_main_queue(), ^{ if (totalBytesExpectedToRead > 0) { __strong __typeof(weakSelf)strongSelf = weakSelf; [strongSelf setProgress:(totalBytesRead / (totalBytesExpectedToRead * 1.0f)) animated:animated]; } }); }]; } #pragma mark - NSKeyValueObserving - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(__unused NSDictionary *)change context:(void *)context { #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) { if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { if ([object countOfBytesExpectedToSend] > 0) { dispatch_async(dispatch_get_main_queue(), ^{ [self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated]; }); } } if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { if ([object countOfBytesExpectedToReceive] > 0) { dispatch_async(dispatch_get_main_queue(), ^{ [self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated]; }); } } if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) { if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) { @try { [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))]; if (context == AFTaskCountOfBytesSentContext) { [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; } if (context == AFTaskCountOfBytesReceivedContext) { [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; } } @catch (NSException * __unused exception) {} } } } #endif } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h ================================================ // UIRefreshControl+AFNetworking.m // // Copyright (c) 2014 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import @class AFURLConnectionOperation; /** This category adds methods to the UIKit framework's `UIRefreshControl` class. The methods in this category provide support for automatically begining and ending refreshing depending on the loading state of a request operation or session task. */ @interface UIRefreshControl (AFNetworking) ///----------------------------------- /// @name Refreshing for Session Tasks ///----------------------------------- /** Binds the refreshing state to the state of the specified task. @param task The task. If `nil`, automatic updating from any previously specified operation will be diabled. */ #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 - (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; #endif ///---------------------------------------- /// @name Refreshing for Request Operations ///---------------------------------------- /** Binds the refreshing state to the execution state of the specified operation. @param operation The operation. If `nil`, automatic updating from any previously specified operation will be disabled. */ - (void)setRefreshingWithStateOfOperation:(AFURLConnectionOperation *)operation; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m ================================================ // UIRefreshControl+AFNetworking.m // // Copyright (c) 2014 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "UIRefreshControl+AFNetworking.h" #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import "AFHTTPRequestOperation.h" #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 #import "AFURLSessionManager.h" #endif @implementation UIRefreshControl (AFNetworking) #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 - (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; if (task) { if (task.state != NSURLSessionTaskStateCompleted) { if (task.state == NSURLSessionTaskStateRunning) { [self beginRefreshing]; } else { [self endRefreshing]; } [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task]; [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task]; [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task]; } } } #endif - (void)setRefreshingWithStateOfOperation:(AFURLConnectionOperation *)operation { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self name:AFNetworkingOperationDidStartNotification object:nil]; [notificationCenter removeObserver:self name:AFNetworkingOperationDidFinishNotification object:nil]; if (operation) { if (![operation isFinished]) { if ([operation isExecuting]) { [self beginRefreshing]; } else { [self endRefreshing]; } [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingOperationDidStartNotification object:operation]; [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingOperationDidFinishNotification object:operation]; } } } #pragma mark - - (void)af_beginRefreshing { dispatch_async(dispatch_get_main_queue(), ^{ [self beginRefreshing]; }); } - (void)af_endRefreshing { dispatch_async(dispatch_get_main_queue(), ^{ [self endRefreshing]; }); } @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIWebView+AFNetworking.h ================================================ // UIWebView+AFNetworking.h // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import #import "AFURLRequestSerialization.h" #import "AFURLResponseSerialization.h" /** This category adds methods to the UIKit framework's `UIWebView` class. The methods in this category provide increased control over the request cycle, including progress monitoring and success / failure handling. @discussion When using these category methods, make sure to assign `delegate` for the web view, which implements `–webView:shouldStartLoadWithRequest:navigationType:` appropriately. This allows for tapped links to be loaded through AFNetworking, and can ensure that `canGoBack` & `canGoForward` update their values correctly. */ @interface UIWebView (AFNetworking) /** The request serializer used to serialize requests made with the `-loadRequest:...` category methods. By default, this is an instance of `AFHTTPRequestSerializer`. */ @property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; /** The response serializer used to serialize responses made with the `-loadRequest:...` category methods. By default, this is an instance of `AFHTTPResponseSerializer`. */ @property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; /** Asynchronously loads the specified request. @param request A URL request identifying the location of the content to load. This must not be `nil`. @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. @param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. */ - (void)loadRequest:(NSURLRequest *)request progress:(void (^)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))progress success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success failure:(void (^)(NSError *error))failure; /** Asynchronously loads the data associated with a particular request with a specified MIME type and text encoding. @param request A URL request identifying the location of the content to load. This must not be `nil`. @param MIMEType The MIME type of the content. Defaults to the content type of the response if not specified. @param textEncodingName The IANA encoding name, as in `utf-8` or `utf-16`. Defaults to the response text encoding if not specified. @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. @param success A block object to be executed when the request finishes loading successfully. This block returns the data to be loaded by the web view and takes two arguments: the response, and the downloaded data. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. */ - (void)loadRequest:(NSURLRequest *)request MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName progress:(void (^)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))progress success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success failure:(void (^)(NSError *error))failure; @end #endif ================================================ FILE: MLRecorder/AFNetwoking/UIKit+AFNetworking/UIWebView+AFNetworking.m ================================================ // UIWebView+AFNetworking.m // // Copyright (c) 2013 AFNetworking (http://afnetworking.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "UIWebView+AFNetworking.h" #import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #import "AFHTTPRequestOperation.h" @interface UIWebView (_AFNetworking) @property (readwrite, nonatomic, strong, setter = af_setHTTPRequestOperation:) AFHTTPRequestOperation *af_HTTPRequestOperation; @end @implementation UIWebView (_AFNetworking) - (AFHTTPRequestOperation *)af_HTTPRequestOperation { return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_HTTPRequestOperation)); } - (void)af_setHTTPRequestOperation:(AFHTTPRequestOperation *)operation { objc_setAssociatedObject(self, @selector(af_HTTPRequestOperation), operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end #pragma mark - @implementation UIWebView (AFNetworking) - (AFHTTPRequestSerializer *)requestSerializer { static AFHTTPRequestSerializer *_af_defaultRequestSerializer = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _af_defaultRequestSerializer = [AFHTTPRequestSerializer serializer]; }); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" return objc_getAssociatedObject(self, @selector(requestSerializer)) ?: _af_defaultRequestSerializer; #pragma clang diagnostic pop } - (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { objc_setAssociatedObject(self, @selector(requestSerializer), requestSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (AFHTTPResponseSerializer *)responseSerializer { static AFHTTPResponseSerializer *_af_defaultResponseSerializer = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _af_defaultResponseSerializer = [AFHTTPResponseSerializer serializer]; }); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" return objc_getAssociatedObject(self, @selector(responseSerializer)) ?: _af_defaultResponseSerializer; #pragma clang diagnostic pop } - (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { objc_setAssociatedObject(self, @selector(responseSerializer), responseSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - - (void)loadRequest:(NSURLRequest *)request progress:(void (^)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))progress success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success failure:(void (^)(NSError *error))failure { [self loadRequest:request MIMEType:nil textEncodingName:nil progress:progress success:^NSData *(NSHTTPURLResponse *response, NSData *data) { NSStringEncoding stringEncoding = NSUTF8StringEncoding; if (response.textEncodingName) { CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); if (encoding != kCFStringEncodingInvalidId) { stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding); } } NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding]; if (success) { string = success(response, string); } return [string dataUsingEncoding:stringEncoding]; } failure:failure]; } - (void)loadRequest:(NSURLRequest *)request MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName progress:(void (^)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))progress success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success failure:(void (^)(NSError *error))failure { NSParameterAssert(request); if (self.af_HTTPRequestOperation) { [self.af_HTTPRequestOperation cancel]; } request = [self.requestSerializer requestBySerializingRequest:request withParameters:nil error:nil]; self.af_HTTPRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; self.af_HTTPRequestOperation.responseSerializer = self.responseSerializer; __weak __typeof(self)weakSelf = self; [self.af_HTTPRequestOperation setDownloadProgressBlock:progress]; [self.af_HTTPRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id __unused responseObject) { NSData *data = success ? success(operation.response, operation.responseData) : operation.responseData; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" __strong __typeof(weakSelf) strongSelf = weakSelf; [strongSelf loadData:data MIMEType:(MIMEType ?: [operation.response MIMEType]) textEncodingName:(textEncodingName ?: [operation.response textEncodingName]) baseURL:[operation.response URL]]; #pragma clang diagnostic pop } failure:^(AFHTTPRequestOperation * __unused operation, NSError *error) { if (failure) { failure(error); } }]; [self.af_HTTPRequestOperation start]; } @end #endif ================================================ FILE: MLRecorder/AmrRecordInBufferWriter.h ================================================ // // AmrRecordInBufferWriter.h // MLRecorder // // Created by molon on 14/12/22. // Copyright (c) 2014年 molon. All rights reserved. // #import #import "MLAudioRecorder.h" @interface AmrRecordInBufferWriter : NSObject @property (nonatomic, copy) void(^didReceiveVoiceData)(NSData *data); @end ================================================ FILE: MLRecorder/AmrRecordInBufferWriter.m ================================================ // // AmrRecordInBufferWriter.m // MLRecorder // // Created by molon on 14/12/22. // Copyright (c) 2014年 molon. All rights reserved. // #import "AmrRecordInBufferWriter.h" //amr编码 #import "interf_enc.h" @interface AmrRecordInBufferWriter() { void *_enstate; } @end @implementation AmrRecordInBufferWriter - (BOOL)createFileWithRecorder:(MLAudioRecorder*)recoder { _enstate = 0; // amr 压缩句柄 _enstate = Encoder_Interface_init(0); if(_enstate==0){ return NO; } return YES; } - (BOOL)writeIntoFileWithData:(NSData*)data withRecorder:(MLAudioRecorder*)recoder inAQ:(AudioQueueRef) inAQ inStartTime:(const AudioTimeStamp *)inStartTime inNumPackets:(UInt32)inNumPackets inPacketDesc:(const AudioStreamPacketDescription*)inPacketDesc { if (self.didReceiveVoiceData) { const void *recordingData = data.bytes; NSUInteger pcmLen = data.length; if (pcmLen<=0){ return YES; } if (pcmLen%2!=0){ pcmLen--; //防止意外,如果不是偶数,情愿减去最后一个字节。 DLOG(@"不是偶数"); } NSMutableData *amrData = [NSMutableData data]; unsigned char buffer[320]; for (int i =0; i < pcmLen ;i+=160*2) { short *pPacket = (short *)((unsigned char*)recordingData+i); if (pcmLen-i<160*2){ continue; //不是一个完整的就拜拜 } memset(buffer, 0, sizeof(buffer)); //encode int recvLen = Encoder_Interface_Encode(_enstate,MR515,pPacket,buffer,0); if (recvLen>0) { [amrData appendBytes:buffer length:recvLen]; } } self.didReceiveVoiceData(amrData); } return YES; } - (BOOL)completeWriteWithRecorder:(MLAudioRecorder*)recoder withIsError:(BOOL)isError { return YES; } -(void)dealloc { if (_enstate){ Encoder_Interface_exit((void*)_enstate); _enstate = 0; } } @end ================================================ FILE: MLRecorder/AppDelegate.h ================================================ // // AppDelegate.h // MLRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: MLRecorder/AppDelegate.m ================================================ // // AppDelegate.m // MLRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import "AppDelegate.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. return YES; } - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - (void)applicationWillEnterForeground:(UIApplication *)application { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } @end ================================================ FILE: MLRecorder/Base.lproj/Main.storyboard ================================================ ================================================ FILE: MLRecorder/CafRecordInBufferWriter.h ================================================ // // CafRecordInBuffer.h // MLRecorder // // Created by molon on 14/12/19. // Copyright (c) 2014年 molon. All rights reserved. // #import #import "MLAudioRecorder.h" //简单的测试,这里代码不太好,有兴趣自己根据实际情况改 @interface CafRecordInBufferWriter : NSObject @property (nonatomic, copy) void(^didReceiveVoiceData)(NSData *data); @end ================================================ FILE: MLRecorder/CafRecordInBufferWriter.m ================================================ // // CafRecordInBuffer.m // MLRecorder // // Created by molon on 14/12/19. // Copyright (c) 2014年 molon. All rights reserved. // #import "CafRecordInBufferWriter.h" @interface CafRecordInBufferWriter() @end @implementation CafRecordInBufferWriter - (BOOL)createFileWithRecorder:(MLAudioRecorder*)recoder { return YES; } - (BOOL)writeIntoFileWithData:(NSData*)data withRecorder:(MLAudioRecorder*)recoder inAQ:(AudioQueueRef) inAQ inStartTime:(const AudioTimeStamp *)inStartTime inNumPackets:(UInt32)inNumPackets inPacketDesc:(const AudioStreamPacketDescription*)inPacketDesc { if (self.didReceiveVoiceData) { self.didReceiveVoiceData(data); } return YES; } - (BOOL)completeWriteWithRecorder:(MLAudioRecorder*)recoder withIsError:(BOOL)isError { return YES; } -(void)dealloc { } @end ================================================ FILE: MLRecorder/Debug.h ================================================ // // Debug.h // MolonFrame // // Created by Molon on 13-10-4. // Copyright (c) 2013年 Molon. All rights reserved. // #ifndef MolonFrame_Debug_h #define MolonFrame_Debug_h #ifdef DEBUG #define DLOG(format, ...) \ NSLog(@"\n%s:%d\n%@", \ __PRETTY_FUNCTION__, __LINE__, \ [NSString stringWithFormat:format, ## __VA_ARGS__]) #define _po(o) DLOG(@"%@", (o)) #define _pn(o) DLOG(@"%d", (o)) #define _pf(o) DLOG(@"%f", (o)) #define _ps(o) DLOG(@"CGSize: {%.0f, %.0f}", (o).width, (o).height) #define _pr(o) DLOG(@"NSRect: {{%.0f, %.0f}, {%.0f, %.0f}}", (o).origin.x, (o).origin.y, (o).size.width, (o).size.height) #define DOBJ(obj) DLOG(@"%s: %@", #obj, [(obj) description]) #define MARK NSLog(@"\nMARK: %s, %d", __PRETTY_FUNCTION__, __LINE__) #define START_TIMER \ NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; #define END_TIMER(msg) \ DLOG([NSString stringWithFormat:"%@ Time = %f", msg, \ [NSDate timeIntervalSinceReferenceDate]-start]); #else #define DLOG #define _po #define _pn #define _pf #define _ps #define _pr #define DOBJ #define MARK #define START_TIMER #define END_TIMER #endif #ifdef DEBUG #define LOG(...) NSLog(__VA_ARGS__) #define LOG_METHOD \ NSLog(@"\nLine:%d\nFunction:%s\n", __LINE__, __FUNCTION__) #else #define LOG(...) #define LOG_METHOD #endif #endif ================================================ FILE: MLRecorder/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: MLRecorder/Images.xcassets/LaunchImage.launchimage/Contents.json ================================================ { "images" : [ { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", "subtype" : "retina4", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: MLRecorder/MLAudioPlayer/AmrPlayerReader.h ================================================ // // AmrPlayerReader.h // MLRecorder // // Created by molon on 5/23/14. // Copyright (c) 2014 molon. All rights reserved. // #import #import "MLAudioPlayer.h" @interface AmrPlayerReader : NSObject @property (nonatomic, copy) NSString *filePath; + (double)durationOfAmrFilePath:(NSString*)filePath; - (double)duration; @end ================================================ FILE: MLRecorder/MLAudioPlayer/AmrPlayerReader.m ================================================ // // AmrPlayerReader.m // MLRecorder // // Created by molon on 5/23/14. // Copyright (c) 2014 molon. All rights reserved. // #import "AmrPlayerReader.h" //amr解码 #import "interf_dec.h" #define AMR_MAGIC_NUMBER "#!AMR\n" #define PCM_FRAME_SIZE 160 // 8khz 8000*0.02=160 #define MAX_AMR_FRAME_SIZE 32 #define AMR_FRAME_COUNT_PER_SECOND 50 int amrEncodeMode[] = {4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200}; // amr 编码方式 @interface AmrPlayerReader() { FILE *_file; void *_destate; //帧头标识和帧大小 unsigned char _stdFrameHeader; int _stdFrameSize; } @property (nonatomic, assign) NSUInteger readedLength; @property (nonatomic, assign) double readedSecondCount; @end @implementation AmrPlayerReader const int myround(const double x) { return((int)(x+0.5)); } // 根据帧头计算当前帧大小 int caclAMRFrameSize(unsigned char frameHeader) { int mode; int temp1 = 0; int temp2 = 0; int frameSize; temp1 = frameHeader; // 编码方式编号 = 帧头的3-6位 temp1 &= 0x78; // 0111-1000 temp1 >>= 3; mode = amrEncodeMode[temp1]; // 计算amr音频数据帧大小 // 原理: amr 一帧对应20ms,那么一秒有50帧的音频数据 temp2 = myround((double)(((double)mode / (double)AMR_FRAME_COUNT_PER_SECOND) / (double)8)); frameSize = myround((double)temp2 + 0.5); return frameSize; } // 读第一个帧 - (参考帧) BOOL ReadAMRFrameFirst(FILE* fpamr, int* stdFrameSize, unsigned char* stdFrameHeader) { unsigned long curpos = ftell(fpamr); //记录当前位置,这一帧只是读取一下,并不做处理 fseek(fpamr, strlen(AMR_MAGIC_NUMBER), SEEK_SET); //先读帧头 fread(stdFrameHeader, 1, sizeof(unsigned char), fpamr); if (feof(fpamr)) return NO; fseek(fpamr,curpos,SEEK_SET); //还原位置 // 根据帧头计算帧大小 *stdFrameSize = caclAMRFrameSize(*stdFrameHeader); return YES; } long filesize(FILE *stream) { long curpos,length; curpos=ftell(stream); fseek(stream,0L,SEEK_END); length=ftell(stream); fseek(stream,curpos,SEEK_SET); return length; } + (double)durationOfAmrFilePath:(NSString*)filePath { //建立amr文件 if ([filePath hasPrefix:@"file://"]) { filePath = [filePath substringFromIndex:7]; } FILE *file = fopen((const char *)[filePath UTF8String], "rb"); if (file==0) { DLOG(@"打开文件失败:%s",__FUNCTION__); return 0; } unsigned char stdFrameHeader; int stdFrameSize; if(!ReadAMRFrameFirst(file, &stdFrameSize, &stdFrameHeader)){ return 0; } //检测此文件一共有多少帧 long fileSize = filesize(file); if(file){ fclose(file); } return ((fileSize - strlen(AMR_MAGIC_NUMBER))/(double)stdFrameSize)/(double)AMR_FRAME_COUNT_PER_SECOND; } - (double)duration { return [[self class]durationOfAmrFilePath:self.filePath]; } - (BOOL)openFileWithPlayer:(MLAudioPlayer*)player { _destate = 0; // amr 解压句柄 _destate = Decoder_Interface_init(); if(_destate==0){ return NO; } //建立amr文件 NSString *filePath = self.filePath; if ([filePath hasPrefix:@"file://"]) { filePath = [filePath substringFromIndex:7]; } _file = fopen((const char *)[filePath UTF8String], "rb"); if (_file==0) { DLOG(@"打开文件失败:%s",__FUNCTION__); return NO; } //忽略文件头大小 char magic[8]; static const char* amrHeader = AMR_MAGIC_NUMBER; int realReadedLength = fread(magic, sizeof(char), strlen(amrHeader), _file); if (strncmp(magic, amrHeader, strlen(amrHeader))) { return NO; } self.readedLength += realReadedLength; //读取一个参考帧 if(!ReadAMRFrameFirst(_file, &_stdFrameSize, &_stdFrameHeader)){ return NO; } // DLOG(@"帧大小%d,帧头%c",_stdFrameSize,_stdFrameHeader); return YES; } - (AudioStreamBasicDescription)customAudioFormatAfterOpenFile { AudioStreamBasicDescription format; format.mSampleRate = 8000; format.mChannelsPerFrame = 1; format.mFormatID = kAudioFormatLinearPCM; format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; format.mBitsPerChannel = 16; format.mBytesPerPacket = format.mBytesPerFrame = (format.mBitsPerChannel / 8) * format.mChannelsPerFrame; format.mFramesPerPacket = 1; return format; } - (NSData*)readDataFromFileWithPlayer:(MLAudioPlayer*)player andBufferSize:(NSUInteger)bufferSize error:(NSError**)error { //读取数据 if (!_file) { return nil; } //计算存储到bufferSize里需要读取多少帧 int needReadFrameCount = floor(bufferSize/(PCM_FRAME_SIZE*sizeof(short))); NSMutableData *data = [NSMutableData data]; unsigned char amrFrame[MAX_AMR_FRAME_SIZE]; short pcmFrame[PCM_FRAME_SIZE]; for (NSUInteger i=0; i #import @class MLAudioPlayer; typedef void (^MLAudioPlayerReceiveStoppedBlock)(); typedef void (^MLAudioPlayerReceiveErrorBlock)(NSError *error); /** * 错误标识 */ typedef NS_OPTIONS(NSUInteger, MLAudioPlayerErrorCode) { MLAudioPlayerErrorCodeAboutFile = 0, //关于文件操作的错误 MLAudioPlayerErrorCodeAboutQueue, //关于音频输入队列的错误 MLAudioPlayerErrorCodeAboutSession, //关于audio session的错误 MLAudioPlayerErrorCodeAboutOther, //关于其他的错误 }; @protocol FileReaderForMLAudioPlayer @required - (BOOL)openFileWithPlayer:(MLAudioPlayer*)player; - (AudioStreamBasicDescription)customAudioFormatAfterOpenFile; - (NSData*)readDataFromFileWithPlayer:(MLAudioPlayer*)player andBufferSize:(NSUInteger)bufferSize error:(NSError**)error; - (BOOL)completeReadWithPlayer:(MLAudioPlayer*)player withIsError:(BOOL)isError; @end @interface MLAudioPlayer : NSObject { @public //音频输入队列 AudioQueueRef _audioQueue; //音频输入数据format AudioStreamBasicDescription _audioFormat; } @property (nonatomic, assign) BOOL isPlaying; @property (nonatomic, weak) id fileReaderDelegate; @property (nonatomic, copy) MLAudioPlayerReceiveErrorBlock receiveErrorBlock; @property (nonatomic, copy) MLAudioPlayerReceiveStoppedBlock receiveStoppedBlock; - (void)startPlaying; - (void)stopPlaying; @end ================================================ FILE: MLRecorder/MLAudioPlayer/MLAudioPlayer.m ================================================ // // MLAudioPlayer.m // MLRecorder // // Created by molon on 5/22/14. // Copyright (c) 2014 molon. All rights reserved. // #import "MLAudioPlayer.h" #import #define kMLAudioPlayerErrorDomain @"MLAudioPlayerErrorDomain" #define kDefaultBufferDurationSeconds 0.5 #define kNumberAudioQueueBuffers 3 #define IfAudioQueueErrorPostAndReturn(operation,error) \ do{\ if(operation!=noErr) { \ [self postAErrorWithErrorCode:MLAudioPlayerErrorCodeAboutQueue andDescription:error]; \ return; \ } \ }while(0) @interface MLAudioPlayer() { //音频输出缓冲区 AudioQueueBufferRef _audioBuffers[kNumberAudioQueueBuffers]; } @property (nonatomic, assign) NSUInteger bufferByteSize; @property (nonatomic, assign) BOOL isPlayDone;//是否正常的播放完毕 @end @implementation MLAudioPlayer - (instancetype)init { self = [super init]; if (self) { self.isPlayDone = YES; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sensorStateChange:) name:UIDeviceProximityStateDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRouteChange:) name:AVAudioSessionRouteChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; } return self; } - (void)dealloc { NSAssert(!self.isPlaying, @"MLAudioPlayer dealloc之前必须停止播放"); // if (self.isPlaying){ // [self stopPlaying]; // } [[NSNotificationCenter defaultCenter] removeObserver:self]; DLOG(@"MLAudioPlayer dealloc"); } #pragma mark - read data callback void isRunningProc (void * inUserData,AudioQueueRef inAQ,AudioQueuePropertyID inID) { MLAudioPlayer *player = (__bridge MLAudioPlayer*)inUserData; UInt32 isRunning; UInt32 size = sizeof(isRunning); OSStatus result = AudioQueueGetProperty (inAQ, kAudioQueueProperty_IsRunning, &isRunning, &size); if ((result == noErr) && (!isRunning)&&player.isPlaying){ [player performSelector:@selector(stopPlaying) withObject:nil afterDelay:0.001f]; } } void outBufferHandler(void *inUserData,AudioQueueRef inAQ,AudioQueueBufferRef inCompleteAQBuffer) { MLAudioPlayer *player = (__bridge MLAudioPlayer*)inUserData; if (player.isPlayDone) { return; } //获取数据放进去 NSData *data = nil; if (player.fileReaderDelegate) { NSError *error = nil; data = [player.fileReaderDelegate readDataFromFileWithPlayer:player andBufferSize:player.bufferByteSize error:&error]; if (error) { [player postAErrorWithErrorCode:MLAudioPlayerErrorCodeAboutFile andDescription:@"读取数据失败"]; return; } } if (data.length>0) { memcpy(inCompleteAQBuffer->mAudioData, data.bytes, data.length); inCompleteAQBuffer->mAudioDataByteSize = data.length; inCompleteAQBuffer->mPacketDescriptionCount = 0; if(AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, 0, NULL)!=noErr){ [player postAErrorWithErrorCode:MLAudioPlayerErrorCodeAboutQueue andDescription:@"重准备音频输出缓存区失败"]; } }else{ player.isPlayDone = YES; AudioQueueStop(inAQ, false); //注意这里是停止没错但是传递的false,不会停止当前未完成的播放。 } } #pragma mark - control - (void)startPlaying { NSAssert(!self.isPlaying, @"播放必须先停止上一个才可开始新的"); NSError *error = nil; //设置audio session的category BOOL ret = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]; if (!ret) { [self postAErrorWithErrorCode:MLAudioPlayerErrorCodeAboutSession andDescription:@"为AVAudioSession设置Category失败"]; return; } //启用audio session ret = [[AVAudioSession sharedInstance] setActive:YES error:&error]; if (!ret) { [self postAErrorWithErrorCode:MLAudioPlayerErrorCodeAboutSession andDescription:@"Active AVAudioSession失败"]; return; } //需要检测delegate没实现方法就不行。 if (!self.fileReaderDelegate|| ![self.fileReaderDelegate respondsToSelector:@selector(customAudioFormatAfterOpenFile)]|| ![self.fileReaderDelegate respondsToSelector:@selector(openFileWithPlayer:)]|| ![self.fileReaderDelegate respondsToSelector:@selector(readDataFromFileWithPlayer:andBufferSize:error:)]|| ![self.fileReaderDelegate respondsToSelector:@selector(completeReadWithPlayer:withIsError:)]) { [self postAErrorWithErrorCode:MLAudioPlayerErrorCodeAboutOther andDescription:@"fileWriterDelegate的代理未设置或其代理方法不完整"]; return; } //打开文件 if (self.fileReaderDelegate&&![self.fileReaderDelegate openFileWithPlayer:self]) { [self postAErrorWithErrorCode:MLAudioPlayerErrorCodeAboutFile andDescription:@"为音频输出打开文件失败"]; return; } //得到format AudioStreamBasicDescription format = [self.fileReaderDelegate customAudioFormatAfterOpenFile]; memcpy(&_audioFormat, &format, sizeof(_audioFormat)); //简单检测下不支持可变速率 if (_audioFormat.mFramesPerPacket<=0||_audioFormat.mBytesPerPacket<=0) { [self postAErrorWithErrorCode:MLAudioPlayerErrorCodeAboutOther andDescription:@"format 设置有误,此player不支持VBR"]; return; } //设置音频输出队列 IfAudioQueueErrorPostAndReturn(AudioQueueNewOutput(&_audioFormat, outBufferHandler, (__bridge void *)(self), CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &_audioQueue),@"音频输出队列初始化失败"); //设置正在运行的回调,这个还真不知道啥时候执行,回头测试下 IfAudioQueueErrorPostAndReturn(AudioQueueAddPropertyListener(_audioQueue, kAudioQueueProperty_IsRunning,isRunningProc, (__bridge void *)(self)), @"adding property listener"); //计算估算的缓存区大小,这里我们忽略可变速率的情况 static const int maxBufferSize = 0x10000; static const int minBufferSize = 0x4000; //每秒采集的帧数/每个packet有的帧数,算出来每秒有多少packet,然后乘以秒数,即为inSeconds时间里需要的packet数目 //最后乘以参数给予的buffer最大的packet数目。即为估算的保守的buffer大小 Float64 numPacketsForTime = ceil(_audioFormat.mSampleRate* kDefaultBufferDurationSeconds)/ _audioFormat.mFramesPerPacket ; int bufferByteSize = ceil(numPacketsForTime * _audioFormat.mBytesPerPacket); bufferByteSize = bufferByteSize>maxBufferSize?maxBufferSize:bufferByteSize; bufferByteSize = bufferByteSize #import /** * 错误标识 */ typedef NS_OPTIONS(NSUInteger, MLAudioRealTimePlayerErrorCode) { MLAudioRealTimePlayerErrorCodeAboutQueue = 0, //关于音频输入队列的错误 MLAudioRealTimePlayerErrorCodeAboutSession, //关于audio session的错误 MLAudioRealTimePlayerErrorCodeAboutOther, //关于其他的错误 }; @interface MLAudioRealTimePlayer : NSObject { @public //音频输入队列 AudioQueueRef _audioQueue; //音频输入数据format AudioStreamBasicDescription _audioFormat; } //自定义format时候使用 - (instancetype)initWithAudioFormat:(AudioStreamBasicDescription)audioFormat; @property (nonatomic, assign,readonly) NSUInteger bufferByteSize; //可以设置音量默认是1.0 @property (nonatomic, assign) AudioQueueParameterValue volume; //注意得到错误之后,didReceiveStoppedBlock也会得到。其他类并非如此,但现在感觉这样才合理 @property (nonatomic, copy) void(^didReceiveErrorBlock)(MLAudioRealTimePlayer *player,NSError *error); @property (nonatomic, copy) void(^didReceiveStoppedBlock)(MLAudioRealTimePlayer *player); //追加PCM数据等待播放,注意只支持PCM数据,其他格式,请解码后传递 - (void)appendPacket:(NSData *)audioPacket; //清空未播放的数据,可以清除并且立即enqueue新的,即可忽略中间的未读段,一般不需要调用 - (void)cleanPackets; //开始播放 - (void)start; //这个方法会清空当前未播放的Buffer,并且停止播放。 - (void)stop; //其他,判断当前是否无数据播放 - (BOOL)isWaiting; @end ================================================ FILE: MLRecorder/MLAudioRealTimePlayer/MLAudioRealTimePlayer.m ================================================ // // MLAudioRealTimePlayer.m // MLRecorder // // Created by molon on 14/12/19. // Copyright (c) 2014年 molon. All rights reserved. // #import "MLAudioRealTimePlayer.h" #import #define kMLAudioRealTimePlayerErrorDomain @"MLAudioRealTimePlayerErrorDomain" #define kNumberAudioQueueBuffers 3 #define kDefaultBufferDurationSeconds 0.25 #define IfAudioQueueErrorPostAndReturn(operation,error) \ do{\ if(operation!=noErr) { \ [self postAErrorWithErrorCode:MLAudioRealTimePlayerErrorCodeAboutQueue andDescription:error]; \ return; \ } \ }while(0) @interface MLAudioRealTimePlayer() { //音频输出缓冲区 AudioQueueBufferRef _audioBuffers[kNumberAudioQueueBuffers]; //音频输出缓存区是否处于等待状态 BOOL _isWaitingOfAudioBuffers[kNumberAudioQueueBuffers]; } //标识当前是否正在播放中,注意是最准确的标识,根据audioqueue 队列的回调设置的 @property (nonatomic, assign) BOOL isPlaying; @property (nonatomic, strong) NSMutableArray *audioPackets; @property (nonatomic, assign) NSUInteger bufferByteSize; @end @implementation MLAudioRealTimePlayer static inline AudioStreamBasicDescription kDefaultAudioFormat() { static AudioStreamBasicDescription _defaultAudioFormat; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _defaultAudioFormat.mSampleRate = 8000; _defaultAudioFormat.mChannelsPerFrame = 1; _defaultAudioFormat.mFormatID = kAudioFormatLinearPCM; _defaultAudioFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; _defaultAudioFormat.mBitsPerChannel = 16; _defaultAudioFormat.mBytesPerPacket = _defaultAudioFormat.mBytesPerFrame = (_defaultAudioFormat.mBitsPerChannel / 8) * _defaultAudioFormat.mChannelsPerFrame; _defaultAudioFormat.mFramesPerPacket = 1; }); return _defaultAudioFormat; } - (void)dealloc { NSAssert(!self.isPlaying, @"MLAudioRealTimePlayer dealloc之前必须停止播放"); [[NSNotificationCenter defaultCenter] removeObserver:self]; DLOG(@"%@ dealloc",NSStringFromClass([self class])); } - (instancetype)init { self = [super init]; if (self) { AudioStreamBasicDescription format = kDefaultAudioFormat(); [self configureWithAudioFormat:format]; } return self; } - (instancetype)initWithAudioFormat:(AudioStreamBasicDescription)audioFormat { self = [super init]; if (self) { [self configureWithAudioFormat:audioFormat]; } return self; } - (void)configureWithAudioFormat:(AudioStreamBasicDescription)audioFormat { memcpy(&_audioFormat, &audioFormat, sizeof(_audioFormat)); //简单检测下不支持可变速率 if (_audioFormat.mFramesPerPacket<=0||_audioFormat.mBytesPerPacket<=0) { [self postAErrorWithErrorCode:MLAudioRealTimePlayerErrorCodeAboutOther andDescription:@"format 设置有误,此player不支持VBR"]; return; } //设置音频输出队列 IfAudioQueueErrorPostAndReturn(AudioQueueNewOutput(&_audioFormat, outBufferHandlerForMLRealTimePlayer, (__bridge void *)(self), CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &_audioQueue),@"音频输出队列初始化失败"); //计算估算的缓存区大小,这里我们忽略可变速率的情况 static const int maxBufferSize = 0x10000; static const int minBufferSize = 0x4000; //每秒采集的帧数/每个packet有的帧数,算出来每秒有多少packet,然后乘以秒数,即为inSeconds时间里需要的packet数目 //最后乘以参数给予的buffer最大的packet数目。即为估算的保守的buffer大小 Float64 numPacketsForTime = ceil(_audioFormat.mSampleRate* kDefaultBufferDurationSeconds)/ _audioFormat.mFramesPerPacket ; int bufferByteSize = ceil(numPacketsForTime * _audioFormat.mBytesPerPacket); bufferByteSize = bufferByteSize>maxBufferSize?maxBufferSize:bufferByteSize; bufferByteSize = bufferByteSizemUserData = &(_isWaitingOfAudioBuffers[i]); } //设置正在运行的回调,这个一般在执行start和stop的时候会执行 IfAudioQueueErrorPostAndReturn(AudioQueueAddPropertyListener(_audioQueue, kAudioQueueProperty_IsRunning,isRunningProcForMLRealTimePlayer, (__bridge void *)(self)), @"adding property listener"); //设置音量 self.volume = 1.0f; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sensorStateChange:) name:UIDeviceProximityStateDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRouteChange:) name:AVAudioSessionRouteChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; } #pragma mark - getter - (NSMutableArray *)audioPackets { if (!_audioPackets) { _audioPackets = [NSMutableArray array]; } return _audioPackets; } #pragma mark - error - (void)postAErrorWithErrorCode:(MLAudioRealTimePlayerErrorCode)code andDescription:(NSString*)description { NSError *error = [NSError errorWithDomain:kMLAudioRealTimePlayerErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey:description}]; if (self.didReceiveErrorBlock) { self.didReceiveErrorBlock(self,error); } //停止 [self stop]; } #pragma mark - callback - (void)enqueueToPlayPacket:(NSData*)audioPacket withAudioBuffer:(AudioQueueBufferRef)audioBuffer { NSAssert([audioPacket length] <= audioBuffer->mAudioDataBytesCapacity, @"Error: audioPacket太大了"); //把数据塞进去准备播放 audioBuffer->mAudioDataByteSize = [audioPacket length]; memcpy(audioBuffer->mAudioData, audioPacket.bytes, audioPacket.length); audioBuffer->mPacketDescriptionCount = 0; IfAudioQueueErrorPostAndReturn(AudioQueueEnqueueBuffer(_audioQueue, audioBuffer, 0, NULL),@"准备音频输出缓存区失败"); } void isRunningProcForMLRealTimePlayer(void * inUserData,AudioQueueRef inAQ,AudioQueuePropertyID inID) { MLAudioRealTimePlayer *player = (__bridge MLAudioRealTimePlayer*)inUserData; UInt32 isRunning; UInt32 size = sizeof(isRunning); OSStatus result = AudioQueueGetProperty (inAQ, kAudioQueueProperty_IsRunning, &isRunning, &size); if (result == noErr){ player.isPlaying = isRunning; if (!player.isPlaying) { if (player.didReceiveStoppedBlock) { player.didReceiveStoppedBlock(player); } } } } void outBufferHandlerForMLRealTimePlayer(void *inUserData,AudioQueueRef inAQ,AudioQueueBufferRef inCompleteAQBuffer) { MLAudioRealTimePlayer *player = (__bridge MLAudioRealTimePlayer*)inUserData; BOOL *p_isWaitingOfAudioBuffer = (BOOL *)(inCompleteAQBuffer->mUserData); //这里就设置为在等待塞数据 *p_isWaitingOfAudioBuffer = YES; if (player.audioPackets.count>0) { NSData *packet = [player.audioPackets objectAtIndex:0]; //删除丫的 [player.audioPackets removeObjectAtIndex:0]; //投递播放请求 [player enqueueToPlayPacket:packet withAudioBuffer:inCompleteAQBuffer]; // NSLog(@"buffer %p 投递了 packet %p",inCompleteAQBuffer,packet); //塞了完毕,即将播放 *p_isWaitingOfAudioBuffer = NO; }else{ // NSLog(@"此Buffuer等待"); } } #pragma mark - outcall - (void)setVolume:(AudioQueueParameterValue)volume { _volume = volume; IfAudioQueueErrorPostAndReturn(AudioQueueSetParameter(_audioQueue, kAudioQueueParam_Volume, volume),@"为音频输出设置音量失败"); } - (BOOL)isWaiting { //三个缓冲区现在都处于闲置状态才算数 for (NSInteger i=0; i=kNumberAudioQueueBuffers) { AudioQueueReset(_audioQueue); // NSLog(@"等待后继续填充实时数据"); //塞入三个Buffer for (NSInteger i=0; i #import "MLAudioRecorder.h" @interface AmrRecordWriter : NSObject @property (nonatomic, copy) NSString *filePath; @property (nonatomic, assign) unsigned long maxFileSize; @property (nonatomic, assign) double maxSecondCount; @end ================================================ FILE: MLRecorder/MLAudioRecorder/AmrRecordWriter.m ================================================ // // AmrRecordWriter.m // MLAudioRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import "AmrRecordWriter.h" //amr编码 #import "interf_enc.h" @interface AmrRecordWriter() { FILE *_file; void *_destate; } @property (nonatomic, assign) unsigned long recordedFileSize; @property (nonatomic, assign) double recordedSecondCount; @property (nonatomic, assign) unsigned long lastBytesLength; @property (nonatomic, assign) unsigned char * lastBytes; @end @implementation AmrRecordWriter - (BOOL)createFileWithRecorder:(MLAudioRecorder*)recoder; { _destate = 0; // amr 压缩句柄 _destate = Encoder_Interface_init(0); if(_destate==0){ return NO; } //建立amr文件 _file = fopen((const char *)[self.filePath UTF8String], "wb+"); if (_file==0) { NSLog(@"建立文件失败:%s",__FUNCTION__); return NO; } self.recordedFileSize = 0; self.recordedSecondCount = 0; if(!self.lastBytes) self.lastBytes = malloc(320); self.lastBytesLength = 0; //写入文件头 static const char* amrHeader = "#!AMR\n"; if(fwrite(amrHeader, 1, strlen(amrHeader), _file)==0){ return NO; } self.recordedFileSize += strlen(amrHeader); return YES; } - (BOOL)writeIntoFileWithData:(NSData*)data withRecorder:(MLAudioRecorder*)recoder inAQ:(AudioQueueRef) inAQ inStartTime:(const AudioTimeStamp *)inStartTime inNumPackets:(UInt32)inNumPackets inPacketDesc:(const AudioStreamPacketDescription*)inPacketDesc { if (self.maxSecondCount>0){ if (self.recordedSecondCount+recoder.bufferDurationSeconds>self.maxSecondCount){ // NSLog(@"录音超时"); dispatch_async(dispatch_get_main_queue(), ^{ [recoder stopRecording]; }); return YES; } self.recordedSecondCount += recoder.bufferDurationSeconds; } //编码 const void *recordingData = data.bytes; NSUInteger pcmLen = data.length; // NSLog(@"%lu",(unsigned long)pcmLen); if (pcmLen<=0){ return YES; } if (pcmLen%2!=0){ pcmLen--; //防止意外,如果不是偶数,情愿减去最后一个字节。 // NSLog(@"不是偶数"); } unsigned char * bytes = malloc(pcmLen+320); memset(bytes,0,pcmLen+320); memcpy(bytes,self.lastBytes,self.lastBytesLength); memcpy(bytes+self.lastBytesLength, recordingData, pcmLen); pcmLen += self.lastBytesLength; self.lastBytesLength=0; unsigned char buffer[320]; for (int i =0; i < pcmLen ;i+=160*2) { short *pPacket = (short *)((unsigned char*)bytes+i); if (pcmLen-i<160*2){ self.lastBytesLength = pcmLen - i; memcpy(self.lastBytes, pPacket, self.lastBytesLength); continue; //不是一个完整的就拜拜,等待下次数据传递进来再处理 } memset(buffer, 0, sizeof(buffer)); //encode int recvLen = Encoder_Interface_Encode(_destate,MR515,pPacket,buffer,0); if (recvLen>0) { if (self.maxFileSize>0){ if(self.recordedFileSize+recvLen>self.maxFileSize){ // NSLog(@"录音文件过大"); dispatch_async(dispatch_get_main_queue(), ^{ [recoder stopRecording]; }); free(bytes); return YES;//超过了最大文件大小就直接返回 } } if(fwrite(buffer,1,recvLen,_file)==0){ free(bytes); return NO;//只有写文件有可能出错。返回NO } self.recordedFileSize += recvLen; } } free(bytes); return YES; } - (BOOL)completeWriteWithRecorder:(MLAudioRecorder*)recoder withIsError:(BOOL)isError { //关闭就关闭吧。管他关闭成功与否 if(_file){ fclose(_file); _file = 0; } if (_destate){ Encoder_Interface_exit((void*)_destate); _destate = 0; } if(_lastBytes) { free(_lastBytes); _lastBytes = nil; } _lastBytesLength = 0; return YES; } - (void)dealloc { if(_file){ fclose(_file); _file = 0; } if (_destate){ Encoder_Interface_exit((void*)_destate); _destate = 0; } if(_lastBytes) { free(_lastBytes); _lastBytes = nil; } _lastBytesLength = 0; } @end ================================================ FILE: MLRecorder/MLAudioRecorder/CafRecordWriter.h ================================================ // // CafRecordWriter.h // MLAudioRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import #import "MLAudioRecorder.h" @interface CafRecordWriter : NSObject @property (nonatomic, copy) NSString *filePath; @end ================================================ FILE: MLRecorder/MLAudioRecorder/CafRecordWriter.m ================================================ // // CafRecordWriter.m // MLAudioRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import "CafRecordWriter.h" @interface CafRecordWriter() { AudioFileID mRecordFile; SInt64 recordPacketCount; } @end @implementation CafRecordWriter - (BOOL)createFileWithRecorder:(MLAudioRecorder*)recoder { //建立文件 recordPacketCount = 0; CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, (CFStringRef)self.filePath, NULL); OSStatus err = AudioFileCreateWithURL(url, kAudioFileCAFType, (const AudioStreamBasicDescription *)(&(recoder->_recordFormat)), kAudioFileFlags_EraseFile, &mRecordFile); CFRelease(url); return err==noErr; } - (BOOL)writeIntoFileWithData:(NSData*)data withRecorder:(MLAudioRecorder*)recoder inAQ:(AudioQueueRef) inAQ inStartTime:(const AudioTimeStamp *)inStartTime inNumPackets:(UInt32)inNumPackets inPacketDesc:(const AudioStreamPacketDescription*)inPacketDesc { OSStatus err = AudioFileWritePackets(mRecordFile, FALSE, data.length, inPacketDesc, recordPacketCount, &inNumPackets, data.bytes); if (err!=noErr) { return NO; } recordPacketCount += inNumPackets; return YES; } - (BOOL)completeWriteWithRecorder:(MLAudioRecorder*)recoder withIsError:(BOOL)isError { if (mRecordFile) { AudioFileClose(mRecordFile); } // NSData *data = [[NSData alloc]initWithContentsOfFile:self.filePath]; // DLOG(@"文件长度%ld",data.length); return YES; } -(void)dealloc { if (mRecordFile) { AudioFileClose(mRecordFile); } } @end ================================================ FILE: MLRecorder/MLAudioRecorder/MLAudioMeterObserver.h ================================================ // // MLAudioMeterObserver.h // MLRecorder // // Created by molon on 5/13/14. // Copyright (c) 2014 molon. All rights reserved. // #import #import @class MLAudioMeterObserver; typedef void (^MLAudioMeterObserverActionBlock)(NSArray *levelMeterStates,MLAudioMeterObserver *meterObserver); typedef void (^MLAudioMeterObserverErrorBlock)(NSError *error,MLAudioMeterObserver *meterObserver); /** * 错误标识 */ typedef NS_OPTIONS(NSUInteger, MLAudioMeterObserverErrorCode) { MLAudioMeterObserverErrorCodeAboutQueue, //关于音频输入队列的错误 }; @interface LevelMeterState : NSObject @property (nonatomic, assign) Float32 mAveragePower; @property (nonatomic, assign) Float32 mPeakPower; @end @interface MLAudioMeterObserver : NSObject { AudioQueueRef _audioQueue; AudioQueueLevelMeterState *_levelMeterStates; } @property AudioQueueRef audioQueue; @property (nonatomic, copy) MLAudioMeterObserverActionBlock actionBlock; @property (nonatomic, copy) MLAudioMeterObserverErrorBlock errorBlock; @property (nonatomic, assign) NSTimeInterval refreshInterval; //刷新间隔,默认0.1 /** * 根据meterStates计算出音量,音量为 0-1 * */ + (Float32)volumeForLevelMeterStates:(NSArray*)levelMeterStates; @end ================================================ FILE: MLRecorder/MLAudioRecorder/MLAudioMeterObserver.m ================================================ // // MLAudioMeterObserver.m // MLRecorder // // Created by molon on 5/13/14. // Copyright (c) 2014 molon. All rights reserved. // #import "MLAudioMeterObserver.h" #define kDefaultRefreshInterval 0.1 //默认0.1秒刷新一次 #define kMLAudioMeterObserverErrorDomain @"MLAudioMeterObserverErrorDomain" #define IfAudioQueueErrorPostAndReturn(operation,error) \ if(operation!=noErr) { \ [self postAErrorWithErrorCode:MLAudioMeterObserverErrorCodeAboutQueue andDescription:error]; \ return; \ } \ @implementation LevelMeterState @end @interface MLAudioMeterObserver() @property (nonatomic, strong) NSTimer *timer; @property (nonatomic, assign) NSUInteger channelCount; @end @implementation MLAudioMeterObserver - (instancetype)init { self = [super init]; if (self) { //这里默认用_设置下吧。免得直接初始化了timer _refreshInterval = kDefaultRefreshInterval; self.channelCount = 1; //象征性的初始化一下 _levelMeterStates = (AudioQueueLevelMeterState*)malloc(sizeof(AudioQueueLevelMeterState) * self.channelCount); } return self; } - (void)dealloc { [self.timer invalidate]; self.timer = nil; free(_levelMeterStates); DLOG(@"MLAudioMeterObserver dealloc"); } #pragma mark - setter and getter - (void)setRefreshInterval:(NSTimeInterval)refreshInterval { _refreshInterval = refreshInterval; //重置timer [self.timer invalidate]; self.timer = [NSTimer scheduledTimerWithTimeInterval:refreshInterval target:self selector:@selector(refresh) userInfo:nil repeats:YES ]; } - (AudioQueueRef)audioQueue { return _audioQueue; } - (void)setAudioQueue:(AudioQueueRef)audioQueue { if (_audioQueue!=NULL&&audioQueue == _audioQueue){ //一样的就无需再处理 return; } //处理关闭定时器 [self.timer invalidate]; self.timer = nil; //根据新的音频队列重新初始化_levelMeterStates的内存块 if (audioQueue==NULL){ return; } _audioQueue = audioQueue; //检测这玩意是否支持光谱图 UInt32 val = 1; IfAudioQueueErrorPostAndReturn(AudioQueueSetProperty(audioQueue, kAudioQueueProperty_EnableLevelMetering, &val, sizeof(UInt32)), @"couldn't enable metering"); if (!val){ DLOG(@"不支持光谱图"); //需要发送错误 return; } // now check the number of channels in the new queue, we will need to reallocate if this has changed AudioStreamBasicDescription queueFormat; UInt32 data_sz = sizeof(queueFormat); IfAudioQueueErrorPostAndReturn(AudioQueueGetProperty(audioQueue, kAudioQueueProperty_StreamDescription, &queueFormat, &data_sz), @"couldn't get stream description"); self.channelCount = queueFormat.mChannelsPerFrame; //重新初始化大小 _levelMeterStates = (AudioQueueLevelMeterState*)realloc(_levelMeterStates, self.channelCount * sizeof(AudioQueueLevelMeterState)); //重新设置timer self.timer = [NSTimer scheduledTimerWithTimeInterval:self.refreshInterval target:self selector:@selector(refresh) userInfo:nil repeats:YES ]; } - (void)refresh { UInt32 data_sz = sizeof(AudioQueueLevelMeterState) * self.channelCount; IfAudioQueueErrorPostAndReturn(AudioQueueGetProperty(_audioQueue, kAudioQueueProperty_CurrentLevelMeterDB, _levelMeterStates, &data_sz),@"获取meter数据失败"); //转化成LevelMeterState数组传递到block NSMutableArray *meters = [NSMutableArray arrayWithCapacity:self.channelCount]; for (int i=0; i #import //录音停止事件的block回调,作用参考MLAudioRecorderDelegate的recordStopped和recordError: typedef void (^MLAudioRecorderReceiveStoppedBlock)(); typedef void (^MLAudioRecorderReceiveErrorBlock)(NSError *error); /** * 错误标识 */ typedef NS_OPTIONS(NSUInteger, MLAudioRecorderErrorCode) { MLAudioRecorderErrorCodeAboutFile = 0, //关于文件操作的错误 MLAudioRecorderErrorCodeAboutQueue, //关于音频输入队列的错误 MLAudioRecorderErrorCodeAboutSession, //关于audio session的错误 MLAudioRecorderErrorCodeAboutOther, //关于其他的错误 }; @class MLAudioRecorder; /** * 处理写文件操作的,实际是转码的操作在其中进行。算作可扩展自定义的转码器 * 当然如果是实时语音的需求的话,就可以在此处理编码后发送语音数据到对方 * PS:这里的三个方法是在后台线程中处理的 */ @protocol FileWriterForMLAudioRecorder @optional - (AudioStreamBasicDescription)customAudioFormatBeforeCreateFile; @required /** * 在录音开始时候建立文件和写入文件头信息等操作 * */ - (BOOL)createFileWithRecorder:(MLAudioRecorder*)recoder; /** * 写入音频输入数据,内部处理转码等其他逻辑 * 能传递过来的都传递了。以方便多能扩展使用 */ - (BOOL)writeIntoFileWithData:(NSData*)data withRecorder:(MLAudioRecorder*)recoder inAQ:(AudioQueueRef) inAQ inStartTime:(const AudioTimeStamp *)inStartTime inNumPackets:(UInt32)inNumPackets inPacketDesc:(const AudioStreamPacketDescription*)inPacketDesc; /** * 文件写入完成之后的操作,例如文件句柄关闭等,isError表示是否是因为错误才调用的 * */ - (BOOL)completeWriteWithRecorder:(MLAudioRecorder*)recoder withIsError:(BOOL)isError; @end @protocol MLAudioRecorderDelegate @required /** * 录音遇到了错误,例如创建文件失败啊。写入失败啊。关闭文件失败啊,等等。 */ - (void)recordError:(NSError *)error; @optional /** * 录音被停止 * 一般是在writer delegate中因为一些状况意外停止录音获得此事件时候使用,参考AmrRecordWriter里实现。 */ - (void)recordStopped; @end @interface MLAudioRecorder : NSObject { @public //音频输入队列 AudioQueueRef _audioQueue; //音频输入数据format AudioStreamBasicDescription _recordFormat; } /** * 是否正在录音 */ @property (atomic, assign,readonly) BOOL isRecording; /** * 这俩是当前的采样率和缓冲区采集秒数,根据情况可以设置(对其设置必须在startRecording之前才有效),随意设置可能有意外发生。 * 这俩属性被标识为原子性的,读取写入是线程安全的。 */ @property (atomic, assign) NSUInteger sampleRate; @property (atomic, assign) double bufferDurationSeconds; /** * 处理写文件操作的,实际是转码的操作在其中进行。算作可扩展自定义的转码器 */ @property (nonatomic, weak) id fileWriterDelegate; /** * 参考MLAudioRecorderReceiveStoppedBlock和MLAudioRecorderReceiveErrorBlock */ @property (nonatomic, copy) MLAudioRecorderReceiveStoppedBlock receiveStoppedBlock; @property (nonatomic, copy) MLAudioRecorderReceiveErrorBlock receiveErrorBlock; /** * 参考MLAudioRecorderDelegate */ @property (nonatomic, assign) id delegate; - (void)startRecording; - (void)stopRecording; @end ================================================ FILE: MLRecorder/MLAudioRecorder/MLAudioRecorder.m ================================================ // // MLAudioRecorder.m // MLAudioRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import "MLAudioRecorder.h" #import /** * 缓存区的个数,3个一般不用改 */ #define kNumberAudioQueueBuffers 3 /** * 每次的音频输入队列缓存区所保存的是多少秒的数据 */ #define kDefaultBufferDurationSeconds 0.5 /** * 采样率,要转码为amr的话必须为8000 */ #define kDefaultSampleRate 8000 #define kMLAudioRecorderErrorDomain @"MLAudioRecorderErrorDomain" #define IfAudioQueueErrorPostAndReturn(operation,error) \ do{\ if(operation!=noErr) { \ [self postAErrorWithErrorCode:MLAudioRecorderErrorCodeAboutQueue andDescription:error]; \ return; \ } \ }while(0) @interface MLAudioRecorder() { //音频输入缓冲区 AudioQueueBufferRef _audioBuffers[kNumberAudioQueueBuffers]; } @property (nonatomic, strong) dispatch_queue_t writeFileQueue; @property (nonatomic, strong) dispatch_semaphore_t semError; //一个信号量,用来保证队列中写文件错误事件处理只调用一次 @property (nonatomic, assign) BOOL isRecording; @end @implementation MLAudioRecorder - (instancetype)init { self = [super init]; if (self) { //建立写入文件线程队列,串行,和一个信号量标识 self.writeFileQueue = dispatch_queue_create("com.molon.MLAudioRecorder.writeFileQueue", NULL); self.sampleRate = kDefaultSampleRate; self.bufferDurationSeconds = kDefaultBufferDurationSeconds; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; } return self; } - (void)dealloc { NSAssert(!self.isRecording, @"MLAudioRecorder dealloc之前必须停止录音"); //由于上面做了需要在外部调用stopRecording的限制,下面这块不需要了。 // if (self.isRecording){ // [self stopRecording]; // } [[NSNotificationCenter defaultCenter] removeObserver:self]; DLOG(@"MLAudioRecorder dealloc"); } // 回调函数 void inputBufferHandler(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime,UInt32 inNumPackets, const AudioStreamPacketDescription *inPacketDesc) { MLAudioRecorder *recorder = (__bridge MLAudioRecorder*)inUserData; if (inNumPackets > 0) { NSData *pcmData = [[NSData alloc]initWithBytes:inBuffer->mAudioData length:inBuffer->mAudioDataByteSize]; if (pcmData&&pcmData.length>0) { //在后台串行队列中去处理文件写入 dispatch_async(recorder.writeFileQueue, ^{ if(recorder.fileWriterDelegate&&![recorder.fileWriterDelegate writeIntoFileWithData:pcmData withRecorder:recorder inAQ:inAQ inStartTime:inStartTime inNumPackets:inNumPackets inPacketDesc:inPacketDesc]){ //保证只处理了一次 if (dispatch_semaphore_wait(recorder.semError,DISPATCH_TIME_NOW)==0){ //回到主线程 dispatch_async(dispatch_get_main_queue(),^{ [recorder postAErrorWithErrorCode:MLAudioRecorderErrorCodeAboutFile andDescription:@"写入文件失败"]; }); } } }); } } if (recorder.isRecording) { if(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL)!=noErr){ recorder.isRecording = NO; //这里直接设置下,能防止队列中3个缓存,重复post error //回到主线程 dispatch_async(dispatch_get_main_queue(),^{ [recorder postAErrorWithErrorCode:MLAudioRecorderErrorCodeAboutQueue andDescription:@"重准备音频输入缓存区失败"]; }); } } } - (void)startRecording { NSAssert(!self.isRecording, @"录音必须先停止上一个才可开始新的"); NSError *error = nil; //设置audio session的category BOOL ret = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]; if (!ret) { [self postAErrorWithErrorCode:MLAudioRecorderErrorCodeAboutSession andDescription:@"为AVAudioSession设置Category失败"]; return; } //启用audio session ret = [[AVAudioSession sharedInstance] setActive:YES error:&error]; if (!ret) { [self postAErrorWithErrorCode:MLAudioRecorderErrorCodeAboutSession andDescription:@"Active AVAudioSession失败"]; return; } if(!self.fileWriterDelegate||![self.fileWriterDelegate respondsToSelector:@selector(createFileWithRecorder:)]||![self.fileWriterDelegate respondsToSelector:@selector(writeIntoFileWithData:withRecorder:inAQ:inStartTime:inNumPackets:inPacketDesc:)]||![self.fileWriterDelegate respondsToSelector:@selector(completeWriteWithRecorder:withIsError:)]){ [self postAErrorWithErrorCode:MLAudioRecorderErrorCodeAboutOther andDescription:@"fileWriterDelegate的代理未设置或其代理方法不完整"]; return; } //设置录音的format数据 if (self.fileWriterDelegate&&[self.fileWriterDelegate respondsToSelector:@selector(customAudioFormatBeforeCreateFile)]) { dispatch_sync(self.writeFileQueue, ^{ AudioStreamBasicDescription format = [self.fileWriterDelegate customAudioFormatBeforeCreateFile]; memcpy(&_recordFormat, &format,sizeof(_recordFormat)); }); }else{ [self setupAudioFormat:kAudioFormatLinearPCM SampleRate:self.sampleRate]; } _recordFormat.mSampleRate = self.sampleRate; //建立文件,顺便同步下串行队列,防止意外前面有没处理的 __block BOOL isContinue = YES;; dispatch_sync(self.writeFileQueue, ^{ if(self.fileWriterDelegate&&![self.fileWriterDelegate createFileWithRecorder:self]){ dispatch_async(dispatch_get_main_queue(),^{ [self postAErrorWithErrorCode:MLAudioRecorderErrorCodeAboutFile andDescription:@"为音频输入建立文件失败"]; }); isContinue = NO; } }); if(!isContinue){ return; } self.semError = dispatch_semaphore_create(0); //重新初始化信号量标识 dispatch_semaphore_signal(self.semError); //设置有一个信号 //设置录音的回调函数 IfAudioQueueErrorPostAndReturn(AudioQueueNewInput(&_recordFormat, inputBufferHandler, (__bridge void *)(self), NULL, NULL, 0, &_audioQueue),@"音频输入队列初始化失败"); //计算估算的缓存区大小 int frames = (int)ceil(self.bufferDurationSeconds * _recordFormat.mSampleRate); int bufferByteSize = frames * _recordFormat.mBytesPerFrame; DLOG(@"缓冲区大小:%d",bufferByteSize); //创建缓冲器 for (int i = 0; i < kNumberAudioQueueBuffers; ++i){ IfAudioQueueErrorPostAndReturn(AudioQueueAllocateBuffer(_audioQueue, bufferByteSize, &_audioBuffers[i]),@"为音频输入队列建立缓冲区失败"); IfAudioQueueErrorPostAndReturn(AudioQueueEnqueueBuffer(_audioQueue, _audioBuffers[i], 0, NULL),@"为音频输入队列缓冲区做准备失败"); } // 开始录音 IfAudioQueueErrorPostAndReturn(AudioQueueStart(_audioQueue, NULL),@"开始音频输入队列失败"); self.isRecording = YES; } - (void)stopRecording { // DLOG(@"stopRecording"); if (self.isRecording) { self.isRecording = NO; //停止录音队列和移除缓冲区,以及关闭session,这里无需考虑成功与否 AudioQueueStop(_audioQueue, true); AudioQueueDispose(_audioQueue, true); [[AVAudioSession sharedInstance] setActive:NO error:nil]; //这里直接做同步 __block BOOL isContinue = YES; dispatch_sync(self.writeFileQueue, ^{ if (self.fileWriterDelegate&&![self.fileWriterDelegate completeWriteWithRecorder:self withIsError:NO]) { dispatch_async(dispatch_get_main_queue(),^{ [self postAErrorWithErrorCode:MLAudioRecorderErrorCodeAboutFile andDescription:@"为音频输入关闭文件失败"]; }); isContinue = NO; } }); if(!isContinue) return; DLOG(@"录音结束"); if(self.delegate&&[self.delegate respondsToSelector:@selector(recordStopped)]){ [self.delegate recordStopped]; } if (self.receiveStoppedBlock){ self.receiveStoppedBlock(); } } } // 设置录音格式 - (void)setupAudioFormat:(UInt32) inFormatID SampleRate:(int)sampeleRate { //重置下 memset(&_recordFormat, 0, sizeof(_recordFormat)); //设置采样率,这里先获取系统默认的测试下 //TODO: //采样率的意思是每秒需要采集的帧数 _recordFormat.mSampleRate = sampeleRate;//[[AVAudioSession sharedInstance] sampleRate]; //设置通道数,这里先使用系统的测试下 //TODO: _recordFormat.mChannelsPerFrame = 1;//(UInt32)[[AVAudioSession sharedInstance] inputNumberOfChannels]; // DLOG(@"sampleRate:%f,通道数:%d",_recordFormat.mSampleRate,_recordFormat.mChannelsPerFrame); //设置format,怎么称呼不知道。 _recordFormat.mFormatID = inFormatID; if (inFormatID == kAudioFormatLinearPCM){ //这个屌属性不知道干啥的。, _recordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; //每个通道里,一帧采集的bit数目 _recordFormat.mBitsPerChannel = 16; //结果分析: 8bit为1byte,即为1个通道里1帧需要采集2byte数据,再*通道数,即为所有通道采集的byte数目。 //所以这里结果赋值给每帧需要采集的byte数目,然后这里的packet也等于一帧的数据。 //至于为什么要这样。。。不知道。。。 _recordFormat.mBytesPerPacket = _recordFormat.mBytesPerFrame = (_recordFormat.mBitsPerChannel / 8) * _recordFormat.mChannelsPerFrame; _recordFormat.mFramesPerPacket = 1; } } - (void)postAErrorWithErrorCode:(MLAudioRecorderErrorCode)code andDescription:(NSString*)description { //关闭可能还未关闭的东西,无需考虑结果 self.isRecording = NO; AudioQueueStop(_audioQueue, true); AudioQueueDispose(_audioQueue, true); [[AVAudioSession sharedInstance] setActive:NO error:nil]; if(self.fileWriterDelegate){ dispatch_sync(self.writeFileQueue, ^{ [self.fileWriterDelegate completeWriteWithRecorder:self withIsError:YES]; }); } DLOG(@"录音发生错误"); NSError *error = [NSError errorWithDomain:kMLAudioRecorderErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey:description}]; if (self.delegate&&[self.delegate respondsToSelector:@selector(recordError:)]){ [self.delegate recordError:error]; } if( self.receiveErrorBlock){ self.receiveErrorBlock(error); } } #pragma mark - notification - (void)sessionInterruption:(NSNotification *)notification { AVAudioSessionInterruptionType interruptionType = [[[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; if (AVAudioSessionInterruptionTypeBegan == interruptionType) { DLOG(@"begin interruption"); //直接停止录音 [self stopRecording]; } else if (AVAudioSessionInterruptionTypeEnded == interruptionType) { DLOG(@"end interruption"); } } @end ================================================ FILE: MLRecorder/MLAudioRecorder/Mp3RecordWriter.h ================================================ // // Mp3RecordWriter.h // MLRecorder // // Created by molon on 5/13/14. // Copyright (c) 2014 molon. All rights reserved. // /** * 一般使用采样率 8000 缓冲区秒数为0.5 * 暂时现在的lame库不支持arm64,所以工程里的设置arch为$(ARCHS_STANDARD_32_BIT) */ #import #import "MLAudioRecorder.h" @interface Mp3RecordWriter : NSObject @property (nonatomic, copy) NSString *filePath; @property (nonatomic, assign) unsigned long maxFileSize; @property (nonatomic, assign) double maxSecondCount; @end ================================================ FILE: MLRecorder/MLAudioRecorder/Mp3RecordWriter.m ================================================ // // Mp3RecordWriter.m // MLRecorder // // Created by molon on 5/13/14. // Copyright (c) 2014 molon. All rights reserved. // #import "Mp3RecordWriter.h" #import @interface Mp3RecordWriter() { FILE *_file; lame_t _lame; } @property (nonatomic, assign) unsigned long recordedFileSize; @property (nonatomic, assign) double recordedSecondCount; @end @implementation Mp3RecordWriter - (BOOL)createFileWithRecorder:(MLAudioRecorder*)recoder; { // mp3压缩参数 _lame = lame_init(); lame_set_num_channels(_lame, 1); lame_set_in_samplerate(_lame, 8000); lame_set_out_samplerate(_lame, 8000); lame_set_brate(_lame, 128); lame_set_mode(_lame, 1); lame_set_quality(_lame, 2); lame_init_params(_lame); //建立mp3文件 _file = fopen((const char *)[self.filePath UTF8String], "wb+"); if (_file==0) { DLOG(@"建立文件失败:%s",__FUNCTION__); return NO; } self.recordedFileSize = 0; self.recordedSecondCount = 0; DLOG(@"filePath:%@",self.filePath); return YES; } - (BOOL)writeIntoFileWithData:(NSData*)data withRecorder:(MLAudioRecorder*)recoder inAQ:(AudioQueueRef) inAQ inStartTime:(const AudioTimeStamp *)inStartTime inNumPackets:(UInt32)inNumPackets inPacketDesc:(const AudioStreamPacketDescription*)inPacketDesc { if (self.maxSecondCount>0){ if (self.recordedSecondCount+recoder.bufferDurationSeconds>self.maxSecondCount){ // DLOG(@"录音超时"); dispatch_async(dispatch_get_main_queue(), ^{ [recoder stopRecording]; }); return YES; } self.recordedSecondCount += recoder.bufferDurationSeconds; } //编码 short *recordingData = (short*)data.bytes; int pcmLen = data.length; if (pcmLen<2){ return YES; } int nsamples = pcmLen / 2; unsigned char buffer[pcmLen]; // mp3 encode int recvLen = lame_encode_buffer(_lame, recordingData, recordingData, nsamples, buffer, pcmLen); // add NSMutable if (recvLen>0) { if (self.maxFileSize>0){ if(self.recordedFileSize+recvLen>self.maxFileSize){ // DLOG(@"录音文件过大"); dispatch_async(dispatch_get_main_queue(), ^{ [recoder stopRecording]; }); return YES;//超过了最大文件大小就直接返回 } } if(fwrite(buffer,1,recvLen,_file)==0){ return NO; } self.recordedFileSize += recvLen; } return YES; } - (BOOL)completeWriteWithRecorder:(MLAudioRecorder*)recoder withIsError:(BOOL)isError { //关闭就关闭吧。管他关闭成功与否 if(_file){ fclose(_file); _file = 0; } if(_lame){ lame_close(_lame); _lame = 0; } return YES; } - (void)dealloc { if(_file){ fclose(_file); _file = 0; } if(_lame){ lame_close(_lame); _lame = 0; } } @end ================================================ FILE: MLRecorder/MLAudioRecorder/amr_en_de/include/opencore-amrnb/interf_dec.h ================================================ /* ------------------------------------------------------------------ * Copyright (C) 2009 Martin Storsjo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ #ifndef OPENCORE_AMRNB_INTERF_DEC_H #define OPENCORE_AMRNB_INTERF_DEC_H #ifdef __cplusplus extern "C" { #endif void* Decoder_Interface_init(void); void Decoder_Interface_exit(void* state); void Decoder_Interface_Decode(void* state, const unsigned char* in, short* out, int bfi); #ifdef __cplusplus } #endif #endif ================================================ FILE: MLRecorder/MLAudioRecorder/amr_en_de/include/opencore-amrnb/interf_enc.h ================================================ /* ------------------------------------------------------------------ * Copyright (C) 2009 Martin Storsjo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ #ifndef OPENCORE_AMRNB_INTERF_ENC_H #define OPENCORE_AMRNB_INTERF_ENC_H #ifdef __cplusplus extern "C" { #endif #ifndef AMRNB_WRAPPER_INTERNAL /* Copied from enc/src/gsmamr_enc.h */ enum Mode { MR475 = 0,/* 4.75 kbps */ MR515, /* 5.15 kbps */ MR59, /* 5.90 kbps */ MR67, /* 6.70 kbps */ MR74, /* 7.40 kbps */ MR795, /* 7.95 kbps */ MR102, /* 10.2 kbps */ MR122, /* 12.2 kbps */ MRDTX, /* DTX */ N_MODES /* Not Used */ }; #endif void* Encoder_Interface_init(int dtx); void Encoder_Interface_exit(void* state); int Encoder_Interface_Encode(void* state, enum Mode mode, const short* speech, unsigned char* out, int forceSpeech); #ifdef __cplusplus } #endif #endif ================================================ FILE: MLRecorder/MLAudioRecorder/amr_en_de/include/opencore-amrwb/dec_if.h ================================================ /* ------------------------------------------------------------------ * Copyright (C) 2009 Martin Storsjo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ #ifndef OPENCORE_AMRWB_DEC_IF_H #define OPENCORE_AMRWB_DEC_IF_H #ifdef __cplusplus extern "C" { #endif #define _good_frame 0 void* D_IF_init(void); void D_IF_decode(void* state, const unsigned char* bits, short* synth, int bfi); void D_IF_exit(void* state); #ifdef __cplusplus } #endif #endif ================================================ FILE: MLRecorder/MLAudioRecorder/amr_en_de/include/opencore-amrwb/if_rom.h ================================================ /* ------------------------------------------------------------------ * Copyright (C) 2009 Martin Storsjo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ #ifndef OPENCORE_AMRWB_IF_ROM_H #define OPENCORE_AMRWB_IF_ROM_H #ifdef __cplusplus extern "C" { #endif #include typedef int16_t Word16; #ifdef __cplusplus } #endif #endif ================================================ FILE: MLRecorder/MLAudioRecorder/mp3_en_de/lame.framework/Headers/lame.h ================================================ /* * Interface to MP3 LAME encoding engine * * Copyright (c) 1999 Mark Taylor * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* $Id: lame.h,v 1.189.2.1 2012/01/08 23:49:58 robert Exp $ */ #ifndef LAME_LAME_H #define LAME_LAME_H /* for size_t typedef */ #include /* for va_list typedef */ #include /* for FILE typedef, TODO: remove when removing lame_mp3_tags_fid */ #include #if defined(__cplusplus) extern "C" { #endif typedef void (*lame_report_function)(const char *format, va_list ap); #if defined(WIN32) || defined(_WIN32) #undef CDECL #define CDECL __cdecl #else #define CDECL #endif #define DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1 typedef enum vbr_mode_e { vbr_off=0, vbr_mt, /* obsolete, same as vbr_mtrh */ vbr_rh, vbr_abr, vbr_mtrh, vbr_max_indicator, /* Don't use this! It's used for sanity checks. */ vbr_default=vbr_mtrh /* change this to change the default VBR mode of LAME */ } vbr_mode; /* MPEG modes */ typedef enum MPEG_mode_e { STEREO = 0, JOINT_STEREO, DUAL_CHANNEL, /* LAME doesn't supports this! */ MONO, NOT_SET, MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ } MPEG_mode; /* Padding types */ typedef enum Padding_type_e { PAD_NO = 0, PAD_ALL, PAD_ADJUST, PAD_MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ } Padding_type; /*presets*/ typedef enum preset_mode_e { /*values from 8 to 320 should be reserved for abr bitrates*/ /*for abr I'd suggest to directly use the targeted bitrate as a value*/ ABR_8 = 8, ABR_320 = 320, V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/ VBR_10 = 410, V8 = 420, VBR_20 = 420, V7 = 430, VBR_30 = 430, V6 = 440, VBR_40 = 440, V5 = 450, VBR_50 = 450, V4 = 460, VBR_60 = 460, V3 = 470, VBR_70 = 470, V2 = 480, VBR_80 = 480, V1 = 490, VBR_90 = 490, V0 = 500, VBR_100 = 500, /*still there for compatibility*/ R3MIX = 1000, STANDARD = 1001, EXTREME = 1002, INSANE = 1003, STANDARD_FAST = 1004, EXTREME_FAST = 1005, MEDIUM = 1006, MEDIUM_FAST = 1007 } preset_mode; /*asm optimizations*/ typedef enum asm_optimizations_e { MMX = 1, AMD_3DNOW = 2, SSE = 3 } asm_optimizations; /* psychoacoustic model */ typedef enum Psy_model_e { PSY_GPSYCHO = 1, PSY_NSPSYTUNE = 2 } Psy_model; /* buffer considerations */ typedef enum buffer_constraint_e { MDB_DEFAULT=0, MDB_STRICT_ISO=1, MDB_MAXIMUM=2 } buffer_constraint; struct lame_global_struct; typedef struct lame_global_struct lame_global_flags; typedef lame_global_flags *lame_t; /*********************************************************************** * * The LAME API * These functions should be called, in this order, for each * MP3 file to be encoded. See the file "API" for more documentation * ***********************************************************************/ /* * REQUIRED: * initialize the encoder. sets default for all encoder parameters, * returns NULL if some malloc()'s failed * otherwise returns pointer to structure needed for all future * API calls. */ lame_global_flags * CDECL lame_init(void); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* obsolete version */ int CDECL lame_init_old(lame_global_flags *); #endif /* * OPTIONAL: * set as needed to override defaults */ /******************************************************************** * input stream description ***********************************************************************/ /* number of samples. default = 2^32-1 */ int CDECL lame_set_num_samples(lame_global_flags *, unsigned long); unsigned long CDECL lame_get_num_samples(const lame_global_flags *); /* input sample rate in Hz. default = 44100hz */ int CDECL lame_set_in_samplerate(lame_global_flags *, int); int CDECL lame_get_in_samplerate(const lame_global_flags *); /* number of channels in input stream. default=2 */ int CDECL lame_set_num_channels(lame_global_flags *, int); int CDECL lame_get_num_channels(const lame_global_flags *); /* scale the input by this amount before encoding. default=1 (not used by decoding routines) */ int CDECL lame_set_scale(lame_global_flags *, float); float CDECL lame_get_scale(const lame_global_flags *); /* scale the channel 0 (left) input by this amount before encoding. default=1 (not used by decoding routines) */ int CDECL lame_set_scale_left(lame_global_flags *, float); float CDECL lame_get_scale_left(const lame_global_flags *); /* scale the channel 1 (right) input by this amount before encoding. default=1 (not used by decoding routines) */ int CDECL lame_set_scale_right(lame_global_flags *, float); float CDECL lame_get_scale_right(const lame_global_flags *); /* output sample rate in Hz. default = 0, which means LAME picks best value based on the amount of compression. MPEG only allows: MPEG1 32, 44.1, 48khz MPEG2 16, 22.05, 24 MPEG2.5 8, 11.025, 12 (not used by decoding routines) */ int CDECL lame_set_out_samplerate(lame_global_flags *, int); int CDECL lame_get_out_samplerate(const lame_global_flags *); /******************************************************************** * general control parameters ***********************************************************************/ /* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */ int CDECL lame_set_analysis(lame_global_flags *, int); int CDECL lame_get_analysis(const lame_global_flags *); /* 1 = write a Xing VBR header frame. default = 1 this variable must have been added by a Hungarian notation Windows programmer :-) */ int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int); int CDECL lame_get_bWriteVbrTag(const lame_global_flags *); /* 1=decode only. use lame/mpglib to convert mp3/ogg to wav. default=0 */ int CDECL lame_set_decode_only(lame_global_flags *, int); int CDECL lame_get_decode_only(const lame_global_flags *); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* 1=encode a Vorbis .ogg file. default=0 */ /* DEPRECATED */ int CDECL lame_set_ogg(lame_global_flags *, int); int CDECL lame_get_ogg(const lame_global_flags *); #endif /* internal algorithm selection. True quality is determined by the bitrate but this variable will effect quality by selecting expensive or cheap algorithms. quality=0..9. 0=best (very slow). 9=worst. recommended: 2 near-best quality, not too slow 5 good quality, fast 7 ok quality, really fast */ int CDECL lame_set_quality(lame_global_flags *, int); int CDECL lame_get_quality(const lame_global_flags *); /* mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono default: lame picks based on compression ration and input channels */ int CDECL lame_set_mode(lame_global_flags *, MPEG_mode); MPEG_mode CDECL lame_get_mode(const lame_global_flags *); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* mode_automs. Use a M/S mode with a switching threshold based on compression ratio DEPRECATED */ int CDECL lame_set_mode_automs(lame_global_flags *, int); int CDECL lame_get_mode_automs(const lame_global_flags *); #endif /* force_ms. Force M/S for all frames. For testing only. default = 0 (disabled) */ int CDECL lame_set_force_ms(lame_global_flags *, int); int CDECL lame_get_force_ms(const lame_global_flags *); /* use free_format? default = 0 (disabled) */ int CDECL lame_set_free_format(lame_global_flags *, int); int CDECL lame_get_free_format(const lame_global_flags *); /* perform ReplayGain analysis? default = 0 (disabled) */ int CDECL lame_set_findReplayGain(lame_global_flags *, int); int CDECL lame_get_findReplayGain(const lame_global_flags *); /* decode on the fly. Search for the peak sample. If the ReplayGain * analysis is enabled then perform the analysis on the decoded data * stream. default = 0 (disabled) * NOTE: if this option is set the build-in decoder should not be used */ int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int); int CDECL lame_get_decode_on_the_fly(const lame_global_flags *); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* DEPRECATED: now does the same as lame_set_findReplayGain() default = 0 (disabled) */ int CDECL lame_set_ReplayGain_input(lame_global_flags *, int); int CDECL lame_get_ReplayGain_input(const lame_global_flags *); /* DEPRECATED: now does the same as lame_set_decode_on_the_fly() && lame_set_findReplayGain() default = 0 (disabled) */ int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int); int CDECL lame_get_ReplayGain_decode(const lame_global_flags *); /* DEPRECATED: now does the same as lame_set_decode_on_the_fly() default = 0 (disabled) */ int CDECL lame_set_findPeakSample(lame_global_flags *, int); int CDECL lame_get_findPeakSample(const lame_global_flags *); #endif /* counters for gapless encoding */ int CDECL lame_set_nogap_total(lame_global_flags*, int); int CDECL lame_get_nogap_total(const lame_global_flags*); int CDECL lame_set_nogap_currentindex(lame_global_flags* , int); int CDECL lame_get_nogap_currentindex(const lame_global_flags*); /* * OPTIONAL: * Set printf like error/debug/message reporting functions. * The second argument has to be a pointer to a function which looks like * void my_debugf(const char *format, va_list ap) * { * (void) vfprintf(stdout, format, ap); * } * If you use NULL as the value of the pointer in the set function, the * lame buildin function will be used (prints to stderr). * To quiet any output you have to replace the body of the example function * with just "return;" and use it in the set function. */ int CDECL lame_set_errorf(lame_global_flags *, lame_report_function); int CDECL lame_set_debugf(lame_global_flags *, lame_report_function); int CDECL lame_set_msgf (lame_global_flags *, lame_report_function); /* set one of brate compression ratio. default is compression ratio of 11. */ int CDECL lame_set_brate(lame_global_flags *, int); int CDECL lame_get_brate(const lame_global_flags *); int CDECL lame_set_compression_ratio(lame_global_flags *, float); float CDECL lame_get_compression_ratio(const lame_global_flags *); int CDECL lame_set_preset( lame_global_flags* gfp, int ); int CDECL lame_set_asm_optimizations( lame_global_flags* gfp, int, int ); /******************************************************************** * frame params ***********************************************************************/ /* mark as copyright. default=0 */ int CDECL lame_set_copyright(lame_global_flags *, int); int CDECL lame_get_copyright(const lame_global_flags *); /* mark as original. default=1 */ int CDECL lame_set_original(lame_global_flags *, int); int CDECL lame_get_original(const lame_global_flags *); /* error_protection. Use 2 bytes from each frame for CRC checksum. default=0 */ int CDECL lame_set_error_protection(lame_global_flags *, int); int CDECL lame_get_error_protection(const lame_global_flags *); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */ int CDECL lame_set_padding_type(lame_global_flags *, Padding_type); Padding_type CDECL lame_get_padding_type(const lame_global_flags *); #endif /* MP3 'private extension' bit Meaningless. default=0 */ int CDECL lame_set_extension(lame_global_flags *, int); int CDECL lame_get_extension(const lame_global_flags *); /* enforce strict ISO compliance. default=0 */ int CDECL lame_set_strict_ISO(lame_global_flags *, int); int CDECL lame_get_strict_ISO(const lame_global_flags *); /******************************************************************** * quantization/noise shaping ***********************************************************************/ /* disable the bit reservoir. For testing only. default=0 */ int CDECL lame_set_disable_reservoir(lame_global_flags *, int); int CDECL lame_get_disable_reservoir(const lame_global_flags *); /* select a different "best quantization" function. default=0 */ int CDECL lame_set_quant_comp(lame_global_flags *, int); int CDECL lame_get_quant_comp(const lame_global_flags *); int CDECL lame_set_quant_comp_short(lame_global_flags *, int); int CDECL lame_get_quant_comp_short(const lame_global_flags *); int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/ int CDECL lame_get_experimentalX(const lame_global_flags *); /* another experimental option. for testing only */ int CDECL lame_set_experimentalY(lame_global_flags *, int); int CDECL lame_get_experimentalY(const lame_global_flags *); /* another experimental option. for testing only */ int CDECL lame_set_experimentalZ(lame_global_flags *, int); int CDECL lame_get_experimentalZ(const lame_global_flags *); /* Naoki's psycho acoustic model. default=0 */ int CDECL lame_set_exp_nspsytune(lame_global_flags *, int); int CDECL lame_get_exp_nspsytune(const lame_global_flags *); void CDECL lame_set_msfix(lame_global_flags *, double); float CDECL lame_get_msfix(const lame_global_flags *); /******************************************************************** * VBR control ***********************************************************************/ /* Types of VBR. default = vbr_off = CBR */ int CDECL lame_set_VBR(lame_global_flags *, vbr_mode); vbr_mode CDECL lame_get_VBR(const lame_global_flags *); /* VBR quality level. 0=highest 9=lowest */ int CDECL lame_set_VBR_q(lame_global_flags *, int); int CDECL lame_get_VBR_q(const lame_global_flags *); /* VBR quality level. 0=highest 9=lowest, Range [0,...,10[ */ int CDECL lame_set_VBR_quality(lame_global_flags *, float); float CDECL lame_get_VBR_quality(const lame_global_flags *); /* Ignored except for VBR=vbr_abr (ABR mode) */ int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int); int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *); int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int); int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *); int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int); int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *); /* 1=strictly enforce VBR_min_bitrate. Normally it will be violated for analog silence */ int CDECL lame_set_VBR_hard_min(lame_global_flags *, int); int CDECL lame_get_VBR_hard_min(const lame_global_flags *); /* for preset */ #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else int CDECL lame_set_preset_expopts(lame_global_flags *, int); #endif /******************************************************************** * Filtering control ***********************************************************************/ /* freq in Hz to apply lowpass. Default = 0 = lame chooses. -1 = disabled */ int CDECL lame_set_lowpassfreq(lame_global_flags *, int); int CDECL lame_get_lowpassfreq(const lame_global_flags *); /* width of transition band, in Hz. Default = one polyphase filter band */ int CDECL lame_set_lowpasswidth(lame_global_flags *, int); int CDECL lame_get_lowpasswidth(const lame_global_flags *); /* freq in Hz to apply highpass. Default = 0 = lame chooses. -1 = disabled */ int CDECL lame_set_highpassfreq(lame_global_flags *, int); int CDECL lame_get_highpassfreq(const lame_global_flags *); /* width of transition band, in Hz. Default = one polyphase filter band */ int CDECL lame_set_highpasswidth(lame_global_flags *, int); int CDECL lame_get_highpasswidth(const lame_global_flags *); /******************************************************************** * psycho acoustics and other arguments which you should not change * unless you know what you are doing ***********************************************************************/ /* only use ATH for masking */ int CDECL lame_set_ATHonly(lame_global_flags *, int); int CDECL lame_get_ATHonly(const lame_global_flags *); /* only use ATH for short blocks */ int CDECL lame_set_ATHshort(lame_global_flags *, int); int CDECL lame_get_ATHshort(const lame_global_flags *); /* disable ATH */ int CDECL lame_set_noATH(lame_global_flags *, int); int CDECL lame_get_noATH(const lame_global_flags *); /* select ATH formula */ int CDECL lame_set_ATHtype(lame_global_flags *, int); int CDECL lame_get_ATHtype(const lame_global_flags *); /* lower ATH by this many db */ int CDECL lame_set_ATHlower(lame_global_flags *, float); float CDECL lame_get_ATHlower(const lame_global_flags *); /* select ATH adaptive adjustment type */ int CDECL lame_set_athaa_type( lame_global_flags *, int); int CDECL lame_get_athaa_type( const lame_global_flags *); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* select the loudness approximation used by the ATH adaptive auto-leveling */ int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int); int CDECL lame_get_athaa_loudapprox( const lame_global_flags *); #endif /* adjust (in dB) the point below which adaptive ATH level adjustment occurs */ int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float); float CDECL lame_get_athaa_sensitivity( const lame_global_flags* ); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* OBSOLETE: predictability limit (ISO tonality formula) */ int CDECL lame_set_cwlimit(lame_global_flags *, int); int CDECL lame_get_cwlimit(const lame_global_flags *); #endif /* allow blocktypes to differ between channels? default: 0 for jstereo, 1 for stereo */ int CDECL lame_set_allow_diff_short(lame_global_flags *, int); int CDECL lame_get_allow_diff_short(const lame_global_flags *); /* use temporal masking effect (default = 1) */ int CDECL lame_set_useTemporal(lame_global_flags *, int); int CDECL lame_get_useTemporal(const lame_global_flags *); /* use temporal masking effect (default = 1) */ int CDECL lame_set_interChRatio(lame_global_flags *, float); float CDECL lame_get_interChRatio(const lame_global_flags *); /* disable short blocks */ int CDECL lame_set_no_short_blocks(lame_global_flags *, int); int CDECL lame_get_no_short_blocks(const lame_global_flags *); /* force short blocks */ int CDECL lame_set_force_short_blocks(lame_global_flags *, int); int CDECL lame_get_force_short_blocks(const lame_global_flags *); /* Input PCM is emphased PCM (for instance from one of the rarely emphased CDs), it is STRONGLY not recommended to use this, because psycho does not take it into account, and last but not least many decoders ignore these bits */ int CDECL lame_set_emphasis(lame_global_flags *, int); int CDECL lame_get_emphasis(const lame_global_flags *); /************************************************************************/ /* internal variables, cannot be set... */ /* provided because they may be of use to calling application */ /************************************************************************/ /* version 0=MPEG-2 1=MPEG-1 (2=MPEG-2.5) */ int CDECL lame_get_version(const lame_global_flags *); /* encoder delay */ int CDECL lame_get_encoder_delay(const lame_global_flags *); /* padding appended to the input to make sure decoder can fully decode all input. Note that this value can only be calculated during the call to lame_encoder_flush(). Before lame_encoder_flush() has been called, the value of encoder_padding = 0. */ int CDECL lame_get_encoder_padding(const lame_global_flags *); /* size of MPEG frame */ int CDECL lame_get_framesize(const lame_global_flags *); /* number of PCM samples buffered, but not yet encoded to mp3 data. */ int CDECL lame_get_mf_samples_to_encode( const lame_global_flags* gfp ); /* size (bytes) of mp3 data buffered, but not yet encoded. this is the number of bytes which would be output by a call to lame_encode_flush_nogap. NOTE: lame_encode_flush() will return more bytes than this because it will encode the reamining buffered PCM samples before flushing the mp3 buffers. */ int CDECL lame_get_size_mp3buffer( const lame_global_flags* gfp ); /* number of frames encoded so far */ int CDECL lame_get_frameNum(const lame_global_flags *); /* lame's estimate of the total number of frames to be encoded only valid if calling program set num_samples */ int CDECL lame_get_totalframes(const lame_global_flags *); /* RadioGain value. Multiplied by 10 and rounded to the nearest. */ int CDECL lame_get_RadioGain(const lame_global_flags *); /* AudiophileGain value. Multipled by 10 and rounded to the nearest. */ int CDECL lame_get_AudiophileGain(const lame_global_flags *); /* the peak sample */ float CDECL lame_get_PeakSample(const lame_global_flags *); /* Gain change required for preventing clipping. The value is correct only if peak sample searching was enabled. If negative then the waveform already does not clip. The value is multiplied by 10 and rounded up. */ int CDECL lame_get_noclipGainChange(const lame_global_flags *); /* user-specified scale factor required for preventing clipping. Value is correct only if peak sample searching was enabled and no user-specified scaling was performed. If negative then either the waveform already does not clip or the value cannot be determined */ float CDECL lame_get_noclipScale(const lame_global_flags *); /* * REQUIRED: * sets more internal configuration based on data provided above. * returns -1 if something failed. */ int CDECL lame_init_params(lame_global_flags *); /* * OPTIONAL: * get the version number, in a string. of the form: * "3.63 (beta)" or just "3.63". */ const char* CDECL get_lame_version ( void ); const char* CDECL get_lame_short_version ( void ); const char* CDECL get_lame_very_short_version ( void ); const char* CDECL get_psy_version ( void ); const char* CDECL get_lame_url ( void ); const char* CDECL get_lame_os_bitness ( void ); /* * OPTIONAL: * get the version numbers in numerical form. */ typedef struct { /* generic LAME version */ int major; int minor; int alpha; /* 0 if not an alpha version */ int beta; /* 0 if not a beta version */ /* version of the psy model */ int psy_major; int psy_minor; int psy_alpha; /* 0 if not an alpha version */ int psy_beta; /* 0 if not a beta version */ /* compile time features */ const char *features; /* Don't make assumptions about the contents! */ } lame_version_t; void CDECL get_lame_version_numerical(lame_version_t *); /* * OPTIONAL: * print internal lame configuration to message handler */ void CDECL lame_print_config(const lame_global_flags* gfp); void CDECL lame_print_internals( const lame_global_flags *gfp); /* * input pcm data, output (maybe) mp3 frames. * This routine handles all buffering, resampling and filtering for you. * * return code number of bytes output in mp3buf. Can be 0 * -1: mp3buf was too small * -2: malloc() problem * -3: lame_init_params() not called * -4: psycho acoustic problems * * The required mp3buf_size can be computed from num_samples, * samplerate and encoding rate, but here is a worst case estimate: * * mp3buf_size in bytes = 1.25*num_samples + 7200 * * I think a tighter bound could be: (mt, March 2000) * MPEG1: * num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512 * MPEG2: * num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256 * * but test first if you use that! * * set mp3buf_size = 0 and LAME will not check if mp3buf_size is * large enough. * * NOTE: * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels * will be averaged into the L channel before encoding only the L channel * This will overwrite the data in buffer_l[] and buffer_r[]. * */ int CDECL lame_encode_buffer ( lame_global_flags* gfp, /* global context handle */ const short int buffer_l [], /* PCM data for left channel */ const short int buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* * as above, but input has L & R channel data interleaved. * NOTE: * num_samples = number of samples in the L (or R) * channel, not the total number of samples in pcm[] */ int CDECL lame_encode_buffer_interleaved( lame_global_flags* gfp, /* global context handlei */ short int pcm[], /* PCM data for left and right channel, interleaved */ int num_samples, /* number of samples per channel, _not_ number of samples in pcm[] */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ int mp3buf_size ); /* number of valid octets in this stream */ /* as lame_encode_buffer, but for 'float's. * !! NOTE: !! data must still be scaled to be in the same range as * short int, +/- 32768 */ int CDECL lame_encode_buffer_float( lame_global_flags* gfp, /* global context handle */ const float pcm_l [], /* PCM data for left channel */ const float pcm_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* as lame_encode_buffer, but for 'float's. * !! NOTE: !! data must be scaled to +/- 1 full scale */ int CDECL lame_encode_buffer_ieee_float( lame_t gfp, const float pcm_l [], /* PCM data for left channel */ const float pcm_r [], /* PCM data for right channel */ const int nsamples, unsigned char * mp3buf, const int mp3buf_size); int CDECL lame_encode_buffer_interleaved_ieee_float( lame_t gfp, const float pcm[], /* PCM data for left and right channel, interleaved */ const int nsamples, unsigned char * mp3buf, const int mp3buf_size); /* as lame_encode_buffer, but for 'double's. * !! NOTE: !! data must be scaled to +/- 1 full scale */ int CDECL lame_encode_buffer_ieee_double( lame_t gfp, const double pcm_l [], /* PCM data for left channel */ const double pcm_r [], /* PCM data for right channel */ const int nsamples, unsigned char * mp3buf, const int mp3buf_size); int CDECL lame_encode_buffer_interleaved_ieee_double( lame_t gfp, const double pcm[], /* PCM data for left and right channel, interleaved */ const int nsamples, unsigned char * mp3buf, const int mp3buf_size); /* as lame_encode_buffer, but for long's * !! NOTE: !! data must still be scaled to be in the same range as * short int, +/- 32768 * * This scaling was a mistake (doesn't allow one to exploit full * precision of type 'long'. Use lame_encode_buffer_long2() instead. * */ int CDECL lame_encode_buffer_long( lame_global_flags* gfp, /* global context handle */ const long buffer_l [], /* PCM data for left channel */ const long buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* Same as lame_encode_buffer_long(), but with correct scaling. * !! NOTE: !! data must still be scaled to be in the same range as * type 'long'. Data should be in the range: +/- 2^(8*size(long)-1) * */ int CDECL lame_encode_buffer_long2( lame_global_flags* gfp, /* global context handle */ const long buffer_l [], /* PCM data for left channel */ const long buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* as lame_encode_buffer, but for int's * !! NOTE: !! input should be scaled to the maximum range of 'int' * If int is 4 bytes, then the values should range from * +/- 2147483648. * * This routine does not (and cannot, without loosing precision) use * the same scaling as the rest of the lame_encode_buffer() routines. * */ int CDECL lame_encode_buffer_int( lame_global_flags* gfp, /* global context handle */ const int buffer_l [], /* PCM data for left channel */ const int buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* * REQUIRED: * lame_encode_flush will flush the intenal PCM buffers, padding with * 0's to make sure the final frame is complete, and then flush * the internal MP3 buffers, and thus may return a * final few mp3 frames. 'mp3buf' should be at least 7200 bytes long * to hold all possible emitted data. * * will also write id3v1 tags (if any) into the bitstream * * return code = number of bytes output to mp3buf. Can be 0 */ int CDECL lame_encode_flush( lame_global_flags * gfp, /* global context handle */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ int size); /* number of valid octets in this stream */ /* * OPTIONAL: * lame_encode_flush_nogap will flush the internal mp3 buffers and pad * the last frame with ancillary data so it is a complete mp3 frame. * * 'mp3buf' should be at least 7200 bytes long * to hold all possible emitted data. * * After a call to this routine, the outputed mp3 data is complete, but * you may continue to encode new PCM samples and write future mp3 data * to a different file. The two mp3 files will play back with no gaps * if they are concatenated together. * * This routine will NOT write id3v1 tags into the bitstream. * * return code = number of bytes output to mp3buf. Can be 0 */ int CDECL lame_encode_flush_nogap( lame_global_flags * gfp, /* global context handle */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ int size); /* number of valid octets in this stream */ /* * OPTIONAL: * Normally, this is called by lame_init_params(). It writes id3v2 and * Xing headers into the front of the bitstream, and sets frame counters * and bitrate histogram data to 0. You can also call this after * lame_encode_flush_nogap(). */ int CDECL lame_init_bitstream( lame_global_flags * gfp); /* global context handle */ /* * OPTIONAL: some simple statistics * a bitrate histogram to visualize the distribution of used frame sizes * a stereo mode histogram to visualize the distribution of used stereo * modes, useful in joint-stereo mode only * 0: LR left-right encoded * 1: LR-I left-right and intensity encoded (currently not supported) * 2: MS mid-side encoded * 3: MS-I mid-side and intensity encoded (currently not supported) * * attention: don't call them after lame_encode_finish * suggested: lame_encode_flush -> lame_*_hist -> lame_close */ void CDECL lame_bitrate_hist( const lame_global_flags * gfp, int bitrate_count[14] ); void CDECL lame_bitrate_kbps( const lame_global_flags * gfp, int bitrate_kbps [14] ); void CDECL lame_stereo_mode_hist( const lame_global_flags * gfp, int stereo_mode_count[4] ); void CDECL lame_bitrate_stereo_mode_hist ( const lame_global_flags * gfp, int bitrate_stmode_count[14][4] ); void CDECL lame_block_type_hist ( const lame_global_flags * gfp, int btype_count[6] ); void CDECL lame_bitrate_block_type_hist ( const lame_global_flags * gfp, int bitrate_btype_count[14][6] ); #if (DEPRECATED_OR_OBSOLETE_CODE_REMOVED && 0) #else /* * OPTIONAL: * lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file * pointer fid. These calls perform forward and backwards seeks, so make * sure fid is a real file. Make sure lame_encode_flush has been called, * and all mp3 data has been written to the file before calling this * function. * NOTE: * if VBR tags are turned off by the user, or turned off by LAME because * the output is not a regular file, this call does nothing * NOTE: * LAME wants to read from the file to skip an optional ID3v2 tag, so * make sure you opened the file for writing and reading. * NOTE: * You can call lame_get_lametag_frame instead, if you want to insert * the lametag yourself. */ void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid); #endif /* * OPTIONAL: * lame_get_lametag_frame copies the final LAME-tag into 'buffer'. * The function returns the number of bytes copied into buffer, or * the required buffer size, if the provided buffer is too small. * Function failed, if the return value is larger than 'size'! * Make sure lame_encode flush has been called before calling this function. * NOTE: * if VBR tags are turned off by the user, or turned off by LAME, * this call does nothing and returns 0. * NOTE: * LAME inserted an empty frame in the beginning of mp3 audio data, * which you have to replace by the final LAME-tag frame after encoding. * In case there is no ID3v2 tag, usually this frame will be the very first * data in your mp3 file. If you put some other leading data into your * file, you'll have to do some bookkeeping about where to write this buffer. */ size_t CDECL lame_get_lametag_frame( const lame_global_flags *, unsigned char* buffer, size_t size); /* * REQUIRED: * final call to free all remaining buffers */ int CDECL lame_close (lame_global_flags *); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* * OBSOLETE: * lame_encode_finish combines lame_encode_flush() and lame_close() in * one call. However, once this call is made, the statistics routines * will no longer work because the data will have been cleared, and * lame_mp3_tags_fid() cannot be called to add data to the VBR header */ int CDECL lame_encode_finish( lame_global_flags* gfp, unsigned char* mp3buf, int size ); #endif /********************************************************************* * * decoding * * a simple interface to mpglib, part of mpg123, is also included if * libmp3lame is compiled with HAVE_MPGLIB * *********************************************************************/ struct hip_global_struct; typedef struct hip_global_struct hip_global_flags; typedef hip_global_flags *hip_t; typedef struct { int header_parsed; /* 1 if header was parsed and following data was computed */ int stereo; /* number of channels */ int samplerate; /* sample rate */ int bitrate; /* bitrate */ int mode; /* mp3 frame type */ int mode_ext; /* mp3 frame type */ int framesize; /* number of samples per mp3 frame */ /* this data is only computed if mpglib detects a Xing VBR header */ unsigned long nsamp; /* number of samples in mp3 file. */ int totalframes; /* total number of frames in mp3 file */ /* this data is not currently computed by the mpglib routines */ int framenum; /* frames decoded counter */ } mp3data_struct; /* required call to initialize decoder */ hip_t CDECL hip_decode_init(void); /* cleanup call to exit decoder */ int CDECL hip_decode_exit(hip_t gfp); /* HIP reporting functions */ void CDECL hip_set_errorf(hip_t gfp, lame_report_function f); void CDECL hip_set_debugf(hip_t gfp, lame_report_function f); void CDECL hip_set_msgf (hip_t gfp, lame_report_function f); /********************************************************************* * input 1 mp3 frame, output (maybe) pcm data. * * nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r); * * input: * len : number of bytes of mp3 data in mp3buf * mp3buf[len] : mp3 data to be decoded * * output: * nout: -1 : decoding error * 0 : need more data before we can complete the decode * >0 : returned 'nout' samples worth of data in pcm_l,pcm_r * pcm_l[nout] : left channel data * pcm_r[nout] : right channel data * *********************************************************************/ int CDECL hip_decode( hip_t gfp , unsigned char * mp3buf , size_t len , short pcm_l[] , short pcm_r[] ); /* same as hip_decode, and also returns mp3 header data */ int CDECL hip_decode_headers( hip_t gfp , unsigned char* mp3buf , size_t len , short pcm_l[] , short pcm_r[] , mp3data_struct* mp3data ); /* same as hip_decode, but returns at most one frame */ int CDECL hip_decode1( hip_t gfp , unsigned char* mp3buf , size_t len , short pcm_l[] , short pcm_r[] ); /* same as hip_decode1, but returns at most one frame and mp3 header data */ int CDECL hip_decode1_headers( hip_t gfp , unsigned char* mp3buf , size_t len , short pcm_l[] , short pcm_r[] , mp3data_struct* mp3data ); /* same as hip_decode1_headers, but also returns enc_delay and enc_padding from VBR Info tag, (-1 if no info tag was found) */ int CDECL hip_decode1_headersB( hip_t gfp , unsigned char* mp3buf , size_t len , short pcm_l[] , short pcm_r[] , mp3data_struct* mp3data , int *enc_delay , int *enc_padding ); /* OBSOLETE: * lame_decode... functions are there to keep old code working * but it is strongly recommended to replace calls by hip_decode... * function calls, see above. */ #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else int CDECL lame_decode_init(void); int CDECL lame_decode( unsigned char * mp3buf, int len, short pcm_l[], short pcm_r[] ); int CDECL lame_decode_headers( unsigned char* mp3buf, int len, short pcm_l[], short pcm_r[], mp3data_struct* mp3data ); int CDECL lame_decode1( unsigned char* mp3buf, int len, short pcm_l[], short pcm_r[] ); int CDECL lame_decode1_headers( unsigned char* mp3buf, int len, short pcm_l[], short pcm_r[], mp3data_struct* mp3data ); int CDECL lame_decode1_headersB( unsigned char* mp3buf, int len, short pcm_l[], short pcm_r[], mp3data_struct* mp3data, int *enc_delay, int *enc_padding ); int CDECL lame_decode_exit(void); #endif /* obsolete lame_decode API calls */ /********************************************************************* * * id3tag stuff * *********************************************************************/ /* * id3tag.h -- Interface to write ID3 version 1 and 2 tags. * * Copyright (C) 2000 Don Melton. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ /* utility to obtain alphabetically sorted list of genre names with numbers */ void CDECL id3tag_genre_list( void (*handler)(int, const char *, void *), void* cookie); void CDECL id3tag_init (lame_t gfp); /* force addition of version 2 tag */ void CDECL id3tag_add_v2 (lame_t gfp); /* add only a version 1 tag */ void CDECL id3tag_v1_only (lame_t gfp); /* add only a version 2 tag */ void CDECL id3tag_v2_only (lame_t gfp); /* pad version 1 tag with spaces instead of nulls */ void CDECL id3tag_space_v1 (lame_t gfp); /* pad version 2 tag with extra 128 bytes */ void CDECL id3tag_pad_v2 (lame_t gfp); /* pad version 2 tag with extra n bytes */ void CDECL id3tag_set_pad (lame_t gfp, size_t n); void CDECL id3tag_set_title(lame_t gfp, const char* title); void CDECL id3tag_set_artist(lame_t gfp, const char* artist); void CDECL id3tag_set_album(lame_t gfp, const char* album); void CDECL id3tag_set_year(lame_t gfp, const char* year); void CDECL id3tag_set_comment(lame_t gfp, const char* comment); /* return -1 result if track number is out of ID3v1 range and ignored for ID3v1 */ int CDECL id3tag_set_track(lame_t gfp, const char* track); /* return non-zero result if genre name or number is invalid result 0: OK result -1: genre number out of range result -2: no valid ID3v1 genre name, mapped to ID3v1 'Other' but taken as-is for ID3v2 genre tag */ int CDECL id3tag_set_genre(lame_t gfp, const char* genre); /* return non-zero result if field name is invalid */ int CDECL id3tag_set_fieldvalue(lame_t gfp, const char* fieldvalue); /* return non-zero result if image type is invalid */ int CDECL id3tag_set_albumart(lame_t gfp, const char* image, size_t size); /* lame_get_id3v1_tag copies ID3v1 tag into buffer. * Function returns number of bytes copied into buffer, or number * of bytes rquired if buffer 'size' is too small. * Function fails, if returned value is larger than 'size'. * NOTE: * This functions does nothing, if user/LAME disabled ID3v1 tag. */ size_t CDECL lame_get_id3v1_tag(lame_t gfp, unsigned char* buffer, size_t size); /* lame_get_id3v2_tag copies ID3v2 tag into buffer. * Function returns number of bytes copied into buffer, or number * of bytes rquired if buffer 'size' is too small. * Function fails, if returned value is larger than 'size'. * NOTE: * This functions does nothing, if user/LAME disabled ID3v2 tag. */ size_t CDECL lame_get_id3v2_tag(lame_t gfp, unsigned char* buffer, size_t size); /* normaly lame_init_param writes ID3v2 tags into the audio stream * Call lame_set_write_id3tag_automatic(gfp, 0) before lame_init_param * to turn off this behaviour and get ID3v2 tag with above function * write it yourself into your file. */ void CDECL lame_set_write_id3tag_automatic(lame_global_flags * gfp, int); int CDECL lame_get_write_id3tag_automatic(lame_global_flags const* gfp); /* experimental */ int CDECL id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text); /* experimental */ int CDECL id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text); #if DEPRECATED_OR_OBSOLETE_CODE_REMOVED #else /* experimental */ int CDECL id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text); /* experimental */ int CDECL id3tag_set_comment_ucs2(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text); /* experimental */ int CDECL id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue); #endif /* experimental */ int CDECL id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue); /* experimental */ int CDECL id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text); /* experimental */ int CDECL id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text); /*********************************************************************** * * list of valid bitrates [kbps] & sample frequencies [Hz]. * first index: 0: MPEG-2 values (sample frequencies 16...24 kHz) * 1: MPEG-1 values (sample frequencies 32...48 kHz) * 2: MPEG-2.5 values (sample frequencies 8...12 kHz) ***********************************************************************/ extern const int bitrate_table [3][16]; extern const int samplerate_table [3][ 4]; /* access functions for use in DLL, global vars are not exported */ int CDECL lame_get_bitrate(int mpeg_version, int table_index); int CDECL lame_get_samplerate(int mpeg_version, int table_index); /* maximum size of albumart image (128KB), which affects LAME_MAXMP3BUFFER as well since lame_encode_buffer() also returns ID3v2 tag data */ #define LAME_MAXALBUMART (128 * 1024) /* maximum size of mp3buffer needed if you encode at most 1152 samples for each call to lame_encode_buffer. see lame_encode_buffer() below (LAME_MAXMP3BUFFER is now obsolete) */ #define LAME_MAXMP3BUFFER (16384 + LAME_MAXALBUMART) typedef enum { LAME_OKAY = 0, LAME_NOERROR = 0, LAME_GENERICERROR = -1, LAME_NOMEM = -10, LAME_BADBITRATE = -11, LAME_BADSAMPFREQ = -12, LAME_INTERNALERROR = -13, FRONTEND_READERROR = -80, FRONTEND_WRITEERROR = -81, FRONTEND_FILETOOLARGE = -82 } lame_errorcodes_t; #if defined(__cplusplus) } #endif #endif /* LAME_LAME_H */ ================================================ FILE: MLRecorder/MLPlayVoiceButton/MLAmrPlayer.h ================================================ // // MLAmrPlayer.h // CustomerPo // // Created by molon on 8/15/14. // Copyright (c) 2014 molon. All rights reserved. // #import #define MLAMRPLAYER_PLAY_RECEIVE_ERROR_NOTIFICATION @"MLAMRPLAYER_PLAY_RECEIVE_ERROR_NOTIFICATION" #define MLAMRPLAYER_PLAY_RECEIVE_STOP_NOTIFICATION @"MLAMRPLAYER_PLAY_RECEIVE_STOP_NOTIFICATION" @interface MLAmrPlayer : NSObject @property (nonatomic, strong,readonly) NSURL *filePath; @property (nonatomic, assign,readonly) BOOL isPlaying; + (instancetype)shareInstance; + (double)durationOfAmrFilePath:(NSURL*)filePath; - (void)playWithFilePath:(NSURL*)filePath; - (void)stopPlaying; @end ================================================ FILE: MLRecorder/MLPlayVoiceButton/MLAmrPlayer.m ================================================ // // MLAmrPlayer.m // CustomerPo // // Created by molon on 8/15/14. // Copyright (c) 2014 molon. All rights reserved. // #import "MLAmrPlayer.h" #import "MLAudioPlayer.h" #import "AmrPlayerReader.h" @interface MLAmrPlayer() @property (nonatomic, strong) MLAudioPlayer *player; @property (nonatomic, strong) AmrPlayerReader *amrReader; @property (nonatomic, strong) NSURL *filePath; @end @implementation MLAmrPlayer + (instancetype)shareInstance { static MLAmrPlayer *_shareInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shareInstance = [MLAmrPlayer new]; }); return _shareInstance; } #pragma mark - life - (void)dealloc { [_player stopPlaying]; } #pragma mark - getter - (MLAudioPlayer *)player { if (!_player) { _player = [MLAudioPlayer new]; _player.fileReaderDelegate = self.amrReader; __weak __typeof(self)weakSelf = self; _player.receiveErrorBlock = ^(NSError *error){ //这里应该post 一个通知,通知音频播放错误 [[NSNotificationCenter defaultCenter]postNotificationName:MLAMRPLAYER_PLAY_RECEIVE_ERROR_NOTIFICATION object:nil userInfo:@{@"error":error,@"filePath":weakSelf.filePath}]; }; _player.receiveStoppedBlock = ^{ //这里应该post 一个通知,通知音频播放完毕 [[NSNotificationCenter defaultCenter]postNotificationName:MLAMRPLAYER_PLAY_RECEIVE_STOP_NOTIFICATION object:nil userInfo:@{@"filePath":weakSelf.filePath}]; }; } return _player; } - (AmrPlayerReader *)amrReader { if (!_amrReader) { _amrReader = [AmrPlayerReader new]; } return _amrReader; } - (BOOL)isPlaying { return self.player.isPlaying; } #pragma mark - setter - (void)setFilePath:(NSURL *)filePath { _filePath = filePath; self.amrReader.filePath = [filePath path]; } #pragma mark - outcall - (void)playWithFilePath:(NSURL*)filePath { [self.player stopPlaying]; self.filePath = filePath; [self.player startPlaying]; } - (void)stopPlaying { [self.player stopPlaying]; } #pragma mark - other + (double)durationOfAmrFilePath:(NSURL*)filePath { return [AmrPlayerReader durationOfAmrFilePath:[filePath path]]; } @end ================================================ FILE: MLRecorder/MLPlayVoiceButton/MLDataCache/MLDataCache.h ================================================ // // MLDataCache.h // CustomerPo // // Created by molon on 8/15/14. // Copyright (c) 2014 molon. All rights reserved. // #import @interface MLDataCache : NSCache + (instancetype)shareInstance; - (NSData *)cachedDataForRequest:(NSURLRequest *)request; - (void)cacheData:(NSData *)data forRequest:(NSURLRequest *)request; /** * 带保存文件回调的 */ - (void)cacheData:(NSData *)data forRequest:(NSURLRequest *)request afterCacheInFileSuccess:(void(^)(NSURL *filePath))success failure:(void(^)())failure; /** * 返回缓存文件路径 */ - (NSURL*)cachedFilePathForRequest:(NSURLRequest *)request; /** * 把文件作为缓存文件复制到我们的缓存文件夹,使用缓存的命名方式 */ - (void)cacheWithFilePath:(NSURL*)filePath forRequest:(NSURLRequest *)request; @end ================================================ FILE: MLRecorder/MLPlayVoiceButton/MLDataCache/MLDataCache.m ================================================ // // MLDataCache.m // CustomerPo // // Created by molon on 8/15/14. // Copyright (c) 2014 molon. All rights reserved. // #import "MLDataCache.h" #import //md5 用到 static dispatch_queue_t cachedata_concurrent_queue() { static dispatch_queue_t ml_cachedata_concurrent_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ ml_cachedata_concurrent_queue = dispatch_queue_create("com.molon.ml_cache_data_concurrent_queue", DISPATCH_QUEUE_CONCURRENT); }); return ml_cachedata_concurrent_queue; } static inline NSString * MLDataCacheKeyFromURLRequest(NSURLRequest *request) { return [[request URL] absoluteString]; } @implementation MLDataCache + (instancetype)shareInstance { static MLDataCache *_shareInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shareInstance = [MLDataCache new]; }); return _shareInstance; } //得到字符串的md5值 + (NSString *)md5String:(NSString *)str { const char *cStr = [str UTF8String]; unsigned char result[CC_MD5_DIGEST_LENGTH]; memset(result, 0, CC_MD5_DIGEST_LENGTH); CC_MD5( cStr, (CC_LONG)strlen(cStr), result); return [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15] ]; } - (NSString*)filePathForKey:(NSString*)key { //文件路径需要搞搞 NSString *documentDirectory = [NSString stringWithFormat:@"%@%@", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0],@"/Voice/"];//音频缓存文件夹名字 if (![[NSFileManager defaultManager] fileExistsAtPath:documentDirectory]){ NSError *error = nil; if(![[NSFileManager defaultManager] createDirectoryAtPath:documentDirectory withIntermediateDirectories:YES attributes:nil error:&error]){ DLOG(@"%@",error); return nil; } } return [documentDirectory stringByAppendingString:[MLDataCache md5String:key]]; } - (NSData *)cachedDataForRequest:(NSURLRequest *)request { switch ([request cachePolicy]) { case NSURLRequestReloadIgnoringCacheData: case NSURLRequestReloadIgnoringLocalAndRemoteCacheData: return nil; default: break; } NSString *key = MLDataCacheKeyFromURLRequest(request); NSData *data = [self objectForKey:key]; if (!data) { //从文件夹里读取 NSString *filePath = [self filePathForKey:key]; if (filePath) { data = [[NSFileManager defaultManager] contentsAtPath:filePath]; if(data){ //存到cache里 [self cacheData:data forRequest:request]; } } } return data; } - (void)cacheData:(NSData *)data forRequest:(NSURLRequest *)request { [self cacheData:data forRequest:request afterCacheInFileSuccess:nil failure:nil]; } - (void)cacheData:(NSData *)data forRequest:(NSURLRequest *)request afterCacheInFileSuccess:(void(^)(NSURL *filePath))success failure:(void(^)())failure { if (!data||!request) { if (failure) { failure(); } return; } NSString *key = MLDataCacheKeyFromURLRequest(request); [self setObject:data forKey:key]; NSString *filePath = [self filePathForKey:key]; if (filePath) { dispatch_async(cachedata_concurrent_queue(), ^{ //下面这玩意是线程安全的,不用害怕 if(![[NSFileManager defaultManager] createFileAtPath:filePath contents:data attributes:nil]){ DLOG(@"建立文件缓存:%@失败",filePath); if (failure) { dispatch_async(dispatch_get_main_queue(), ^{ failure(); }); } }else{ if (success) { dispatch_async(dispatch_get_main_queue(), ^{ success([NSURL fileURLWithPath:filePath]); }); } } }); } } /** * 返回缓存文件路径 */ - (NSURL*)cachedFilePathForRequest:(NSURLRequest *)request { switch ([request cachePolicy]) { case NSURLRequestReloadIgnoringCacheData: case NSURLRequestReloadIgnoringLocalAndRemoteCacheData: return nil; default: break; } //直接从文件夹里找 NSString *filePath = [self filePathForKey:MLDataCacheKeyFromURLRequest(request)]; if ([[NSFileManager defaultManager]isReadableFileAtPath:filePath]) { return [NSURL fileURLWithPath:filePath]; } return nil; } /** * 把文件复制到我们的缓存文件夹 */ - (void)cacheWithFilePath:(NSURL*)filePath forRequest:(NSURLRequest *)request { if (![[NSFileManager defaultManager]isReadableFileAtPath:[filePath path]]||!request) { return; } NSString *cacheFilePath = [self filePathForKey:MLDataCacheKeyFromURLRequest(request)]; //将文件复制到这里 if(![[NSFileManager defaultManager] copyItemAtURL:filePath toURL:[NSURL fileURLWithPath:cacheFilePath] error:nil]){ DLOG(@"缓存文件:%@失败",filePath); } } @end ================================================ FILE: MLRecorder/MLPlayVoiceButton/MLDataResponseSerializer/MLDataResponseSerializer.h ================================================ // // MLDataResponseSerializer.h // CustomerPo // // Created by molon on 8/15/14. // Copyright (c) 2014 molon. All rights reserved. // #import "AFURLResponseSerialization.h" @interface MLDataResponseSerializer : AFHTTPResponseSerializer + (instancetype)shareInstance; + (NSOperationQueue *)sharedDataRequestOperationQueue; @end ================================================ FILE: MLRecorder/MLPlayVoiceButton/MLDataResponseSerializer/MLDataResponseSerializer.m ================================================ // // MLDataResponseSerializer.m // CustomerPo // // Created by molon on 8/15/14. // Copyright (c) 2014 molon. All rights reserved. // #import "MLDataResponseSerializer.h" @implementation MLDataResponseSerializer + (NSOperationQueue *)sharedDataRequestOperationQueue { static NSOperationQueue *_sharedDataRequestOperationQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedDataRequestOperationQueue = [[NSOperationQueue alloc] init]; _sharedDataRequestOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; }); return _sharedDataRequestOperationQueue; } + (instancetype)shareInstance { static MLDataResponseSerializer *_shareInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shareInstance = [MLDataResponseSerializer serializer]; }); return _shareInstance; } - (instancetype)init { self = [super init]; if (!self) { return nil; } self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"*/*", nil]; return self; } - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error { return data; } - (id)copyWithZone:(NSZone *)zone { MLDataResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; return serializer; } @end ================================================ FILE: MLRecorder/MLPlayVoiceButton/MLPlayVoiceButton.h ================================================ // // MLPlayVoiceButton.h // CustomerPo // // Created by molon on 8/15/14. // Copyright (c) 2014 molon. All rights reserved. // #import #import "MLDataCache.h" #define kMLPlayVoiceButtonErrorDomain @"MLPlayVoiceButtonErrorDomain" /** * 错误标识 */ typedef NS_OPTIONS(NSUInteger, MLPlayVoiceButtonErrorCode) { MLPlayVoiceButtonErrorCodeCacheFailed = 0, //写入缓存文件失败 MLPlayVoiceButtonErrorCodeWrongVoiceFomrat,//音频文件格式错误 }; typedef NS_OPTIONS(NSUInteger, MLPlayVoiceButtonType) { MLPlayVoiceButtonTypeLeft = 0, MLPlayVoiceButtonTypeRight, }; typedef NS_OPTIONS(NSUInteger, MLPlayVoiceButtonState) { MLPlayVoiceButtonStateNone = 0, MLPlayVoiceButtonStateNormal, MLPlayVoiceButtonStateDownloading, }; @interface MLPlayVoiceButton : UIButton @property (nonatomic, strong,readonly) NSURL *voiceURL; @property (nonatomic, assign) MLPlayVoiceButtonType type; @property (nonatomic, assign,readonly) MLPlayVoiceButtonState voiceState; @property (nonatomic, assign) NSTimeInterval duration; - (CGFloat)preferredWidth; @property (nonatomic, copy) void(^preferredWidthChangedBlock)(MLPlayVoiceButton *voiceButton,BOOL isShouldBeAnimated); @property (nonatomic, copy) void(^voiceWillPlayBlock)(MLPlayVoiceButton *voiceButton); #pragma mark - cache + (MLDataCache*)sharedDataCache; #pragma mark - cancel - (void)cancelVoiceRequestOperation; #pragma mark - set voice - (void)setVoiceWithURL:(NSURL*)url; - (void)setVoiceWithURL:(NSURL*)url withAutoPlay:(BOOL)autoPlay; - (void)setVoiceWithURL:(NSURL *)url success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSURL* voicePath))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure; - (void)setVoiceWithURLRequest:(NSURLRequest *)urlRequest success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSURL* voicePath))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure; @end ================================================ FILE: MLRecorder/MLPlayVoiceButton/MLPlayVoiceButton.m ================================================ // // MLPlayVoiceButton.m // CustomerPo // // Created by molon on 8/15/14. // Copyright (c) 2014 molon. All rights reserved. // #import "MLPlayVoiceButton.h" #import "MLDataResponseSerializer.h" #import "MLAmrPlayer.h" #import "AFNetworking.h" #define AMR_MAGIC_NUMBER "#!AMR\n" @interface MLPlayVoiceButton() @property (nonatomic, strong) AFHTTPRequestOperation *af_dataRequestOperation; @property (nonatomic, strong) NSURL *voiceURL; @property (nonatomic, assign) BOOL isVoicePlaying; @property (nonatomic, strong) UIImageView *playingSignImageView; @property (nonatomic, strong) NSURL *filePath; @property (nonatomic, strong) UIActivityIndicatorView *indicator; @property (nonatomic, assign) MLPlayVoiceButtonState voiceState; @end @implementation MLPlayVoiceButton #pragma mark - cache + (MLDataCache*)sharedDataCache { return [MLDataCache shareInstance]; } #pragma mark - cancel - (void)cancelVoiceRequestOperation { [self.af_dataRequestOperation cancel]; self.af_dataRequestOperation = nil; } #pragma mark - life - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code [self setUp]; } return self; } - (void)awakeFromNib { [super awakeFromNib]; [self setUp]; } - (void)setUp { [self addSubview:self.playingSignImageView]; [self addSubview:self.indicator]; self.voiceState = MLPlayVoiceButtonStateNone; [self updatePlayingSignImage]; // [self setBackgroundImage:[UIImage imageWithPureColor:[UIColor colorWithWhite:0.253 alpha:0.650]] forState:UIControlStateHighlighted]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playReceiveStop:) name:MLAMRPLAYER_PLAY_RECEIVE_STOP_NOTIFICATION object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playReceiveError:) name:MLAMRPLAYER_PLAY_RECEIVE_ERROR_NOTIFICATION object:nil]; [self addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside]; } - (void)dealloc { [[NSNotificationCenter defaultCenter]removeObserver:self]; } #pragma mark - notification - (void)playReceiveStop:(NSNotification*)notification { NSDictionary *userInfo = notification.userInfo; if (![userInfo[@"filePath"] isEqual:self.filePath]) { return; } DLOG(@"发现音频播放停止:%@,如果发现此处执行多次不用在意。那可能是因为tableView复用的关系",[self.filePath path]); [self updatePlayingSignImage]; } - (void)playReceiveError:(NSNotification*)notification { NSDictionary *userInfo = notification.userInfo; if (![userInfo[@"filePath"] isEqual:self.filePath]) { return; } #warning 这里最好做下发现当前音频播放错误处理 DLOG(@"发现音频播放错误:%@",[self.filePath path]); [self updatePlayingSignImage]; } #pragma mark - event - (void)click { if (!self.filePath) { return; } if (!self.isVoicePlaying) { if (self.voiceWillPlayBlock) { self.voiceWillPlayBlock(self); } [[MLAmrPlayer shareInstance]playWithFilePath:self.filePath]; [self updatePlayingSignImage]; }else{ [[MLAmrPlayer shareInstance]stopPlaying]; } } #pragma mark - getter - (BOOL)isVoicePlaying { if ([MLAmrPlayer shareInstance].isPlaying&&[[MLAmrPlayer shareInstance].filePath isEqual:self.filePath]) { return YES; } return NO; } - (UIImageView *)playingSignImageView { if (!_playingSignImageView) { UIImageView *imageView = [[UIImageView alloc]init]; imageView.contentMode = UIViewContentModeScaleAspectFit; _playingSignImageView = imageView; } return _playingSignImageView; } - (UIActivityIndicatorView *)indicator { if (!_indicator) { _indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; } return _indicator; } #pragma mark - setter - (void)setType:(MLPlayVoiceButtonType)type { _type = type; [self updatePlayingSignImage]; [self setNeedsLayout]; } - (void)setFilePath:(NSURL *)filePath { _filePath = filePath; if (filePath) { if (self.duration<=0) { _duration = [MLAmrPlayer durationOfAmrFilePath:filePath]; } self.voiceState = MLPlayVoiceButtonStateNormal; }else{ _duration = 0.0f; self.voiceState = MLPlayVoiceButtonStateNone; } } - (void)setVoiceState:(MLPlayVoiceButtonState)voiceState { _voiceState = voiceState; //如果none啥都没, if (voiceState == MLPlayVoiceButtonStateNone) { [self.indicator stopAnimating]; self.playingSignImageView.hidden = YES; }else if (voiceState == MLPlayVoiceButtonStateDownloading){ [self.indicator startAnimating]; self.playingSignImageView.hidden = YES; }else if (voiceState == MLPlayVoiceButtonStateNormal){ self.playingSignImageView.hidden = NO; [self.indicator stopAnimating]; [self updatePlayingSignImage]; } if (self.preferredWidthChangedBlock) { self.preferredWidthChangedBlock(self,NO); } } - (void)setDuration:(NSTimeInterval)duration { _duration = duration; if (self.preferredWidthChangedBlock) { self.preferredWidthChangedBlock(self,NO); } } #pragma mark - 图像 - (void)updatePlayingSignImage { if (self.voiceState==MLPlayVoiceButtonStateDownloading) { self.playingSignImageView.image = nil; return; } NSString *prefix = self.type==MLPlayVoiceButtonTypeLeft?@"ReceiverVoiceNodePlaying00":@"SenderVoiceNodePlaying00"; if ([self isVoicePlaying]) { self.playingSignImageView.image = [UIImage animatedImageNamed:prefix duration:1.0f]; }else{ self.playingSignImageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@3",prefix]]; } } #pragma mark - layout - (void)layoutSubviews { [super layoutSubviews]; #define kVoicePlaySignSideLength 20.0f if (self.type == MLPlayVoiceButtonTypeRight) { self.playingSignImageView.frame = CGRectMake(self.frame.size.width-kVoicePlaySignSideLength-5.0f, (self.frame.size.height-kVoicePlaySignSideLength)/2, kVoicePlaySignSideLength, kVoicePlaySignSideLength); }else{ self.playingSignImageView.frame = CGRectMake(5.0f, (self.frame.size.height-kVoicePlaySignSideLength)/2, kVoicePlaySignSideLength, kVoicePlaySignSideLength); } self.indicator.frame = self.playingSignImageView.frame; } #pragma mark - outcall - (void)setVoiceWithURL:(NSURL*)url { [self setVoiceWithURL:url withAutoPlay:NO]; } - (void)setVoiceWithURL:(NSURL*)url withAutoPlay:(BOOL)autoPlay { __weak __typeof(self)weakSelf = self; [self setVoiceWithURL:url success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSURL *voicePath) { if (!voicePath) { weakSelf.filePath = voicePath; return; } weakSelf.filePath = voicePath; if (autoPlay) { if (weakSelf.voiceWillPlayBlock) { weakSelf.voiceWillPlayBlock(weakSelf); } [[MLAmrPlayer shareInstance]playWithFilePath:weakSelf.filePath]; } } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { DLOG(@"%@",error); weakSelf.filePath = nil; }]; } - (void)setVoiceWithURL:(NSURL *)url success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSURL* voicePath))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request addValue:@"*/*" forHTTPHeaderField:@"Accept"]; [self setVoiceWithURLRequest:request success:success failure:failure]; } - (void)setVoiceWithURLRequest:(NSURLRequest *)urlRequest success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSURL* voicePath))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure { self.voiceURL = [urlRequest URL]; //无论如何,该去掉的就得去掉 //#warning 这里有个弊端,例如上一个设置了autoPlay,然后tableViewCell重用后,会取消,然后肯定上面那个就不能自动播放了,似乎也不适合处理这个情况。回头再考虑吧。不过有个应该考虑下,下一半还没下完,然后被重用了,这样之前的下载就被丢弃了!,AFNetworking的图片处理也有类似情况 self.filePath = nil; [self cancelVoiceRequestOperation]; if ([self.voiceURL isFileURL]) { if (success) { success(urlRequest, nil, self.voiceURL); } else if (self.voiceURL) { self.filePath = self.voiceURL; } return; } if (nil==self.voiceURL) { if (success) { success(urlRequest,nil,self.voiceURL); } return; } NSURL *filePath = [[[self class] sharedDataCache] cachedFilePathForRequest:urlRequest]; if (filePath) { if (success) { success(nil, nil, filePath); } else { self.filePath = filePath; } self.af_dataRequestOperation = nil; } else { self.voiceState = MLPlayVoiceButtonStateDownloading; DLOG(@"下载音频%@",[urlRequest URL]); __weak __typeof(self)weakSelf = self; self.af_dataRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; self.af_dataRequestOperation.responseSerializer = [MLDataResponseSerializer shareInstance]; [self.af_dataRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { static const char* amrHeader = AMR_MAGIC_NUMBER; char magic[8]; [responseObject getBytes:magic length:strlen(amrHeader)]; if (strncmp(magic, amrHeader, strlen(amrHeader))) { NSError *error = [NSError errorWithDomain:kMLPlayVoiceButtonErrorDomain code:MLPlayVoiceButtonErrorCodeWrongVoiceFomrat userInfo:@{NSLocalizedDescriptionKey:@"音频非amr文件"}]; if (failure) { failure(urlRequest,operation.response,error); } return; } __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[urlRequest URL] isEqual:[operation.request URL]]) { //写入文件 [[[strongSelf class] sharedDataCache] cacheData:responseObject forRequest:urlRequest afterCacheInFileSuccess:^(NSURL *filePath) { if (success) { success(urlRequest, operation.response, filePath); } else if (filePath) { strongSelf.filePath = filePath; } } failure:^{ NSError *error = [NSError errorWithDomain:kMLPlayVoiceButtonErrorDomain code:MLPlayVoiceButtonErrorCodeCacheFailed userInfo:@{NSLocalizedDescriptionKey:@"写入音频缓存文件失败"}]; if (failure) { failure(urlRequest, operation.response, error); } }]; } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if ([[urlRequest URL] isEqual:[operation.request URL]]) { if (failure) { failure(urlRequest, operation.response, error); } } }]; [[MLDataResponseSerializer sharedDataRequestOperationQueue] addOperation:self.af_dataRequestOperation]; } } #pragma mark - preferredWidth - (CGFloat)preferredWidth { #define kMinDefaultWidth 50.0f #define kMaxWidth 120.0f if (self.voiceState != MLPlayVoiceButtonStateNormal) { return kMinDefaultWidth; } CGFloat width = kMinDefaultWidth + floor(self.duration+0.5f)*5.0f; if (width>kMaxWidth) { width = kMaxWidth; } return width; } @end ================================================ FILE: MLRecorder/MLRecorder-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier com.molon.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: MLRecorder/MLRecorder-Prefix.pch ================================================ // // Prefix header // // The contents of this file are implicitly included at the beginning of every source file. // #import #ifndef __IPHONE_5_0 #warning "This project uses features only available in iOS SDK 5.0 and later." #endif #ifdef __OBJC__ #import #import #import "Debug.h" #endif ================================================ FILE: MLRecorder/PlayNetworkVoiceViewController.h ================================================ // // PlayNetworkVoiceViewController.h // MLRecorder // // Created by molon on 8/28/14. // Copyright (c) 2014 molon. All rights reserved. // #import @interface PlayNetworkVoiceViewController : UIViewController @end ================================================ FILE: MLRecorder/PlayNetworkVoiceViewController.m ================================================ // // PlayNetworkVoiceViewController.m // MLRecorder // // Created by molon on 8/28/14. // Copyright (c) 2014 molon. All rights reserved. // #import "PlayNetworkVoiceViewController.h" #import "MLPlayVoiceButton.h" #import "MLAmrPlayer.h" @interface PlayNetworkVoiceViewController () @property (weak, nonatomic) IBOutlet MLPlayVoiceButton *voiceButton1; @property (weak, nonatomic) IBOutlet MLPlayVoiceButton *voiceButton2; @end @implementation PlayNetworkVoiceViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } - (void)dealloc { [[MLAmrPlayer shareInstance]stopPlaying]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. #warning 注意这俩我是放到github上的,如果github访问不良,则需要使用VPN翻墙才能测试此功能。 [self.voiceButton1 setVoiceWithURL:[NSURL URLWithString:@"https://raw.githubusercontent.com/molon/MLAudioRecorder/master/record1.amr"]]; self.voiceButton2.type = MLPlayVoiceButtonTypeRight; [self.voiceButton2 setVoiceWithURL:[NSURL URLWithString:@"https://raw.githubusercontent.com/molon/MLAudioRecorder/master/record2.amr"]]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end ================================================ FILE: MLRecorder/RealTimeViewController.h ================================================ // // RealTimeViewController.h // MLRecorder // // Created by molon on 14/12/19. // Copyright (c) 2014年 molon. All rights reserved. // #import @interface RealTimeViewController : UIViewController @end ================================================ FILE: MLRecorder/RealTimeViewController.m ================================================ // // RealTimeViewController.m // MLRecorder // // Created by molon on 14/12/19. // Copyright (c) 2014年 molon. All rights reserved. // #import "RealTimeViewController.h" #import "MLAudioRecorder.h" #import "MLAudioRealTimePlayer.h" #import "CafRecordInBufferWriter.h" #import "AmrRecordInBufferWriter.h" //amr解码 #import "interf_dec.h" #warning 特别注意一个问题,测试此功能的时候请使用耳机,否则会造成电脑播放出来的声音又被录了进去,造成死循环不断重复。。老子因为这个屌问题研究了他妈的几个小时。最后还是我媳妇发现的!! @interface RealTimeViewController() @property (nonatomic, strong) MLAudioRecorder *recorder; @property (nonatomic, strong) MLAudioRealTimePlayer *player; @property (nonatomic, strong) CafRecordInBufferWriter *cafRecordWriter; @property (nonatomic, strong) AmrRecordInBufferWriter *amrRecordWriter; @property (nonatomic, strong) UIButton *button; @property (nonatomic, strong) UIButton *simulateSlackButton; @property (nonatomic, assign) BOOL isInSlack; //模拟卡顿中没投递播放的数据记录 @property (nonatomic, strong) NSMutableArray *simulateSlackDatas; @end @implementation RealTimeViewController - (void)dealloc { [_recorder stopRecording]; [_player stop]; } - (void)viewDidLoad { [super viewDidLoad]; self.title = @"录音实时播放"; [self.view addSubview:self.button]; self.button.frame = CGRectMake(0, 0, 80, 40); self.button.center = self.view.center; [self.view addSubview:self.simulateSlackButton]; self.simulateSlackButton.frame = CGRectMake(self.button.frame.origin.x-10.0f, CGRectGetMaxY(self.button.frame)+20.0f, 100, 40); [self.player start]; } #pragma mark - getter - (NSMutableArray *)simulateSlackDatas { if (!_simulateSlackDatas) { _simulateSlackDatas = [NSMutableArray new]; } return _simulateSlackDatas; } - (UIButton *)button { if (!_button) { UIButton *button = [[UIButton alloc]init]; [button setTitle:@"Record" forState:UIControlStateNormal]; [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [button addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside]; _button = button; } return _button; } - (UIButton *)simulateSlackButton { if (!_simulateSlackButton) { UIButton *button = [[UIButton alloc]init]; [button setTitle:@"Slack" forState:UIControlStateNormal]; [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [button addTarget:self action:@selector(simulateSlackButtonPressed) forControlEvents:UIControlEventTouchUpInside]; _simulateSlackButton = button; } return _simulateSlackButton; } - (NSData*)decodeForAmrData:(NSData*)amrData { //其实_destate 只需要初始化一次就够了。这里为了方便先扔这,只是demo嘛。。 void *_destate = 0; // amr 解压句柄 _destate = Decoder_Interface_init(); if(_destate==0){ return nil; } int amrFramelen = 14; int needReadFrameCount = floor(amrData.length/amrFramelen); NSMutableData *data = [NSMutableData data]; unsigned char amrFrame[amrFramelen]; short pcmFrame[160]; for (NSUInteger i=0; i4) { [strongSelf.simulateSlackDatas removeObjectAtIndex:0]; } }else{ [strongSelf.player appendPacket:decodedData]; } }; if (![[NSThread currentThread]isEqual:[NSThread mainThread]]) { dispatch_async(dispatch_get_main_queue(), ^{ //注意这个屌地不是主线程,需要投递到主线程去做 executeBlock(); }); }else{ executeBlock(); } }]; } return _amrRecordWriter; } - (CafRecordInBufferWriter *)cafRecordWriter { if (!_cafRecordWriter) { _cafRecordWriter = [CafRecordInBufferWriter new]; __weak __typeof(self)weakSelf = self; [_cafRecordWriter setDidReceiveVoiceData:^(NSData *data) { __strong __typeof(weakSelf)strongSelf = weakSelf; void (^executeBlock)() = ^{ //投递到Player里 if (strongSelf.isInSlack) { [strongSelf.simulateSlackDatas addObject:data]; //只保留4个,根据kDefaultBufferDurationSeconds的话应该是1秒的时间,原因见simulateSlackButtonPressed if (strongSelf.simulateSlackDatas.count>4) { [strongSelf.simulateSlackDatas removeObjectAtIndex:0]; } }else{ [strongSelf.player appendPacket:data]; } }; if (![[NSThread currentThread]isEqual:[NSThread mainThread]]) { dispatch_async(dispatch_get_main_queue(), ^{ //注意这个屌地不是主线程,需要投递到主线程去做 executeBlock(); }); }else{ executeBlock(); } }]; } return _cafRecordWriter; } - (MLAudioRecorder *)recorder { if (!_recorder) { _recorder = [MLAudioRecorder new]; //这里可以改变是那种编码方式 // _recorder.fileWriterDelegate = self.amrRecordWriter; _recorder.fileWriterDelegate = self.cafRecordWriter; } return _recorder; } - (MLAudioRealTimePlayer *)player { if (!_player) { _player = [MLAudioRealTimePlayer new]; [_player setDidReceiveErrorBlock:^(MLAudioRealTimePlayer *player, NSError *error) { DLOG(@"实时播放错误:%@",error); }]; [_player setDidReceiveStoppedBlock:^(MLAudioRealTimePlayer *player) { DLOG(@"实时播放停止"); }]; } return _player; } #pragma mark - event - (void)buttonPressed { if (self.recorder.isRecording) { [self.recorder stopRecording]; [self.button setTitle:@"Record" forState:UIControlStateNormal]; }else{ [self.recorder startRecording]; [self.button setTitle:@"Stop" forState:UIControlStateNormal]; } } - (void)simulateSlackButtonPressed { if (self.isInSlack) { #warning 这里需要注意,卡顿的时间里记录的数据投递后,卡顿的时间实时播放里会永远延迟,测试下即可发现。对于这种情况是没法避免的,我们只能只能忽略卡顿内的数据,或者只播放其中的最后一小段,这个使用时请根据自身情况判断。 for (NSData *data in self.simulateSlackDatas) { [self.player appendPacket:data]; } [self.simulateSlackDatas removeAllObjects]; self.isInSlack = NO; [self.simulateSlackButton setTitle:@"Slack" forState:UIControlStateNormal]; DLOG(@"卡顿结束"); }else{ DLOG(@"开始卡顿了"); self.isInSlack = YES; [self.simulateSlackButton setTitle:@"Stop Slack" forState:UIControlStateNormal]; } } @end ================================================ FILE: MLRecorder/ViewController.h ================================================ // // ViewController.h // MLRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import @interface ViewController : UIViewController @end ================================================ FILE: MLRecorder/ViewController.m ================================================ // // ViewController.m // MLRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import "ViewController.h" #import "MLAudioRecorder.h" #import "CafRecordWriter.h" #import "AmrRecordWriter.h" #import "Mp3RecordWriter.h" #import #import "MLAudioMeterObserver.h" #import "MLAudioPlayer.h" #import "AmrPlayerReader.h" @interface ViewController () @property (nonatomic, strong) MLAudioRecorder *recorder; @property (nonatomic, strong) CafRecordWriter *cafWriter; @property (nonatomic, strong) AmrRecordWriter *amrWriter; @property (nonatomic, strong) Mp3RecordWriter *mp3Writer; @property (nonatomic, strong) MLAudioPlayer *player; @property (nonatomic, strong) AmrPlayerReader *amrReader; @property (nonatomic, strong) AVAudioPlayer *avAudioPlayer; @property (nonatomic, copy) NSString *filePath; @property (weak, nonatomic) IBOutlet UIButton *recordButton; @property (weak, nonatomic) IBOutlet UIButton *playButton; @property (nonatomic, strong) MLAudioMeterObserver *meterObserver; @end @implementation ViewController - (void)dealloc { //音谱检测关联着录音类,录音类要停止了。所以要设置其audioQueue为nil self.meterObserver.audioQueue = nil; [self.recorder stopRecording]; [self.player stopPlaying]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; CafRecordWriter *writer = [[CafRecordWriter alloc]init]; writer.filePath = [path stringByAppendingPathComponent:@"record.caf"]; self.cafWriter = writer; AmrRecordWriter *amrWriter = [[AmrRecordWriter alloc]init]; amrWriter.filePath = [path stringByAppendingPathComponent:@"record.amr"]; amrWriter.maxSecondCount = 60; amrWriter.maxFileSize = 1024*256; self.amrWriter = amrWriter; Mp3RecordWriter *mp3Writer = [[Mp3RecordWriter alloc]init]; mp3Writer.filePath = [path stringByAppendingPathComponent:@"record.mp3"]; mp3Writer.maxSecondCount = 60; mp3Writer.maxFileSize = 1024*256; self.mp3Writer = mp3Writer; MLAudioMeterObserver *meterObserver = [[MLAudioMeterObserver alloc]init]; meterObserver.actionBlock = ^(NSArray *levelMeterStates,MLAudioMeterObserver *meterObserver){ DLOG(@"volume:%f",[MLAudioMeterObserver volumeForLevelMeterStates:levelMeterStates]); }; meterObserver.errorBlock = ^(NSError *error,MLAudioMeterObserver *meterObserver){ [[[UIAlertView alloc]initWithTitle:@"错误" message:error.userInfo[NSLocalizedDescriptionKey] delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil]show]; }; self.meterObserver = meterObserver; MLAudioRecorder *recorder = [[MLAudioRecorder alloc]init]; __weak __typeof(self)weakSelf = self; recorder.receiveStoppedBlock = ^{ [weakSelf.recordButton setTitle:@"Record" forState:UIControlStateNormal]; weakSelf.meterObserver.audioQueue = nil; }; recorder.receiveErrorBlock = ^(NSError *error){ [weakSelf.recordButton setTitle:@"Record" forState:UIControlStateNormal]; weakSelf.meterObserver.audioQueue = nil; [[[UIAlertView alloc]initWithTitle:@"错误" message:error.userInfo[NSLocalizedDescriptionKey] delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil]show]; }; //caf // recorder.fileWriterDelegate = writer; // self.filePath = writer.filePath; //mp3 // recorder.fileWriterDelegate = mp3Writer; // self.filePath = mp3Writer.filePath; //amr recorder.bufferDurationSeconds = 0.25; recorder.fileWriterDelegate = self.amrWriter; self.recorder = recorder; MLAudioPlayer *player = [[MLAudioPlayer alloc]init]; AmrPlayerReader *amrReader = [[AmrPlayerReader alloc]init]; player.fileReaderDelegate = amrReader; player.receiveErrorBlock = ^(NSError *error){ [weakSelf.playButton setTitle:@"Play" forState:UIControlStateNormal]; [[[UIAlertView alloc]initWithTitle:@"错误" message:error.userInfo[NSLocalizedDescriptionKey] delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil]show]; }; player.receiveStoppedBlock = ^{ [weakSelf.playButton setTitle:@"Play" forState:UIControlStateNormal]; }; self.player = player; self.amrReader = amrReader; //button event test // [self.recordButton addTarget:self action:@selector(dragEnter) forControlEvents:UIControlEventTouchDragEnter]; // [self.recordButton addTarget:self action:@selector(dragExit) forControlEvents:UIControlEventTouchDragExit]; // [self.recordButton addTarget:self action:@selector(upOutSide) forControlEvents:UIControlEventTouchUpOutside]; // [self.recordButton addTarget:self action:@selector(cancel) forControlEvents:UIControlEventTouchCancel]; // [self.recordButton addTarget:self action:@selector(down) forControlEvents:UIControlEventTouchDown]; // [self.recordButton addTarget:self action:@selector(upInside) forControlEvents:UIControlEventTouchUpInside]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionDidChangeInterruptionType:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; } - (void)audioSessionDidChangeInterruptionType:(NSNotification *)notification { AVAudioSessionInterruptionType interruptionType = [[[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; if (AVAudioSessionInterruptionTypeBegan == interruptionType) { DLOG(@"begin"); } else if (AVAudioSessionInterruptionTypeEnded == interruptionType) { DLOG(@"end"); } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)record:(id)sender { UIButton *recordButton = (UIButton*)sender; if (self.recorder.isRecording) { //取消录音 [self.recorder stopRecording]; }else{ [recordButton setTitle:@"Stop" forState:UIControlStateNormal]; //开始录音 [self.recorder startRecording]; self.meterObserver.audioQueue = self.recorder->_audioQueue; } } - (IBAction)play:(id)sender { // UIButton *playButton = (UIButton*)sender; // if (self.avAudioPlayer.isPlaying) { // [self.avAudioPlayer stop]; // [playButton setTitle:@"Play" forState:UIControlStateNormal]; // }else{ // self.avAudioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:self.filePath] error:nil]; // [self.avAudioPlayer play]; // [playButton setTitle:@"Stop" forState:UIControlStateNormal]; // } self.amrReader.filePath = self.amrWriter.filePath; DLOG(@"文件时长%f",[AmrPlayerReader durationOfAmrFilePath:self.amrReader.filePath]); UIButton *playButton = (UIButton*)sender; if (self.player.isPlaying) { [self.player stopPlaying]; }else{ [playButton setTitle:@"Stop" forState:UIControlStateNormal]; [self.player startPlaying]; } } - (void)dragEnter { DLOG(@"T普通提示录音状态"); } - (void)dragExit { DLOG(@"T提示松开可取消"); } - (void)upOutSide { DLOG(@"T取消录音"); } - (void)cancel { DLOG(@"T取消录音"); } - (void)down { DLOG(@"T开始录音"); [self dragEnter]; } - (void)upInside { DLOG(@"T结束录音"); } @end ================================================ FILE: MLRecorder/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: MLRecorder/main.m ================================================ // // main.m // MLRecorder // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: MLRecorder.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 36197FFC192F2E9C00C9198F /* test.amr in Resources */ = {isa = PBXBuildFile; fileRef = 36C3B7E1192C798F00B6063D /* test.amr */; }; 362F410A1A0377D60084A844 /* MLAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 362F41091A0377D60084A844 /* MLAudioPlayer.m */; }; 365F94EE19AECDF9001BB9A0 /* PlayNetworkVoiceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F94ED19AECDF9001BB9A0 /* PlayNetworkVoiceViewController.m */; }; 365F952619AECE7F001BB9A0 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F94F419AECE7F001BB9A0 /* AFHTTPRequestOperation.m */; }; 365F952719AECE7F001BB9A0 /* AFHTTPRequestOperationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F94F619AECE7F001BB9A0 /* AFHTTPRequestOperationManager.m */; }; 365F952819AECE7F001BB9A0 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F94F819AECE7F001BB9A0 /* AFURLConnectionOperation.m */; }; 365F952919AECE7F001BB9A0 /* AFHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F94FB19AECE7F001BB9A0 /* AFHTTPSessionManager.m */; }; 365F952A19AECE7F001BB9A0 /* AFURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F94FD19AECE7F001BB9A0 /* AFURLSessionManager.m */; }; 365F952B19AECE7F001BB9A0 /* AFNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F950019AECE7F001BB9A0 /* AFNetworkReachabilityManager.m */; }; 365F952C19AECE7F001BB9A0 /* AFSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F950319AECE7F001BB9A0 /* AFSecurityPolicy.m */; }; 365F952D19AECE7F001BB9A0 /* AFURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F950619AECE7F001BB9A0 /* AFURLRequestSerialization.m */; }; 365F952E19AECE7F001BB9A0 /* AFURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F950819AECE7F001BB9A0 /* AFURLResponseSerialization.m */; }; 365F952F19AECE7F001BB9A0 /* AFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F950B19AECE7F001BB9A0 /* AFNetworkActivityIndicatorManager.m */; }; 365F953019AECE7F001BB9A0 /* UIActivityIndicatorView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F950D19AECE7F001BB9A0 /* UIActivityIndicatorView+AFNetworking.m */; }; 365F953119AECE7F001BB9A0 /* UIAlertView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F950F19AECE7F001BB9A0 /* UIAlertView+AFNetworking.m */; }; 365F953219AECE7F001BB9A0 /* UIButton+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F951119AECE7F001BB9A0 /* UIButton+AFNetworking.m */; }; 365F953319AECE7F001BB9A0 /* UIImageView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F951319AECE7F001BB9A0 /* UIImageView+AFNetworking.m */; }; 365F953419AECE7F001BB9A0 /* UIProgressView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F951619AECE7F001BB9A0 /* UIProgressView+AFNetworking.m */; }; 365F953519AECE7F001BB9A0 /* UIRefreshControl+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F951819AECE7F001BB9A0 /* UIRefreshControl+AFNetworking.m */; }; 365F953619AECE7F001BB9A0 /* UIWebView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F951A19AECE7F001BB9A0 /* UIWebView+AFNetworking.m */; }; 365F953719AECE7F001BB9A0 /* MLAmrPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F951D19AECE7F001BB9A0 /* MLAmrPlayer.m */; }; 365F953819AECE7F001BB9A0 /* MLDataCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F952019AECE7F001BB9A0 /* MLDataCache.m */; }; 365F953919AECE7F001BB9A0 /* MLDataResponseSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F952319AECE7F001BB9A0 /* MLDataResponseSerializer.m */; }; 365F953A19AECE7F001BB9A0 /* MLPlayVoiceButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 365F952519AECE7F001BB9A0 /* MLPlayVoiceButton.m */; }; 365F955519AED3A5001BB9A0 /* ReceiverVoiceNodePlaying000@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 365F954D19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying000@2x.png */; }; 365F955619AED3A5001BB9A0 /* ReceiverVoiceNodePlaying001@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 365F954E19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying001@2x.png */; }; 365F955719AED3A5001BB9A0 /* ReceiverVoiceNodePlaying002@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 365F954F19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying002@2x.png */; }; 365F955819AED3A5001BB9A0 /* ReceiverVoiceNodePlaying003@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 365F955019AED3A5001BB9A0 /* ReceiverVoiceNodePlaying003@2x.png */; }; 365F955919AED3A5001BB9A0 /* SenderVoiceNodePlaying000@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 365F955119AED3A5001BB9A0 /* SenderVoiceNodePlaying000@2x.png */; }; 365F955A19AED3A5001BB9A0 /* SenderVoiceNodePlaying001@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 365F955219AED3A5001BB9A0 /* SenderVoiceNodePlaying001@2x.png */; }; 365F955B19AED3A5001BB9A0 /* SenderVoiceNodePlaying002@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 365F955319AED3A5001BB9A0 /* SenderVoiceNodePlaying002@2x.png */; }; 365F955C19AED3A5001BB9A0 /* SenderVoiceNodePlaying003@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 365F955419AED3A5001BB9A0 /* SenderVoiceNodePlaying003@2x.png */; }; 368DCC29192FC436001F3574 /* test2.amr in Resources */ = {isa = PBXBuildFile; fileRef = 368DCC28192FC3EF001F3574 /* test2.amr */; }; 369D589419AECA64004AB79C /* AmrPlayerReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 369D589119AECA64004AB79C /* AmrPlayerReader.m */; }; 369D58AF19AECA95004AB79C /* libopencore-amrnb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 369D58A019AECA95004AB79C /* libopencore-amrnb.a */; }; 369D58B019AECA95004AB79C /* libopencore-amrwb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 369D58A119AECA95004AB79C /* libopencore-amrwb.a */; }; 369D58B219AECA95004AB79C /* CafRecordWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 369D58A519AECA95004AB79C /* CafRecordWriter.m */; }; 369D58B319AECA95004AB79C /* MLAudioMeterObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 369D58A719AECA95004AB79C /* MLAudioMeterObserver.m */; }; 369D58B419AECA95004AB79C /* MLAudioRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 369D58A919AECA95004AB79C /* MLAudioRecorder.m */; }; 369D58B619AECA95004AB79C /* Mp3RecordWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 369D58AE19AECA95004AB79C /* Mp3RecordWriter.m */; }; 36B359EA1A47CB10004C70C6 /* AmrRecordInBufferWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 36B359E91A47CB10004C70C6 /* AmrRecordInBufferWriter.m */; }; 36DE7E771921060C00E3067D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DE7E761921060C00E3067D /* Foundation.framework */; }; 36DE7E791921060C00E3067D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DE7E781921060C00E3067D /* CoreGraphics.framework */; }; 36DE7E7B1921060C00E3067D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DE7E7A1921060C00E3067D /* UIKit.framework */; }; 36DE7E811921060D00E3067D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 36DE7E7F1921060C00E3067D /* InfoPlist.strings */; }; 36DE7E831921060D00E3067D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 36DE7E821921060D00E3067D /* main.m */; }; 36DE7E871921060D00E3067D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 36DE7E861921060D00E3067D /* AppDelegate.m */; }; 36DE7E8A1921060D00E3067D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 36DE7E881921060D00E3067D /* Main.storyboard */; }; 36DE7E8D1921060D00E3067D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 36DE7E8C1921060D00E3067D /* ViewController.m */; }; 36DE7E8F1921060D00E3067D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 36DE7E8E1921060D00E3067D /* Images.xcassets */; }; 36DE7E961921060D00E3067D /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DE7E951921060D00E3067D /* XCTest.framework */; }; 36DE7E971921060D00E3067D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DE7E761921060C00E3067D /* Foundation.framework */; }; 36DE7E981921060D00E3067D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DE7E7A1921060C00E3067D /* UIKit.framework */; }; 36DE7EA01921060D00E3067D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 36DE7E9E1921060D00E3067D /* InfoPlist.strings */; }; 36DE7EA21921060D00E3067D /* MLRecorderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 36DE7EA11921060D00E3067D /* MLRecorderTests.m */; }; 36DE7EC3192106CD00E3067D /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DE7EC2192106CD00E3067D /* AudioToolbox.framework */; }; 36DE7EC5192106D100E3067D /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DE7EC4192106D100E3067D /* AVFoundation.framework */; }; 36DEA2341C3F805B00707189 /* AmrRecordWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 36DEA2331C3F805B00707189 /* AmrRecordWriter.m */; }; 36E62AE41A4454BC00A6FE9D /* RealTimeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 36E62AE31A4454BC00A6FE9D /* RealTimeViewController.m */; }; 36E62AE81A44568400A6FE9D /* CafRecordInBufferWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 36E62AE71A44568400A6FE9D /* CafRecordInBufferWriter.m */; }; 36E62AEE1A44A4D400A6FE9D /* MLAudioRealTimePlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 36E62AED1A44A4D400A6FE9D /* MLAudioRealTimePlayer.m */; }; 36EF89D81B3E9A2D00551581 /* lame.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36EF89D71B3E9A2D00551581 /* lame.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 36DE7E991921060D00E3067D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36DE7E6B1921060C00E3067D /* Project object */; proxyType = 1; remoteGlobalIDString = 36DE7E721921060C00E3067D; remoteInfo = MLRecorder; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 362F41081A0377D60084A844 /* MLAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLAudioPlayer.h; sourceTree = ""; }; 362F41091A0377D60084A844 /* MLAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLAudioPlayer.m; sourceTree = ""; }; 365F94EC19AECDF9001BB9A0 /* PlayNetworkVoiceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayNetworkVoiceViewController.h; sourceTree = ""; }; 365F94ED19AECDF9001BB9A0 /* PlayNetworkVoiceViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayNetworkVoiceViewController.m; sourceTree = ""; }; 365F94F119AECE7F001BB9A0 /* AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworking.h; sourceTree = ""; }; 365F94F319AECE7F001BB9A0 /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperation.h; sourceTree = ""; }; 365F94F419AECE7F001BB9A0 /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPRequestOperation.m; sourceTree = ""; }; 365F94F519AECE7F001BB9A0 /* AFHTTPRequestOperationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperationManager.h; sourceTree = ""; }; 365F94F619AECE7F001BB9A0 /* AFHTTPRequestOperationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPRequestOperationManager.m; sourceTree = ""; }; 365F94F719AECE7F001BB9A0 /* AFURLConnectionOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLConnectionOperation.h; sourceTree = ""; }; 365F94F819AECE7F001BB9A0 /* AFURLConnectionOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLConnectionOperation.m; sourceTree = ""; }; 365F94FA19AECE7F001BB9A0 /* AFHTTPSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPSessionManager.h; sourceTree = ""; }; 365F94FB19AECE7F001BB9A0 /* AFHTTPSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPSessionManager.m; sourceTree = ""; }; 365F94FC19AECE7F001BB9A0 /* AFURLSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLSessionManager.h; sourceTree = ""; }; 365F94FD19AECE7F001BB9A0 /* AFURLSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLSessionManager.m; sourceTree = ""; }; 365F94FF19AECE7F001BB9A0 /* AFNetworkReachabilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworkReachabilityManager.h; sourceTree = ""; }; 365F950019AECE7F001BB9A0 /* AFNetworkReachabilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFNetworkReachabilityManager.m; sourceTree = ""; }; 365F950219AECE7F001BB9A0 /* AFSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFSecurityPolicy.h; sourceTree = ""; }; 365F950319AECE7F001BB9A0 /* AFSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFSecurityPolicy.m; sourceTree = ""; }; 365F950519AECE7F001BB9A0 /* AFURLRequestSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLRequestSerialization.h; sourceTree = ""; }; 365F950619AECE7F001BB9A0 /* AFURLRequestSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLRequestSerialization.m; sourceTree = ""; }; 365F950719AECE7F001BB9A0 /* AFURLResponseSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLResponseSerialization.h; sourceTree = ""; }; 365F950819AECE7F001BB9A0 /* AFURLResponseSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLResponseSerialization.m; sourceTree = ""; }; 365F950A19AECE7F001BB9A0 /* AFNetworkActivityIndicatorManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworkActivityIndicatorManager.h; sourceTree = ""; }; 365F950B19AECE7F001BB9A0 /* AFNetworkActivityIndicatorManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFNetworkActivityIndicatorManager.m; sourceTree = ""; }; 365F950C19AECE7F001BB9A0 /* UIActivityIndicatorView+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIActivityIndicatorView+AFNetworking.h"; sourceTree = ""; }; 365F950D19AECE7F001BB9A0 /* UIActivityIndicatorView+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIActivityIndicatorView+AFNetworking.m"; sourceTree = ""; }; 365F950E19AECE7F001BB9A0 /* UIAlertView+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+AFNetworking.h"; sourceTree = ""; }; 365F950F19AECE7F001BB9A0 /* UIAlertView+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+AFNetworking.m"; sourceTree = ""; }; 365F951019AECE7F001BB9A0 /* UIButton+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+AFNetworking.h"; sourceTree = ""; }; 365F951119AECE7F001BB9A0 /* UIButton+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+AFNetworking.m"; sourceTree = ""; }; 365F951219AECE7F001BB9A0 /* UIImageView+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+AFNetworking.h"; sourceTree = ""; }; 365F951319AECE7F001BB9A0 /* UIImageView+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+AFNetworking.m"; sourceTree = ""; }; 365F951419AECE7F001BB9A0 /* UIKit+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIKit+AFNetworking.h"; sourceTree = ""; }; 365F951519AECE7F001BB9A0 /* UIProgressView+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIProgressView+AFNetworking.h"; sourceTree = ""; }; 365F951619AECE7F001BB9A0 /* UIProgressView+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIProgressView+AFNetworking.m"; sourceTree = ""; }; 365F951719AECE7F001BB9A0 /* UIRefreshControl+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIRefreshControl+AFNetworking.h"; sourceTree = ""; }; 365F951819AECE7F001BB9A0 /* UIRefreshControl+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIRefreshControl+AFNetworking.m"; sourceTree = ""; }; 365F951919AECE7F001BB9A0 /* UIWebView+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIWebView+AFNetworking.h"; sourceTree = ""; }; 365F951A19AECE7F001BB9A0 /* UIWebView+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIWebView+AFNetworking.m"; sourceTree = ""; }; 365F951C19AECE7F001BB9A0 /* MLAmrPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLAmrPlayer.h; sourceTree = ""; }; 365F951D19AECE7F001BB9A0 /* MLAmrPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLAmrPlayer.m; sourceTree = ""; }; 365F951F19AECE7F001BB9A0 /* MLDataCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLDataCache.h; sourceTree = ""; }; 365F952019AECE7F001BB9A0 /* MLDataCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLDataCache.m; sourceTree = ""; }; 365F952219AECE7F001BB9A0 /* MLDataResponseSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLDataResponseSerializer.h; sourceTree = ""; }; 365F952319AECE7F001BB9A0 /* MLDataResponseSerializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLDataResponseSerializer.m; sourceTree = ""; }; 365F952419AECE7F001BB9A0 /* MLPlayVoiceButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLPlayVoiceButton.h; sourceTree = ""; }; 365F952519AECE7F001BB9A0 /* MLPlayVoiceButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLPlayVoiceButton.m; sourceTree = ""; }; 365F953B19AED238001BB9A0 /* Debug.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Debug.h; sourceTree = ""; }; 365F954D19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying000@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ReceiverVoiceNodePlaying000@2x.png"; sourceTree = ""; }; 365F954E19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying001@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ReceiverVoiceNodePlaying001@2x.png"; sourceTree = ""; }; 365F954F19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying002@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ReceiverVoiceNodePlaying002@2x.png"; sourceTree = ""; }; 365F955019AED3A5001BB9A0 /* ReceiverVoiceNodePlaying003@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ReceiverVoiceNodePlaying003@2x.png"; sourceTree = ""; }; 365F955119AED3A5001BB9A0 /* SenderVoiceNodePlaying000@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "SenderVoiceNodePlaying000@2x.png"; sourceTree = ""; }; 365F955219AED3A5001BB9A0 /* SenderVoiceNodePlaying001@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "SenderVoiceNodePlaying001@2x.png"; sourceTree = ""; }; 365F955319AED3A5001BB9A0 /* SenderVoiceNodePlaying002@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "SenderVoiceNodePlaying002@2x.png"; sourceTree = ""; }; 365F955419AED3A5001BB9A0 /* SenderVoiceNodePlaying003@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "SenderVoiceNodePlaying003@2x.png"; sourceTree = ""; }; 368DCC28192FC3EF001F3574 /* test2.amr */ = {isa = PBXFileReference; lastKnownFileType = file; path = test2.amr; sourceTree = ""; }; 369D589019AECA64004AB79C /* AmrPlayerReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AmrPlayerReader.h; sourceTree = ""; }; 369D589119AECA64004AB79C /* AmrPlayerReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AmrPlayerReader.m; sourceTree = ""; }; 369D589A19AECA95004AB79C /* interf_dec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interf_dec.h; sourceTree = ""; }; 369D589B19AECA95004AB79C /* interf_enc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interf_enc.h; sourceTree = ""; }; 369D589D19AECA95004AB79C /* dec_if.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dec_if.h; sourceTree = ""; }; 369D589E19AECA95004AB79C /* if_rom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = if_rom.h; sourceTree = ""; }; 369D58A019AECA95004AB79C /* libopencore-amrnb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopencore-amrnb.a"; sourceTree = ""; }; 369D58A119AECA95004AB79C /* libopencore-amrwb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopencore-amrwb.a"; sourceTree = ""; }; 369D58A219AECA95004AB79C /* AmrRecordWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AmrRecordWriter.h; sourceTree = ""; }; 369D58A419AECA95004AB79C /* CafRecordWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CafRecordWriter.h; sourceTree = ""; }; 369D58A519AECA95004AB79C /* CafRecordWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CafRecordWriter.m; sourceTree = ""; }; 369D58A619AECA95004AB79C /* MLAudioMeterObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLAudioMeterObserver.h; sourceTree = ""; }; 369D58A719AECA95004AB79C /* MLAudioMeterObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLAudioMeterObserver.m; sourceTree = ""; }; 369D58A819AECA95004AB79C /* MLAudioRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLAudioRecorder.h; sourceTree = ""; }; 369D58A919AECA95004AB79C /* MLAudioRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLAudioRecorder.m; sourceTree = ""; }; 369D58AD19AECA95004AB79C /* Mp3RecordWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mp3RecordWriter.h; sourceTree = ""; }; 369D58AE19AECA95004AB79C /* Mp3RecordWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Mp3RecordWriter.m; sourceTree = ""; }; 36B359E81A47CB10004C70C6 /* AmrRecordInBufferWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AmrRecordInBufferWriter.h; sourceTree = ""; }; 36B359E91A47CB10004C70C6 /* AmrRecordInBufferWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AmrRecordInBufferWriter.m; sourceTree = ""; }; 36C3B7E1192C798F00B6063D /* test.amr */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.amr; sourceTree = ""; }; 36DE7E731921060C00E3067D /* MLRecorder.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLRecorder.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36DE7E761921060C00E3067D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 36DE7E781921060C00E3067D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 36DE7E7A1921060C00E3067D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 36DE7E7E1921060C00E3067D /* MLRecorder-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MLRecorder-Info.plist"; sourceTree = ""; }; 36DE7E801921060C00E3067D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 36DE7E821921060D00E3067D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 36DE7E841921060D00E3067D /* MLRecorder-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MLRecorder-Prefix.pch"; sourceTree = ""; }; 36DE7E851921060D00E3067D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 36DE7E861921060D00E3067D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 36DE7E891921060D00E3067D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 36DE7E8B1921060D00E3067D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 36DE7E8C1921060D00E3067D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 36DE7E8E1921060D00E3067D /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 36DE7E941921060D00E3067D /* MLRecorderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MLRecorderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 36DE7E951921060D00E3067D /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 36DE7E9D1921060D00E3067D /* MLRecorderTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MLRecorderTests-Info.plist"; sourceTree = ""; }; 36DE7E9F1921060D00E3067D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 36DE7EA11921060D00E3067D /* MLRecorderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MLRecorderTests.m; sourceTree = ""; }; 36DE7EC2192106CD00E3067D /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 36DE7EC4192106D100E3067D /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 36DEA2331C3F805B00707189 /* AmrRecordWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AmrRecordWriter.m; sourceTree = ""; }; 36E62AE21A4454BC00A6FE9D /* RealTimeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealTimeViewController.h; sourceTree = ""; }; 36E62AE31A4454BC00A6FE9D /* RealTimeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RealTimeViewController.m; sourceTree = ""; }; 36E62AE61A44568400A6FE9D /* CafRecordInBufferWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CafRecordInBufferWriter.h; sourceTree = ""; }; 36E62AE71A44568400A6FE9D /* CafRecordInBufferWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CafRecordInBufferWriter.m; sourceTree = ""; }; 36E62AE91A44A35800A6FE9D /* record2.amr */ = {isa = PBXFileReference; lastKnownFileType = file; path = record2.amr; sourceTree = ""; }; 36E62AEA1A44A35800A6FE9D /* record1.amr */ = {isa = PBXFileReference; lastKnownFileType = file; path = record1.amr; sourceTree = ""; }; 36E62AEC1A44A4D400A6FE9D /* MLAudioRealTimePlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLAudioRealTimePlayer.h; sourceTree = ""; }; 36E62AED1A44A4D400A6FE9D /* MLAudioRealTimePlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLAudioRealTimePlayer.m; sourceTree = ""; }; 36EF89D71B3E9A2D00551581 /* lame.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = lame.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 36DE7E701921060C00E3067D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 36DE7EC5192106D100E3067D /* AVFoundation.framework in Frameworks */, 36EF89D81B3E9A2D00551581 /* lame.framework in Frameworks */, 369D58AF19AECA95004AB79C /* libopencore-amrnb.a in Frameworks */, 36DE7EC3192106CD00E3067D /* AudioToolbox.framework in Frameworks */, 36DE7E791921060C00E3067D /* CoreGraphics.framework in Frameworks */, 36DE7E7B1921060C00E3067D /* UIKit.framework in Frameworks */, 369D58B019AECA95004AB79C /* libopencore-amrwb.a in Frameworks */, 36DE7E771921060C00E3067D /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 36DE7E911921060D00E3067D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 36DE7E961921060D00E3067D /* XCTest.framework in Frameworks */, 36DE7E981921060D00E3067D /* UIKit.framework in Frameworks */, 36DE7E971921060D00E3067D /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 365F94EF19AECE7F001BB9A0 /* AFNetwoking */ = { isa = PBXGroup; children = ( 365F94F019AECE7F001BB9A0 /* AFNetworking */, 365F950919AECE7F001BB9A0 /* UIKit+AFNetworking */, ); path = AFNetwoking; sourceTree = ""; }; 365F94F019AECE7F001BB9A0 /* AFNetworking */ = { isa = PBXGroup; children = ( 365F94F119AECE7F001BB9A0 /* AFNetworking.h */, 365F94F219AECE7F001BB9A0 /* NSURLConnection */, 365F94F919AECE7F001BB9A0 /* NSURLSession */, 365F94FE19AECE7F001BB9A0 /* Reachability */, 365F950119AECE7F001BB9A0 /* Security */, 365F950419AECE7F001BB9A0 /* Serialization */, ); path = AFNetworking; sourceTree = ""; }; 365F94F219AECE7F001BB9A0 /* NSURLConnection */ = { isa = PBXGroup; children = ( 365F94F319AECE7F001BB9A0 /* AFHTTPRequestOperation.h */, 365F94F419AECE7F001BB9A0 /* AFHTTPRequestOperation.m */, 365F94F519AECE7F001BB9A0 /* AFHTTPRequestOperationManager.h */, 365F94F619AECE7F001BB9A0 /* AFHTTPRequestOperationManager.m */, 365F94F719AECE7F001BB9A0 /* AFURLConnectionOperation.h */, 365F94F819AECE7F001BB9A0 /* AFURLConnectionOperation.m */, ); path = NSURLConnection; sourceTree = ""; }; 365F94F919AECE7F001BB9A0 /* NSURLSession */ = { isa = PBXGroup; children = ( 365F94FA19AECE7F001BB9A0 /* AFHTTPSessionManager.h */, 365F94FB19AECE7F001BB9A0 /* AFHTTPSessionManager.m */, 365F94FC19AECE7F001BB9A0 /* AFURLSessionManager.h */, 365F94FD19AECE7F001BB9A0 /* AFURLSessionManager.m */, ); path = NSURLSession; sourceTree = ""; }; 365F94FE19AECE7F001BB9A0 /* Reachability */ = { isa = PBXGroup; children = ( 365F94FF19AECE7F001BB9A0 /* AFNetworkReachabilityManager.h */, 365F950019AECE7F001BB9A0 /* AFNetworkReachabilityManager.m */, ); path = Reachability; sourceTree = ""; }; 365F950119AECE7F001BB9A0 /* Security */ = { isa = PBXGroup; children = ( 365F950219AECE7F001BB9A0 /* AFSecurityPolicy.h */, 365F950319AECE7F001BB9A0 /* AFSecurityPolicy.m */, ); path = Security; sourceTree = ""; }; 365F950419AECE7F001BB9A0 /* Serialization */ = { isa = PBXGroup; children = ( 365F950519AECE7F001BB9A0 /* AFURLRequestSerialization.h */, 365F950619AECE7F001BB9A0 /* AFURLRequestSerialization.m */, 365F950719AECE7F001BB9A0 /* AFURLResponseSerialization.h */, 365F950819AECE7F001BB9A0 /* AFURLResponseSerialization.m */, ); path = Serialization; sourceTree = ""; }; 365F950919AECE7F001BB9A0 /* UIKit+AFNetworking */ = { isa = PBXGroup; children = ( 365F950A19AECE7F001BB9A0 /* AFNetworkActivityIndicatorManager.h */, 365F950B19AECE7F001BB9A0 /* AFNetworkActivityIndicatorManager.m */, 365F950C19AECE7F001BB9A0 /* UIActivityIndicatorView+AFNetworking.h */, 365F950D19AECE7F001BB9A0 /* UIActivityIndicatorView+AFNetworking.m */, 365F950E19AECE7F001BB9A0 /* UIAlertView+AFNetworking.h */, 365F950F19AECE7F001BB9A0 /* UIAlertView+AFNetworking.m */, 365F951019AECE7F001BB9A0 /* UIButton+AFNetworking.h */, 365F951119AECE7F001BB9A0 /* UIButton+AFNetworking.m */, 365F951219AECE7F001BB9A0 /* UIImageView+AFNetworking.h */, 365F951319AECE7F001BB9A0 /* UIImageView+AFNetworking.m */, 365F951419AECE7F001BB9A0 /* UIKit+AFNetworking.h */, 365F951519AECE7F001BB9A0 /* UIProgressView+AFNetworking.h */, 365F951619AECE7F001BB9A0 /* UIProgressView+AFNetworking.m */, 365F951719AECE7F001BB9A0 /* UIRefreshControl+AFNetworking.h */, 365F951819AECE7F001BB9A0 /* UIRefreshControl+AFNetworking.m */, 365F951919AECE7F001BB9A0 /* UIWebView+AFNetworking.h */, 365F951A19AECE7F001BB9A0 /* UIWebView+AFNetworking.m */, ); path = "UIKit+AFNetworking"; sourceTree = ""; }; 365F951B19AECE7F001BB9A0 /* MLPlayVoiceButton */ = { isa = PBXGroup; children = ( 365F953C19AED322001BB9A0 /* VoicePlaySign */, 365F951C19AECE7F001BB9A0 /* MLAmrPlayer.h */, 365F951D19AECE7F001BB9A0 /* MLAmrPlayer.m */, 365F951E19AECE7F001BB9A0 /* MLDataCache */, 365F952119AECE7F001BB9A0 /* MLDataResponseSerializer */, 365F952419AECE7F001BB9A0 /* MLPlayVoiceButton.h */, 365F952519AECE7F001BB9A0 /* MLPlayVoiceButton.m */, ); path = MLPlayVoiceButton; sourceTree = ""; }; 365F951E19AECE7F001BB9A0 /* MLDataCache */ = { isa = PBXGroup; children = ( 365F951F19AECE7F001BB9A0 /* MLDataCache.h */, 365F952019AECE7F001BB9A0 /* MLDataCache.m */, ); path = MLDataCache; sourceTree = ""; }; 365F952119AECE7F001BB9A0 /* MLDataResponseSerializer */ = { isa = PBXGroup; children = ( 365F952219AECE7F001BB9A0 /* MLDataResponseSerializer.h */, 365F952319AECE7F001BB9A0 /* MLDataResponseSerializer.m */, ); path = MLDataResponseSerializer; sourceTree = ""; }; 365F953C19AED322001BB9A0 /* VoicePlaySign */ = { isa = PBXGroup; children = ( 365F954D19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying000@2x.png */, 365F954E19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying001@2x.png */, 365F954F19AED3A5001BB9A0 /* ReceiverVoiceNodePlaying002@2x.png */, 365F955019AED3A5001BB9A0 /* ReceiverVoiceNodePlaying003@2x.png */, 365F955119AED3A5001BB9A0 /* SenderVoiceNodePlaying000@2x.png */, 365F955219AED3A5001BB9A0 /* SenderVoiceNodePlaying001@2x.png */, 365F955319AED3A5001BB9A0 /* SenderVoiceNodePlaying002@2x.png */, 365F955419AED3A5001BB9A0 /* SenderVoiceNodePlaying003@2x.png */, ); path = VoicePlaySign; sourceTree = ""; }; 369D588F19AECA64004AB79C /* MLAudioPlayer */ = { isa = PBXGroup; children = ( 369D589019AECA64004AB79C /* AmrPlayerReader.h */, 369D589119AECA64004AB79C /* AmrPlayerReader.m */, 362F41081A0377D60084A844 /* MLAudioPlayer.h */, 362F41091A0377D60084A844 /* MLAudioPlayer.m */, ); path = MLAudioPlayer; sourceTree = ""; }; 369D589619AECA95004AB79C /* MLAudioRecorder */ = { isa = PBXGroup; children = ( 36EF89D61B3E9A2D00551581 /* mp3_en_de */, 369D589719AECA95004AB79C /* amr_en_de */, 369D58A219AECA95004AB79C /* AmrRecordWriter.h */, 36DEA2331C3F805B00707189 /* AmrRecordWriter.m */, 369D58A419AECA95004AB79C /* CafRecordWriter.h */, 369D58A519AECA95004AB79C /* CafRecordWriter.m */, 369D58A619AECA95004AB79C /* MLAudioMeterObserver.h */, 369D58A719AECA95004AB79C /* MLAudioMeterObserver.m */, 369D58A819AECA95004AB79C /* MLAudioRecorder.h */, 369D58A919AECA95004AB79C /* MLAudioRecorder.m */, 369D58AD19AECA95004AB79C /* Mp3RecordWriter.h */, 369D58AE19AECA95004AB79C /* Mp3RecordWriter.m */, ); path = MLAudioRecorder; sourceTree = ""; }; 369D589719AECA95004AB79C /* amr_en_de */ = { isa = PBXGroup; children = ( 369D589819AECA95004AB79C /* include */, 369D589F19AECA95004AB79C /* lib */, ); path = amr_en_de; sourceTree = ""; }; 369D589819AECA95004AB79C /* include */ = { isa = PBXGroup; children = ( 369D589919AECA95004AB79C /* opencore-amrnb */, 369D589C19AECA95004AB79C /* opencore-amrwb */, ); path = include; sourceTree = ""; }; 369D589919AECA95004AB79C /* opencore-amrnb */ = { isa = PBXGroup; children = ( 369D589A19AECA95004AB79C /* interf_dec.h */, 369D589B19AECA95004AB79C /* interf_enc.h */, ); path = "opencore-amrnb"; sourceTree = ""; }; 369D589C19AECA95004AB79C /* opencore-amrwb */ = { isa = PBXGroup; children = ( 369D589D19AECA95004AB79C /* dec_if.h */, 369D589E19AECA95004AB79C /* if_rom.h */, ); path = "opencore-amrwb"; sourceTree = ""; }; 369D589F19AECA95004AB79C /* lib */ = { isa = PBXGroup; children = ( 369D58A019AECA95004AB79C /* libopencore-amrnb.a */, 369D58A119AECA95004AB79C /* libopencore-amrwb.a */, ); path = lib; sourceTree = ""; }; 36DE7E6A1921060C00E3067D = { isa = PBXGroup; children = ( 36E62AE91A44A35800A6FE9D /* record2.amr */, 36E62AEA1A44A35800A6FE9D /* record1.amr */, 36DE7E7C1921060C00E3067D /* MLRecorder */, 36DE7E9B1921060D00E3067D /* MLRecorderTests */, 36DE7E751921060C00E3067D /* Frameworks */, 36DE7E741921060C00E3067D /* Products */, ); sourceTree = ""; }; 36DE7E741921060C00E3067D /* Products */ = { isa = PBXGroup; children = ( 36DE7E731921060C00E3067D /* MLRecorder.app */, 36DE7E941921060D00E3067D /* MLRecorderTests.xctest */, ); name = Products; sourceTree = ""; }; 36DE7E751921060C00E3067D /* Frameworks */ = { isa = PBXGroup; children = ( 36DE7EC4192106D100E3067D /* AVFoundation.framework */, 36DE7EC2192106CD00E3067D /* AudioToolbox.framework */, 36DE7E761921060C00E3067D /* Foundation.framework */, 36DE7E781921060C00E3067D /* CoreGraphics.framework */, 36DE7E7A1921060C00E3067D /* UIKit.framework */, 36DE7E951921060D00E3067D /* XCTest.framework */, ); name = Frameworks; sourceTree = ""; }; 36DE7E7C1921060C00E3067D /* MLRecorder */ = { isa = PBXGroup; children = ( 365F953B19AED238001BB9A0 /* Debug.h */, 365F94EF19AECE7F001BB9A0 /* AFNetwoking */, 365F951B19AECE7F001BB9A0 /* MLPlayVoiceButton */, 369D589619AECA95004AB79C /* MLAudioRecorder */, 369D588F19AECA64004AB79C /* MLAudioPlayer */, 36E62AEB1A44A4D400A6FE9D /* MLAudioRealTimePlayer */, 36E62AE51A44565100A6FE9D /* TestRealTime */, 36C3B7E1192C798F00B6063D /* test.amr */, 368DCC28192FC3EF001F3574 /* test2.amr */, 36DE7E851921060D00E3067D /* AppDelegate.h */, 36DE7E861921060D00E3067D /* AppDelegate.m */, 36DE7E881921060D00E3067D /* Main.storyboard */, 36DE7E8B1921060D00E3067D /* ViewController.h */, 36DE7E8C1921060D00E3067D /* ViewController.m */, 365F94EC19AECDF9001BB9A0 /* PlayNetworkVoiceViewController.h */, 365F94ED19AECDF9001BB9A0 /* PlayNetworkVoiceViewController.m */, 36E62AE21A4454BC00A6FE9D /* RealTimeViewController.h */, 36E62AE31A4454BC00A6FE9D /* RealTimeViewController.m */, 36DE7E8E1921060D00E3067D /* Images.xcassets */, 36DE7E7D1921060C00E3067D /* Supporting Files */, ); path = MLRecorder; sourceTree = ""; }; 36DE7E7D1921060C00E3067D /* Supporting Files */ = { isa = PBXGroup; children = ( 36DE7E7E1921060C00E3067D /* MLRecorder-Info.plist */, 36DE7E7F1921060C00E3067D /* InfoPlist.strings */, 36DE7E821921060D00E3067D /* main.m */, 36DE7E841921060D00E3067D /* MLRecorder-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; }; 36DE7E9B1921060D00E3067D /* MLRecorderTests */ = { isa = PBXGroup; children = ( 36DE7EA11921060D00E3067D /* MLRecorderTests.m */, 36DE7E9C1921060D00E3067D /* Supporting Files */, ); path = MLRecorderTests; sourceTree = ""; }; 36DE7E9C1921060D00E3067D /* Supporting Files */ = { isa = PBXGroup; children = ( 36DE7E9D1921060D00E3067D /* MLRecorderTests-Info.plist */, 36DE7E9E1921060D00E3067D /* InfoPlist.strings */, ); name = "Supporting Files"; sourceTree = ""; }; 36E62AE51A44565100A6FE9D /* TestRealTime */ = { isa = PBXGroup; children = ( 36E62AE61A44568400A6FE9D /* CafRecordInBufferWriter.h */, 36E62AE71A44568400A6FE9D /* CafRecordInBufferWriter.m */, 36B359E81A47CB10004C70C6 /* AmrRecordInBufferWriter.h */, 36B359E91A47CB10004C70C6 /* AmrRecordInBufferWriter.m */, ); name = TestRealTime; sourceTree = ""; }; 36E62AEB1A44A4D400A6FE9D /* MLAudioRealTimePlayer */ = { isa = PBXGroup; children = ( 36E62AEC1A44A4D400A6FE9D /* MLAudioRealTimePlayer.h */, 36E62AED1A44A4D400A6FE9D /* MLAudioRealTimePlayer.m */, ); path = MLAudioRealTimePlayer; sourceTree = ""; }; 36EF89D61B3E9A2D00551581 /* mp3_en_de */ = { isa = PBXGroup; children = ( 36EF89D71B3E9A2D00551581 /* lame.framework */, ); path = mp3_en_de; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 36DE7E721921060C00E3067D /* MLRecorder */ = { isa = PBXNativeTarget; buildConfigurationList = 36DE7EA51921060D00E3067D /* Build configuration list for PBXNativeTarget "MLRecorder" */; buildPhases = ( 36DE7E6F1921060C00E3067D /* Sources */, 36DE7E701921060C00E3067D /* Frameworks */, 36DE7E711921060C00E3067D /* Resources */, ); buildRules = ( ); dependencies = ( ); name = MLRecorder; productName = MLRecorder; productReference = 36DE7E731921060C00E3067D /* MLRecorder.app */; productType = "com.apple.product-type.application"; }; 36DE7E931921060D00E3067D /* MLRecorderTests */ = { isa = PBXNativeTarget; buildConfigurationList = 36DE7EA81921060D00E3067D /* Build configuration list for PBXNativeTarget "MLRecorderTests" */; buildPhases = ( 36DE7E901921060D00E3067D /* Sources */, 36DE7E911921060D00E3067D /* Frameworks */, 36DE7E921921060D00E3067D /* Resources */, ); buildRules = ( ); dependencies = ( 36DE7E9A1921060D00E3067D /* PBXTargetDependency */, ); name = MLRecorderTests; productName = MLRecorderTests; productReference = 36DE7E941921060D00E3067D /* MLRecorderTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 36DE7E6B1921060C00E3067D /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0510; ORGANIZATIONNAME = molon; TargetAttributes = { 36DE7E931921060D00E3067D = { TestTargetID = 36DE7E721921060C00E3067D; }; }; }; buildConfigurationList = 36DE7E6E1921060C00E3067D /* Build configuration list for PBXProject "MLRecorder" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 36DE7E6A1921060C00E3067D; productRefGroup = 36DE7E741921060C00E3067D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 36DE7E721921060C00E3067D /* MLRecorder */, 36DE7E931921060D00E3067D /* MLRecorderTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 36DE7E711921060C00E3067D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 365F955A19AED3A5001BB9A0 /* SenderVoiceNodePlaying001@2x.png in Resources */, 365F955919AED3A5001BB9A0 /* SenderVoiceNodePlaying000@2x.png in Resources */, 368DCC29192FC436001F3574 /* test2.amr in Resources */, 36197FFC192F2E9C00C9198F /* test.amr in Resources */, 365F955B19AED3A5001BB9A0 /* SenderVoiceNodePlaying002@2x.png in Resources */, 36DE7E8F1921060D00E3067D /* Images.xcassets in Resources */, 365F955C19AED3A5001BB9A0 /* SenderVoiceNodePlaying003@2x.png in Resources */, 365F955619AED3A5001BB9A0 /* ReceiverVoiceNodePlaying001@2x.png in Resources */, 36DE7E811921060D00E3067D /* InfoPlist.strings in Resources */, 365F955519AED3A5001BB9A0 /* ReceiverVoiceNodePlaying000@2x.png in Resources */, 365F955719AED3A5001BB9A0 /* ReceiverVoiceNodePlaying002@2x.png in Resources */, 365F955819AED3A5001BB9A0 /* ReceiverVoiceNodePlaying003@2x.png in Resources */, 36DE7E8A1921060D00E3067D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 36DE7E921921060D00E3067D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 36DE7EA01921060D00E3067D /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 36DE7E6F1921060C00E3067D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 36DEA2341C3F805B00707189 /* AmrRecordWriter.m in Sources */, 365F952819AECE7F001BB9A0 /* AFURLConnectionOperation.m in Sources */, 365F952619AECE7F001BB9A0 /* AFHTTPRequestOperation.m in Sources */, 362F410A1A0377D60084A844 /* MLAudioPlayer.m in Sources */, 369D58B319AECA95004AB79C /* MLAudioMeterObserver.m in Sources */, 365F94EE19AECDF9001BB9A0 /* PlayNetworkVoiceViewController.m in Sources */, 365F952F19AECE7F001BB9A0 /* AFNetworkActivityIndicatorManager.m in Sources */, 36E62AE41A4454BC00A6FE9D /* RealTimeViewController.m in Sources */, 369D589419AECA64004AB79C /* AmrPlayerReader.m in Sources */, 369D58B619AECA95004AB79C /* Mp3RecordWriter.m in Sources */, 365F952719AECE7F001BB9A0 /* AFHTTPRequestOperationManager.m in Sources */, 365F952A19AECE7F001BB9A0 /* AFURLSessionManager.m in Sources */, 365F953319AECE7F001BB9A0 /* UIImageView+AFNetworking.m in Sources */, 365F953419AECE7F001BB9A0 /* UIProgressView+AFNetworking.m in Sources */, 365F952C19AECE7F001BB9A0 /* AFSecurityPolicy.m in Sources */, 365F953519AECE7F001BB9A0 /* UIRefreshControl+AFNetworking.m in Sources */, 365F953A19AECE7F001BB9A0 /* MLPlayVoiceButton.m in Sources */, 36DE7E8D1921060D00E3067D /* ViewController.m in Sources */, 365F953719AECE7F001BB9A0 /* MLAmrPlayer.m in Sources */, 365F953819AECE7F001BB9A0 /* MLDataCache.m in Sources */, 365F952D19AECE7F001BB9A0 /* AFURLRequestSerialization.m in Sources */, 365F953219AECE7F001BB9A0 /* UIButton+AFNetworking.m in Sources */, 365F953019AECE7F001BB9A0 /* UIActivityIndicatorView+AFNetworking.m in Sources */, 36E62AE81A44568400A6FE9D /* CafRecordInBufferWriter.m in Sources */, 36E62AEE1A44A4D400A6FE9D /* MLAudioRealTimePlayer.m in Sources */, 36B359EA1A47CB10004C70C6 /* AmrRecordInBufferWriter.m in Sources */, 365F952B19AECE7F001BB9A0 /* AFNetworkReachabilityManager.m in Sources */, 365F953919AECE7F001BB9A0 /* MLDataResponseSerializer.m in Sources */, 365F952E19AECE7F001BB9A0 /* AFURLResponseSerialization.m in Sources */, 36DE7E871921060D00E3067D /* AppDelegate.m in Sources */, 369D58B419AECA95004AB79C /* MLAudioRecorder.m in Sources */, 365F952919AECE7F001BB9A0 /* AFHTTPSessionManager.m in Sources */, 365F953619AECE7F001BB9A0 /* UIWebView+AFNetworking.m in Sources */, 369D58B219AECA95004AB79C /* CafRecordWriter.m in Sources */, 36DE7E831921060D00E3067D /* main.m in Sources */, 365F953119AECE7F001BB9A0 /* UIAlertView+AFNetworking.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 36DE7E901921060D00E3067D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 36DE7EA21921060D00E3067D /* MLRecorderTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 36DE7E9A1921060D00E3067D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 36DE7E721921060C00E3067D /* MLRecorder */; targetProxy = 36DE7E991921060D00E3067D /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 36DE7E7F1921060C00E3067D /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 36DE7E801921060C00E3067D /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; 36DE7E881921060D00E3067D /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 36DE7E891921060D00E3067D /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 36DE7E9E1921060D00E3067D /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 36DE7E9F1921060D00E3067D /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 36DE7EA31921060D00E3067D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 7.1; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; 36DE7EA41921060D00E3067D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 7.1; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; 36DE7EA61921060D00E3067D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", "$(PROJECT_DIR)/MLRecorder/MLAudioRecorder/mp3_en_de", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MLRecorder/MLRecorder-Prefix.pch"; INFOPLIST_FILE = "MLRecorder/MLRecorder-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 6.0; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/MLRecorder/MLAudioRecorder/amr_en_de/lib", ); PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Debug; }; 36DE7EA71921060D00E3067D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", "$(PROJECT_DIR)/MLRecorder/MLAudioRecorder/mp3_en_de", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MLRecorder/MLRecorder-Prefix.pch"; INFOPLIST_FILE = "MLRecorder/MLRecorder-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 6.0; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/MLRecorder/MLAudioRecorder/amr_en_de/lib", ); PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Release; }; 36DE7EA91921060D00E3067D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MLRecorder.app/MLRecorder"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MLRecorder/MLRecorder-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = "MLRecorderTests/MLRecorderTests-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; }; name = Debug; }; 36DE7EAA1921060D00E3067D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MLRecorder.app/MLRecorder"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MLRecorder/MLRecorder-Prefix.pch"; INFOPLIST_FILE = "MLRecorderTests/MLRecorderTests-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 36DE7E6E1921060C00E3067D /* Build configuration list for PBXProject "MLRecorder" */ = { isa = XCConfigurationList; buildConfigurations = ( 36DE7EA31921060D00E3067D /* Debug */, 36DE7EA41921060D00E3067D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 36DE7EA51921060D00E3067D /* Build configuration list for PBXNativeTarget "MLRecorder" */ = { isa = XCConfigurationList; buildConfigurations = ( 36DE7EA61921060D00E3067D /* Debug */, 36DE7EA71921060D00E3067D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 36DE7EA81921060D00E3067D /* Build configuration list for PBXNativeTarget "MLRecorderTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 36DE7EA91921060D00E3067D /* Debug */, 36DE7EAA1921060D00E3067D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 36DE7E6B1921060C00E3067D /* Project object */; } ================================================ FILE: MLRecorder.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: MLRecorder.xcodeproj/xcuserdata/molon.xcuserdatad/xcschemes/MLRecorder.xcscheme ================================================ ================================================ FILE: MLRecorder.xcodeproj/xcuserdata/molon.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState MLRecorder.xcscheme orderHint 0 SuppressBuildableAutocreation 36DE7E721921060C00E3067D primary 36DE7E931921060D00E3067D primary ================================================ FILE: MLRecorderTests/MLRecorderTests-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier com.molon.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: MLRecorderTests/MLRecorderTests.m ================================================ // // MLRecorderTests.m // MLRecorderTests // // Created by molon on 5/12/14. // Copyright (c) 2014 molon. All rights reserved. // #import @interface MLRecorderTests : XCTestCase @end @implementation MLRecorderTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testExample { XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); } @end ================================================ FILE: MLRecorderTests/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: README.md ================================================ MLAudioRecorder =============== **此库只是学习语音相关内容时候的练手之作,并不怎么适合直接用在工程里,可能存在诸多问题(此库以后也不会维护了),所以注意仅供参考学习。** 边录边转码,播放网络音频Button(本地缓存),且可作为实时语音参考。 2014.12.20 最新添加 MLAudioRealTimePlayer 可以实时播放PCM流。 工程里有demo测试,千万记得带耳机测试,否则会有录音播放死循环,在下因为这个原因耗费心神很久很久,最后被我媳妇发现。。 感谢: [wuqiong/mp3lame-for-iOS](https://github.com/wuqiong/mp3lame-for-iOS) 提供mp3lame armv64支持。