KCSP/ProductApp/ProductApp/ProductMain/ShouYe/saveManger/VChangeManager.m

199 lines
8.4 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// VChangeManager.m
// ProductApp
//
// Created by 工作 on 2025/4/8.
//
#import "VChangeManager.h"
#import <AVFoundation/AVFoundation.h>
@interface VChangeManager ()
{
AVAssetReader *assetReader;
AVAssetReaderTrackOutput *readerVideoTrackOutput, *readerAudioTrackOutput;
AVAssetWriter *assetWriter;
AVAssetWriterInput *assetVideoWriterInput, *assetAudioWriterInput;
}
@end
@implementation VChangeManager
+(id)manager
{
static dispatch_once_t once;
static VChangeManager *manager;
dispatch_once(&once, ^ {
manager = [[VChangeManager alloc] init];
});
return manager;
}
-(void)kaiShiZHUrl:(NSURL *)fileUrl back:(VChangeManagerBack)backValue
{
NSString *writerPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/12345.mp4"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:writerPath]) {
[fileManager removeItemAtPath:writerPath error:nil];
}
AVAsset *asset = [AVAsset assetWithURL:fileUrl];
NSError *error;
//创建AVAssetReader对象用来读取asset数据
assetReader = [AVAssetReader assetReaderWithAsset:asset error:&error];
AVAsset *localAsset = assetReader.asset;
if([localAsset tracksWithMediaType:AVMediaTypeVideo].count==0 || [localAsset tracksWithMediaType:AVMediaTypeAudio].count==0)
{
backValue(NO,@"");
return;
}
AVAssetTrack *videoTrack = [[localAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
AVAssetTrack *audioTrack = [[localAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
NSDictionary *videoSetting = @{(id)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
(id)kCVPixelBufferIOSurfacePropertiesKey : [NSDictionary dictionary],
};
// AVAssetReaderTrackOutput用来设置怎么读数据
readerVideoTrackOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:videoSetting];
//音频以pcm流的形似读数据
NSDictionary *audioSetting = @{AVFormatIDKey : [NSNumber numberWithUnsignedInt:kAudioFormatLinearPCM]};
readerAudioTrackOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:audioTrack outputSettings:audioSetting];
if ([assetReader canAddOutput:readerVideoTrackOutput]) {
[assetReader addOutput:readerVideoTrackOutput];
}
if ([assetReader canAddOutput:readerAudioTrackOutput]) {
[assetReader addOutput:readerAudioTrackOutput];
}
//开始读
[assetReader startReading];
NSURL *writerUrl = [NSURL fileURLWithPath:writerPath];
//创建一个写数据对象
assetWriter = [[AVAssetWriter alloc] initWithURL:writerUrl fileType:AVFileTypeMPEG4 error:nil];
//配置写数据,设置比特率,帧率等
NSDictionary *compressionProperties = @{ AVVideoAverageBitRateKey : @(1.38*1024*1024),
AVVideoExpectedSourceFrameRateKey: @(30),
AVVideoProfileLevelKey : AVVideoProfileLevelH264HighAutoLevel };
// 获取视频尺寸
CGSize videoSize = videoTrack.naturalSize;
int videoWidth = (int)videoSize.width;
int videoHeight = (int)videoSize.height;
//配置编码器宽高等
NSDictionary *compressionVideoSetting = @{
AVVideoCodecKey : AVVideoCodecTypeH264,
AVVideoWidthKey : @(videoWidth),
AVVideoHeightKey : @(videoHeight),
AVVideoCompressionPropertiesKey : compressionProperties
};
AudioChannelLayout stereoChannelLayout = {
.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo,
.mChannelBitmap = 0,
.mNumberChannelDescriptions = 0
};
NSData *channelLayoutAsData = [NSData dataWithBytes:&stereoChannelLayout length:offsetof(AudioChannelLayout, mChannelDescriptions)];
//写入音频配置
NSDictionary *compressionAudioSetting = @{
AVFormatIDKey : [NSNumber numberWithUnsignedInt:kAudioFormatMPEG4AAC],
AVEncoderBitRateKey : [NSNumber numberWithInteger:64000],
AVSampleRateKey : [NSNumber numberWithInteger:44100],
AVChannelLayoutKey : channelLayoutAsData,
AVNumberOfChannelsKey : [NSNumber numberWithUnsignedInteger:2]
};
//AVAssetWriterInput用来说明怎么写数据
assetVideoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:compressionVideoSetting];
assetAudioWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:compressionAudioSetting];
if ([assetWriter canAddInput:assetVideoWriterInput]) {
[assetWriter addInput:assetVideoWriterInput];
}
if ([assetWriter canAddInput:assetAudioWriterInput]) {
[assetWriter addInput:assetAudioWriterInput];
}
//开始写
[assetWriter startWriting];
[assetWriter startSessionAtSourceTime:kCMTimeZero];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t videoWriter = dispatch_queue_create("videoWriter", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t audioWriter = dispatch_queue_create("audioWriter", DISPATCH_QUEUE_CONCURRENT);
__block BOOL isVideoComplete = NO;
dispatch_group_enter(group);
//要想写数据就带有数据源以下是将readerVideoTrackOutput读出来的数据加入到assetVideoWriterInput中再写入本地音频和视频读取写入方式一样
__block BOOL issaveVideo = NO;
[assetVideoWriterInput requestMediaDataWhenReadyOnQueue:videoWriter usingBlock:^{
while (!isVideoComplete && assetVideoWriterInput.isReadyForMoreMediaData) {
//样本数据
@autoreleasepool {
//每次读取一个buffer
if(issaveVideo==NO)
{
issaveVideo = YES;
CMSampleBufferRef buffer = [readerVideoTrackOutput copyNextSampleBuffer];
if (buffer) {
//将读来的buffer加入到写对象中开始写
[assetVideoWriterInput appendSampleBuffer:buffer];
CFRelease(buffer);
buffer = NULL;
} else {
isVideoComplete = YES;
}
issaveVideo = NO;
}
}
}
if (isVideoComplete) {
//关闭写入会话
[assetVideoWriterInput markAsFinished];
dispatch_group_leave(group);
}
}];
__block BOOL isAudioComplete = NO;
__block BOOL issaveAudio = NO;
dispatch_group_enter(group);
[assetAudioWriterInput requestMediaDataWhenReadyOnQueue:audioWriter usingBlock:^{
while (!isAudioComplete && assetAudioWriterInput.isReadyForMoreMediaData) {
if(issaveAudio==NO)
{
issaveAudio = YES;
//样本数据
CMSampleBufferRef buffer = [readerAudioTrackOutput copyNextSampleBuffer];
if (buffer) {
[assetAudioWriterInput appendSampleBuffer:buffer];
CFRelease(buffer);
buffer = NULL;
} else {
isAudioComplete = YES;
}
issaveAudio = NO;
}
}
if (isAudioComplete) {
//关闭写入会话
[assetAudioWriterInput markAsFinished];
dispatch_group_leave(group);
}
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[assetWriter finishWritingWithCompletionHandler:^{
AVAssetWriterStatus status = assetWriter.status;
if (status == AVAssetWriterStatusCompleted) {
[assetReader cancelReading];
[assetWriter cancelWriting];
///成功
backValue(YES,writerPath);
} else {
///失败
backValue(NO,@"");
}
}];
});
}
@end