iOS-視頻剪切、拼接温眉、背景音樂(lè)类溢、混音

//剪輯時(shí)長(zhǎng)
typedef struct TimeRange {
CGFloat location;
CGFloat length;
} TimeRange;

@interface JWVideoEditManage : NSObject
/**
剪輯視頻

@param videoUrl 路徑
@param videoRange 剪輯范圍
@param completionHandle 完成
*/

  • (void)captureVideoWithVideoUrl:(NSURL *)videoUrl
    timeRange:(TimeRange)videoRange
    completion:(void (^)(NSURL * outPutUrl,NSError * error))completionHandle;

/**
去除視頻聲道聲音

@param videoUrl 路徑
@param completionHandle 完成
*/

  • (void)deleteVideoAudio:(NSURL )videoUrl
    completion:(void (^)(NSURL * outPutUrl,NSError * error))completionHandle;
    /
    *
    合成配音視頻

@param videoUrl 無(wú)聲視頻
@param audioUrl 聲音路徑
@param completionHandle nil
*/

  • (void)addBackgroundMiusicWithVideoUrlStr:(NSURL )videoUrl
    audioUrl:(NSURL )audioUrl
    completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle;
    /

    合成配音視頻

@param videoUrl 視頻
@param audioUrl 音樂(lè)
@param musicStart 音樂(lè)開(kāi)始時(shí)間
@param videoRange 視頻截取區(qū)間
@param videoVolume 視頻音量
@param musicVolume 音樂(lè)音量
@param completionHandle
*/

  • (void)addBackgroundMiusicWithVideoUrlStr:(NSURL *)videoUrl
    audioUrl:(NSURL *)audioUrl
    musicStartTime:(CGFloat)musicStart
    videoTimeRange:(TimeRange)videoRange
    videoVolume:(CGFloat)videoVolume
    musicVolume:(CGFloat)musicVolume
    completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle;

/**
轉(zhuǎn)換視頻格式,導(dǎo)出視頻

@param inputURL 輸入源
@param handler 回調(diào)
*/

  • (void)exportSessionVideoWithInputURL:(NSURL*)inputURL
    completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle;

/**
相冊(cè)讀取之后照片路徑辩诞,內(nèi)部會(huì)將相冊(cè)內(nèi)部的視頻緩存至沙盒路徑

@param info imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
@param result 緩存路徑
*/

  • (void)getVideoLocalPath:(NSDictionary<NSString *,id> *)info
    result:(void(^)(NSURL * videoURL))result;

/**
獲取視頻縮略圖

@param url url
*/

  • (CGImageRef)thumbnailImageRequestWithURL:(NSURL *)url;

/**
清楚剪輯視頻路徑
*/

  • (void)clearCutVideoCache;

  • (void)exportSessionVideoWithInputURL:(NSURL)inputURL completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle fiter:(CIFilter)filter;

//MARK: 獲取視頻時(shí)長(zhǎng)
/**
獲取視頻市場(chǎng)

@param mediaUrl 路徑
@return value
*/

  • (CGFloat)getMediaDurationWithMediaUrl:(NSURL *)mediaUrl;
    @end

m

import <AssetsLibrary/AssetsLibrary.h>

import <Photos/Photos.h>

define kCutVideoPath @"cutDoneVideo.mov" //剪切的視頻文件名

define kDubVideoPath @"dubVideoPath.mov" //剪切的視頻文件名

define kMergeVideoPath @"mergeVideoPath.mp4" //剪切的視頻文件名

define kMergeAudioPath @"mergeAudioPath.caf" //合成視頻時(shí)將音頻轉(zhuǎn)換成caf軌道

define kTempAssetVideo @"tempAssetVideo.mov" //相冊(cè)讀取之后沙盒路徑

//視頻配音錄音 洒琢、壓縮裁剪路徑

define kCachesVideoStorageEdit ({\

NSString * path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"VideoStorageEdit"];
if (![[NSFileManager defaultManager]fileExistsAtPath:path]) {
[[NSFileManager defaultManager]createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}
(path);
})

@implementation VideoManager

//MARK: 檢查保存視頻權(quán)限

  • (void)checkVideoPermissionCompletionHandler:(void (^)(BOOL granted))handler {
    PHAuthorizationStatus permission = [PHPhotoLibrary authorizationStatus];
    if (permission == PHAuthorizationStatusAuthorized) {
    if (handler) {
    handler(YES);
    }
    }else {
    [self resquestUserPermissionCompletionHandler:handler];
    }
    }
    //請(qǐng)求權(quán)限

  • (void)resquestUserPermissionCompletionHandler:(void (^)(BOOL granted))handler {
    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
    if (status == PHAuthorizationStatusAuthorized) {
    // 通過(guò)驗(yàn)證
    dispatch_async(dispatch_get_main_queue(), ^{
    if (handler) {
    handler(YES);
    }
    });
    }else {
    // 未通過(guò)驗(yàn)證
    handler(NO);
    }

    }];
    }
    //MARK: 剪輯視頻

  • (void)captureVideoWithVideoUrl:(NSURL )videoUrl timeRange:(TimeRange)videoRange completion:(void (^)(NSURL * outPutUrl,NSError * error))completionHandle{
    if (!videoUrl.path || [videoUrl.path isEqualToString:@""] || videoRange.length == NSNotFound) {
    return;
    }
    //AVURLAsset此類(lèi)主要用于獲取媒體信息象迎,包括視頻、聲音等
    AVURLAsset
    videoAsset = [[AVURLAsset alloc] initWithURL:videoUrl options:nil];
    NSArray tracks = videoAsset.tracks;
    NSLog(@"所有軌道:%@\n",tracks);//打印出所有的資源軌道
    //創(chuàng)建AVMutableComposition對(duì)象來(lái)添加視頻音頻資源的AVMutableCompositionTrack
    AVMutableComposition
    mixComposition = [AVMutableComposition composition];
    //CMTimeRangeMake(start, duration),start起始時(shí)間汪厨,duration時(shí)長(zhǎng),都是CMTime類(lèi)型
    //CMTimeMake(int64_t value, int32_t timescale)衷戈,返回CMTime,value視頻的一個(gè)總幀數(shù)谦趣,timescale是指每秒視頻播放的幀數(shù)蔚润,視頻播放速率,(value / timescale)才是視頻實(shí)際的秒數(shù)時(shí)長(zhǎng)除盏,timescale一般情況下不改變窃祝,截取視頻長(zhǎng)度通過(guò)改變value的值
    //CMTimeMakeWithSeconds(Float64 seconds, int32_t preferredTimeScale)粪小,返回CMTime,seconds截取時(shí)長(zhǎng)(單位秒)逞壁,preferredTimeScale每秒幀數(shù)
    //開(kāi)始位置startTime
    CMTime startTime = CMTimeMakeWithSeconds(videoRange.location, videoAsset.duration.timescale);
    //截取長(zhǎng)度videoDuration
    CMTime videoDuration = CMTimeMakeWithSeconds(videoRange.length, videoAsset.duration.timescale);

    CMTimeRange videoTimeRange = CMTimeRangeMake(startTime, videoDuration);

    //視頻采集compositionVideoTrack 添加視頻軌道
    AVVideoComposition * videoComposition = [self addVideoCompositionTrack:mixComposition timeRange:videoTimeRange assert:videoAsset];

    //視頻聲音采集(也可不執(zhí)行這段代碼不采集視頻音軌,合并后的視頻文件將沒(méi)有視頻原來(lái)的聲音)
    [self addAudioCompositionTrack:mixComposition timeRange:videoTimeRange assert:videoAsset];

    //AVAssetExportSession用于合并文件姿骏,導(dǎo)出合并后文件,presetName文件的輸出類(lèi)型
    NSString *outPutPath = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/cutoutput-%@.mp4", [self getNowTimeTimestamp2]];

    [self exportVideoSession:mixComposition outPath:outPutPath videoComposition:videoComposition completion:completionHandle];
    }
    //MARK: 去除視頻聲道

  • (void)deleteVideoAudio:(NSURL )videoUrl completion:(void (^)(NSURL * outPutUrl,NSError * error))completionHandle{
    if (!videoUrl.path || [videoUrl.path isEqualToString:@""]) {
    return;
    }
    //AVURLAsset此類(lèi)主要用于獲取媒體信息蟋恬,包括視頻歼争、聲音等
    AVURLAsset
    videoAsset = [[AVURLAsset alloc] initWithURL:videoUrl options:nil];
    //創(chuàng)建AVMutableComposition對(duì)象來(lái)添加視頻音頻資源的AVMutableCompositionTrack
    AVMutableComposition* mixComposition = [AVMutableComposition composition];

    //視頻采集compositionVideoTrack
    AVVideoComposition * videoComposition = [self addVideoCompositionTrack:mixComposition timeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) assert:videoAsset];

    //AVAssetExportSession用于合并文件俩莽,導(dǎo)出合并后文件,presetName文件的輸出類(lèi)型
    NSString *outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:kDubVideoPath];
    [self exportVideoSession:mixComposition outPath:outPutPath videoComposition:videoComposition completion:completionHandle];
    }
    //MARK: 合并配音文件 只保留背景音樂(lè)

  • (void)addBackgroundMiusicWithVideoUrlStr:(NSURL )videoUrl audioUrl:(NSURL )audioUrl completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle {
    AVURLAsset
    audioAsset = [[AVURLAsset alloc] initWithURL:audioUrl options:nil];
    AVURLAsset
    videoAsset = [[AVURLAsset alloc] initWithURL:videoUrl options:nil];
    //創(chuàng)建AVMutableComposition對(duì)象來(lái)添加視頻音頻資源的AVMutableCompositionTrack
    AVMutableComposition* mixComposition = [AVMutableComposition composition];
    //截取長(zhǎng)度videoDuration
    CMTimeRange videoTimeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration);
    //視頻采集compositionVideoTrack
    AVVideoComposition * videoComposition = [self addVideoCompositionTrack:mixComposition timeRange:videoTimeRange assert:videoAsset];

    //外部音頻采集璧疗,最后合成到原視頻崩侠,與原視頻的音頻不沖突
    //聲音長(zhǎng)度截取范圍==視頻長(zhǎng)度
    CMTimeRange audioTimeRange = videoTimeRange;
    if (audioUrl) {
    [self addAudioCompositionTrack:mixComposition timeRange:audioTimeRange assert:audioAsset];
    }
    NSString *outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:kMergeVideoPath];
    //導(dǎo)出視頻
    [self exportVideoSession:mixComposition outPath:outPutPath videoComposition:videoComposition completion:completionHandle];
    }

/**
合成配音視頻 MARK: 剪切視頻 合并配音文件 開(kāi)始時(shí)間

@param videoUrl 視頻
@param audioUrl 音樂(lè)
@param musicStart 音樂(lè)開(kāi)始時(shí)間
@param videoRange 視頻截取區(qū)間
@param videoVolume 視頻音量
@param musicVolume 音樂(lè)音量
@param completionHandle
*/

  • (void)addBackgroundMiusicWithVideoUrlStr:(NSURL *)videoUrl
    audioUrl:(NSURL *)audioUrl
    musicStartTime:(CGFloat)musicStart
    videoTimeRange:(TimeRange)videoRange
    videoVolume:(CGFloat)videoVolume
    musicVolume:(CGFloat)musicVolume
    completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle {

    AVURLAsset* videoAsset = [[AVURLAsset alloc] initWithURL:videoUrl options:nil];
    //創(chuàng)建AVMutableComposition對(duì)象來(lái)添加視頻音頻資源的AVMutableCompositionTrack
    AVMutableComposition* mixComposition = [AVMutableComposition composition];

    // 視頻 截取長(zhǎng)度videoDuration
    //開(kāi)始位置startTime
    CMTime videoStartTime = CMTimeMakeWithSeconds(videoRange.location, videoAsset.duration.timescale);
    //截取長(zhǎng)度videoDuration
    CMTime videoDuration = CMTimeMakeWithSeconds(videoRange.length, videoAsset.duration.timescale);
    //視頻截取區(qū)間
    CMTimeRange videoTimeRange = CMTimeRangeMake(videoStartTime, videoDuration);

    //視頻采集compositionVideoTrack
    AVVideoComposition * videoComposition = [self addVideoCompositionTrack:mixComposition timeRange:videoTimeRange assert:videoAsset];

    //外部音頻采集矢炼,最后合成到原視頻八拱,與原視頻的音頻不沖突
    if (audioUrl) {
    AVURLAsset* audioAsset = [[AVURLAsset alloc] initWithURL:audioUrl options:nil];

      CGFloat musicLength = CMTimeGetSeconds(audioAsset.duration);
      //音樂(lè)長(zhǎng)度不超過(guò)視頻長(zhǎng)度
      CGFloat musicCutLength = musicLength - musicStart > videoRange.length ? videoRange.length : musicLength -musicStart;
      TimeRange musicRange = {musicStart,musicCutLength};
      
      //開(kāi)始位置startTime
      CMTime musicStartTime = CMTimeMakeWithSeconds(musicRange.location, audioAsset.duration.timescale);
      //截取長(zhǎng)度videoDuration
      CMTime musicDuration = CMTimeMakeWithSeconds(musicRange.length, audioAsset.duration.timescale);
      //音樂(lè)截取區(qū)間
      CMTimeRange musicTimeRange = CMTimeRangeMake(musicStartTime, musicDuration);
      
     NSArray  *videoAudioMixToolsAry = [self mergeVideoAndAudioWithVIdeoAsset:videoAsset musicAssert:audioAsset mixComposition:mixComposition videoVolume:videoVolume musicVolume:musicVolume videoRange:videoTimeRange musicRange:musicTimeRange ];
      NSString *outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:kMergeVideoPath];
      //導(dǎo)出視頻
      [self exportVideoSession:mixComposition outPath:outPutPath videoComposition:videoComposition videoAudioMixToolsAry:videoAudioMixToolsAry completion:completionHandle];
    

    } else {
    //視頻聲音采集(也可不執(zhí)行這段代碼不采集視頻音軌,合并后的視頻文件將沒(méi)有視頻原來(lái)的聲音)
    [self addAudioCompositionTrack:mixComposition timeRange:videoTimeRange assert:videoAsset];
    NSString *outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:kMergeVideoPath];
    //導(dǎo)出視頻
    [self exportVideoSession:mixComposition outPath:outPutPath videoComposition:videoComposition completion:completionHandle];

    }

}

//MARK: 添加視頻 軌道
/**
添加視頻 軌道

@param mixComposition 合成器
@param asset 源
@param timeRange 視頻返回
@return 視頻合成方案
*/

  • (AVVideoComposition *)addVideoCompositionTrack:(AVMutableComposition *)mixComposition timeRange:(CMTimeRange)timeRange assert:(AVURLAsset *)asset{
    //音頻采集compositionCommentaryTrack
    //TimeRange截取的范圍長(zhǎng)度
    //ofTrack來(lái)源
    //atTime插放在視頻的時(shí)間位置
    NSError * error = nil;
    //音頻混合軌道
    AVMutableCompositionTrack *compositionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    //插入視頻軌道
    // 1.
    [compositionTrack insertTimeRange:timeRange ofTrack:([asset tracksWithMediaType:AVMediaTypeVideo].count > 0) ? [asset tracksWithMediaType:AVMediaTypeVideo].firstObject : nil atTime:kCMTimeZero error:&error];
    //下面3行代碼用于保證后面輸出的視頻方向跟原視頻方向一致
    AVAssetTrack *assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo]firstObject];
    [compositionTrack setPreferredTransform:assetVideoTrack.preferredTransform];
    NSLog(@"幀率:%f榛搔,比特率:%f", assetVideoTrack.nominalFrameRate,assetVideoTrack.estimatedDataRate);
    // 2.
    AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionTrack];
    [videolayerInstruction setOpacity:0.0 atTime:compositionTrack.asset.duration];
    // 3.
    AVMutableVideoCompositionInstruction *videoCompositionInstrution = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    videoCompositionInstrution.timeRange = CMTimeRangeMake(kCMTimeZero, compositionTrack.asset.duration);
    videoCompositionInstrution.layerInstructions = @[videolayerInstruction];
    // 4.
    AVMutableVideoComposition videoComposition = [AVMutableVideoComposition videoComposition];
    videoComposition.renderSize = CGSizeMake(compositionTrack.naturalSize.width, compositionTrack.naturalSize.height);//視頻寬高,必須設(shè)置践惑,否則會(huì)奔潰
    /

    電影:24
    PAL(帕爾制腹泌,電視廣播制式)和SEACM():25
    NTSC(美國(guó)電視標(biāo)準(zhǔn)委員會(huì)):29.97
    Web/CD-ROM:15
    其他視頻類(lèi)型,非丟幀視頻尔觉,E-D動(dòng)畫(huà) 30
    */
    // videoComposition.frameDuration = CMTimeMake(1, 43);//必須設(shè)置凉袱,否則會(huì)奔潰侦铜,一般30就夠了
    videoComposition.frameDuration = CMTimeMake(1, 30);//必須設(shè)置专甩,否則會(huì)奔潰,一般30就夠了
    // videoComposition.renderScale
    videoComposition.instructions = [NSArray arrayWithObject:videoCompositionInstrution];
    NSLog(@"error---%@",error);

    return videoComposition;
    }
    //MARK: 添加聲音 軌道 只保留單個(gè)視頻聲音
    /**
    添加聲音 軌道钉稍,

@param mixComposition 合成器
@param asset 源
@param timeRange 視頻返回
*/

  • (void)addAudioCompositionTrack:(AVMutableComposition *)mixComposition timeRange:(CMTimeRange)timeRange assert:(AVURLAsset *)asset{
    //音頻采集compositionCommentaryTrack
    //TimeRange截取的范圍長(zhǎng)度
    //ofTrack來(lái)源
    //atTime插放在視頻的時(shí)間位置
    NSError * error = nil;
    //這里采用信號(hào)量加鎖涤躲,是聲音轉(zhuǎn)換完成之后繼續(xù)進(jìn)行合成視頻
    dispatch_semaphore_t t = dispatch_semaphore_create(0);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self changeAudioTypeToCAFAsset:asset Complete:^(AVURLAsset *newAudioAsset, NSError error) {
    AVAssetTrack audioAssetTrack = [[newAudioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
    AVMutableCompositionTrack * audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    // 加入合成軌道之中
    [audioTrack insertTimeRange:timeRange ofTrack:audioAssetTrack atTime:CMTimeMakeWithSeconds(0, asset.duration.timescale) error:nil];
    dispatch_semaphore_signal(t);
    }];
    });
    dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);
    NSLog(@"error---%@",error);
    }
    //MARK: 轉(zhuǎn)換聲音頻道
    /

    聲音軌道轉(zhuǎn)換成caf
    這里值得注意的是:因?yàn)閕OS對(duì)MP4視頻要求高,如果需要合成MP4視頻聲音軌道必須使用aac格式的贡未,所以這里視頻采用mov格式种樱,聲音頻道使用caf格式的蒙袍,內(nèi)部包含將視頻源中間的聲道轉(zhuǎn)換成caf格式的

@param asset 源
@param handle (新輸出的聲音路徑,錯(cuò)誤信息)
*/

  • (void)changeAudioTypeToCAFAsset:(AVURLAsset *)asset Complete:(void(^)(AVURLAsset * newAudioAsset,NSError * error))handle {
    AVMutableComposition *composition = [AVMutableComposition composition];

    AVAssetTrack *audioAssetTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];

    AVMutableCompositionTrack * audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    // 加入合成軌道之中
    CMTimeRange audioTimeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
    //插入音頻
    [audioTrack insertTimeRange:audioTimeRange ofTrack:audioAssetTrack atTime:CMTimeMakeWithSeconds(0, asset.duration.timescale) error:nil];

    //合成器
    AVAssetExportSession *exportSesstion = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];

    //輸出路徑
    NSString * path = [kCachesVideoStorageEdit stringByAppendingPathComponent:kMergeAudioPath];
    if ([[NSFileManager defaultManager]fileExistsAtPath:path]) {
    [[NSFileManager defaultManager]removeItemAtPath:path error:nil];
    }
    exportSesstion.outputURL = [NSURL fileURLWithPath:path];
    exportSesstion.outputFileType = AVFileTypeCoreAudioFormat;
    exportSesstion.shouldOptimizeForNetworkUse = YES;
    [exportSesstion exportAsynchronouslyWithCompletionHandler:^{
    AVAssetExportSessionStatus status = exportSesstion.status;
    if (status == AVAssetExportSessionStatusCompleted) {
    NSLog(@"聲音導(dǎo)出成功");
    AVURLAsset * newAudioAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:path] options:nil];
    if (handle) {
    handle(newAudioAsset,nil);
    }
    }else{
    NSLog(@"聲音導(dǎo)出失敗%@",exportSesstion.error);
    if (handle) {
    handle(nil,exportSesstion.error);
    }
    }
    }];
    }

/**
混音

@param videoAsset 第一段
@param musicAssert 第二段
@param mixComposition 原視頻容器
@param videoVolume 第一段音量
@param musicVolume 第二段音量
@return 混合聲音對(duì)象
*/

  • (NSArray *)mergeVideoAndAudioWithVIdeoAsset:(AVURLAsset *)videoAsset musicAssert:(AVURLAsset *)musicAssert mixComposition:(AVMutableComposition *)composition videoVolume:(float)videoVolume musicVolume:(float)musicVolume videoRange:(CMTimeRange)videoRange musicRange:(CMTimeRange)musicRange {

    videoVolume = videoVolume >0 ? videoVolume : 1;//聲音默認(rèn)1,
    musicVolume = musicVolume >0 ? musicVolume : 1;//聲音默認(rèn)1,

    //修改背景音樂(lè)的音量start 更改音量大小
    NSMutableArray * params = [[NSMutableArray alloc] initWithCapacity:0];

//    第一段聲音處理
if (videoAsset) {
    AVAssetTrack *audioAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
    
    AVMutableCompositionTrack * audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    // 加入合成軌道之中

// CMTimeRange audioTimeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration);
//插入音頻
// atTime:kCMTimeInvalid 混音 音頻1和音頻2 同時(shí)播放
// atTime:firstAudioAsset.duration 拼接 音頻2的播放在音頻1結(jié)束以后開(kāi)始
[audioTrack insertTimeRange:videoRange ofTrack:audioAssetTrack atTime:kCMTimeZero error:nil];

    //調(diào)節(jié)音量
    //獲取音頻軌道
    AVMutableAudioMixInputParameters *firstAudioParam = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:audioAssetTrack];
    //設(shè)置音軌音量,可以設(shè)置漸變,設(shè)置為1.0就是全音量 setVolumeRampFromStartVolume:0.1 toEndVolume:1.0
    [firstAudioParam setVolumeRampFromStartVolume:videoVolume toEndVolume:videoVolume timeRange:videoRange];
    [firstAudioParam setTrackID:audioAssetTrack.trackID];
    [params addObject:firstAudioParam];
}
//    第二段聲音處理
if (musicAssert) {
    AVAssetTrack *musicAssetTrack = [[musicAssert tracksWithMediaType:AVMediaTypeAudio] firstObject];
    
    AVMutableCompositionTrack * musicTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    // 加入合成軌道之中

// CMTimeRange musicTimeRange = CMTimeRangeMake(kCMTimeZero, musicAssert.duration);
//插入音頻
// atTime:kCMTimeInvalid 混音 音頻1和音頻2 同時(shí)播放
// atTime:firstAudioAsset.duration 拼接 音頻2的播放在音頻1結(jié)束以后開(kāi)始
[musicTrack insertTimeRange:musicRange ofTrack:musicAssetTrack atTime:kCMTimeZero error:nil];

    //調(diào)節(jié)音量
    //獲取音頻軌道
    AVMutableAudioMixInputParameters *secondAudioParam = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:musicAssetTrack];
    //設(shè)置音軌音量,可以設(shè)置漸變,設(shè)置為1.0就是全音量 setVolumeRampFromStartVolume:0.1 toEndVolume:1.0
    [secondAudioParam setVolumeRampFromStartVolume:musicVolume toEndVolume:musicVolume timeRange:musicRange];
    [secondAudioParam setTrackID:musicAssetTrack.trackID];
    [params addObject:secondAudioParam];
}

return params;

}
//MARK: 導(dǎo)出合成視頻
/**
導(dǎo)出合成視頻

@param mixComposition 合成器
@param outPutPath 輸出路徑
@param videoComposition 設(shè)置導(dǎo)出視頻的處理方案
@param completionHandle 完成
*/

  • (void)exportVideoSession:(AVMutableComposition *)mixComposition outPath:(NSString *)outPutPath videoComposition:(AVVideoComposition *)videoComposition completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle {

    //AVAssetExportSession用于合并文件缸托,導(dǎo)出合并后文件左敌,presetName文件的輸出類(lèi)型
    AVAssetExportSession *assetExportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetPassthrough];

    //混合后的視頻輸出路徑
    NSURL *outPutUrl = [NSURL fileURLWithPath:outPutPath];

    if ([[NSFileManager defaultManager] fileExistsAtPath:outPutPath]){
    [[NSFileManager defaultManager] removeItemAtPath:outPutPath error:nil];
    }
    //輸出視頻格式 AVFileTypeMPEG4 AVFileTypeQuickTimeMovie...
    assetExportSession.outputFileType = AVFileTypeQuickTimeMovie;
    // NSArray *fileTypes = assetExportSession.
    assetExportSession.outputURL = outPutUrl;
    //輸出文件是否網(wǎng)絡(luò)優(yōu)化
    assetExportSession.shouldOptimizeForNetworkUse = YES;
    //設(shè)置導(dǎo)出視頻的處理方案
    if (videoComposition) {
    assetExportSession.videoComposition = videoComposition;
    }

    [assetExportSession exportAsynchronouslyWithCompletionHandler:^{
    dispatch_async(dispatch_get_main_queue(), ^{
    if (assetExportSession.status == AVAssetExportSessionStatusCompleted) {
    NSLog(@"轉(zhuǎn)碼完成");
    }else {
    NSLog(@"%@",assetExportSession.error);
    }
    completionHandle(outPutUrl,assetExportSession.error);
    });
    }];
    }

  • (void)exportVideoSession:(AVMutableComposition *)mixComposition outPath:(NSString *)outPutPath videoComposition:(AVVideoComposition *)videoComposition videoAudioMixToolsAry:(NSArray *)videoAudioMixToolsAry completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle {

    //AVAssetExportSession用于合并文件,導(dǎo)出合并后文件俐镐,presetName文件的輸出類(lèi)型
    AVAssetExportSession *assetExportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetPassthrough];

    //混合后的視頻輸出路徑
    NSURL *outPutUrl = [NSURL fileURLWithPath:outPutPath];

    if ([[NSFileManager defaultManager] fileExistsAtPath:outPutPath]){
    [[NSFileManager defaultManager] removeItemAtPath:outPutPath error:nil];
    }
    //輸出視頻格式 AVFileTypeMPEG4 AVFileTypeQuickTimeMovie...
    assetExportSession.outputFileType = AVFileTypeQuickTimeMovie;
    // NSArray *fileTypes = assetExportSession.
    assetExportSession.outputURL = outPutUrl;
    //輸出文件是否網(wǎng)絡(luò)優(yōu)化
    assetExportSession.shouldOptimizeForNetworkUse = YES;
    // 音視頻軌道
    if (videoAudioMixToolsAry.count > 0 ) {
    AVMutableAudioMix *videoAudioMixTools = [AVMutableAudioMix audioMix];
    videoAudioMixTools.inputParameters = [NSArray arrayWithArray:videoAudioMixToolsAry];
    assetExportSession.audioMix = videoAudioMixTools;
    }
    //設(shè)置導(dǎo)出視頻的處理方案
    if (videoComposition) {
    assetExportSession.videoComposition = videoComposition;
    }

    [assetExportSession exportAsynchronouslyWithCompletionHandler:^{
    dispatch_async(dispatch_get_main_queue(), ^{
    if (assetExportSession.status == AVAssetExportSessionStatusCompleted) {
    NSLog(@"轉(zhuǎn)碼完成");
    }else {
    NSLog(@"%@",assetExportSession.error);
    }
    completionHandle(outPutUrl,assetExportSession.error);
    });
    }];
    }
    //MARK: 獲取視頻時(shí)長(zhǎng)
    /**
    獲取視頻市場(chǎng)

@param mediaUrl 路徑
@return value
*/

  • (CGFloat)getMediaDurationWithMediaUrl:(NSURL *)mediaUrl {

    AVURLAsset *mediaAsset = [[AVURLAsset alloc] initWithURL:mediaUrl options:nil];
    CMTime duration = mediaAsset.duration;

    return duration.value / duration.timescale;
    }

  • (NSUInteger)degressFromVideoFileWithURL:(NSURL *)url {
    NSUInteger degress = 0;

    AVAsset *asset = [AVAsset assetWithURL:url];
    NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
    if([tracks count] > 0) {
    AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
    CGAffineTransform t = videoTrack.preferredTransform;

      if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
          // Portrait
          degress = 90;
      }else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
          // PortraitUpsideDown
          degress = 270;
      }else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
          // LandscapeRight
          degress = 0;
      }else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
          // LandscapeLeft
          degress = 180;
      }
    

    }
    return degress;
    }
    //MARK: 獲取視頻縮略圖
    /**
    獲取視頻縮略圖

@param url 視頻路徑
@return 圖片
*/

  • (CGImageRef)thumbnailImageRequestWithURL:(NSURL *)url
    {
    //根據(jù)url創(chuàng)建AVURLAsset
    AVURLAsset *urlAsset=[AVURLAsset assetWithURL:url];

    //根據(jù)AVURLAsset創(chuàng)建AVAssetImageGenerator
    AVAssetImageGenerator imageGenerator=[AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];
    imageGenerator.appliesPreferredTrackTransform = YES;
    /
    截圖

    • requestTime:縮略圖創(chuàng)建時(shí)間
    • actualTime:縮略圖實(shí)際生成的時(shí)間
      /
      NSError error=nil;
      CMTime time = CMTimeMake(0, 10);
      // CMTime time=CMTimeMakeWithSeconds(0, 10);//CMTime是表示視頻時(shí)間信息的結(jié)構(gòu)體矫限,第一個(gè)參數(shù)表示是視頻第幾秒,第二個(gè)參數(shù)表示每秒幀數(shù).(如果要獲取的某一秒的第幾幀可以使用CMTimeMake方法)
      CMTime actualTime;
      CGImageRef cgImage= [imageGenerator copyCGImageAtTime:time actualTime:&actualTime error:&error];
      if(error){
      NSLog(@"截取視頻縮略圖時(shí)發(fā)生錯(cuò)誤佩抹,錯(cuò)誤信息:%@",error.localizedDescription);
      }
      CMTimeShow(actualTime);
      return cgImage;
      }
      //MARK: 轉(zhuǎn)換視頻格式叼风,導(dǎo)出視頻
      /

      轉(zhuǎn)換視頻格式,導(dǎo)出視頻

@param inputURL 輸入源
@param outputURL 輸出源
@param handler 回調(diào)
*/

  • (void)exportSessionVideoWithInputURL:(NSURL)inputURL completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle{
    if (!inputURL.path || [inputURL.path isEqualToString:@""] || inputURL.path.length == NSNotFound) {
    return;
    }
    //AVURLAsset此類(lèi)主要用于獲取媒體信息棍苹,包括視頻无宿、聲音等
    AVURLAsset
    videoAsset = [[AVURLAsset alloc] initWithURL:inputURL options:nil];
    NSArray tracks = videoAsset.tracks;
    NSLog(@"所有軌道:%@\n",tracks);//打印出所有的資源軌道
    //創(chuàng)建AVMutableComposition對(duì)象來(lái)添加視頻音頻資源的AVMutableCompositionTrack
    AVMutableComposition
    mixComposition = [AVMutableComposition composition];
    //視頻采集compositionVideoTrack 添加視頻軌道
    AVVideoComposition * videoComposition = [self addVideoCompositionTrack:mixComposition timeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) assert:videoAsset];
    //視頻聲音采集(也可不執(zhí)行這段代碼不采集視頻音軌,合并后的視頻文件將沒(méi)有視頻原來(lái)的聲音)
    [self addAudioCompositionTrack:mixComposition timeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) assert:videoAsset];
    //AVAssetExportSession用于合并文件枢里,導(dǎo)出合并后文件孽鸡,presetName文件的輸出類(lèi)型
    NSString *outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:@"convertVideo.mov"];
    [self exportVideoSession:mixComposition outPath:outPutPath videoComposition:videoComposition completion:completionHandle];

}

  • (void)RreplaceVideoWithTimeLeft:(float)leftTime rightTime:(float)rightTime InputURL:(NSURL*)inputURL completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle {

}
//MARK: 轉(zhuǎn)換視頻格式,導(dǎo)出視頻
/**
轉(zhuǎn)換視頻格式栏豺,導(dǎo)出視頻

@param inputURL 輸入源
@param outputURL 輸出源
@param handler 回調(diào)
*/

  • (void)exportSessionVideoWithInputURL:(NSURL)inputURL completion:(void(^)(NSURL * outPutUrl,NSError * error))completionHandle fiter:(CIFilter)filter {
    if (!inputURL.path || [inputURL.path isEqualToString:@""] || inputURL.path.length == NSNotFound) {
    return;
    }
    //AVURLAsset此類(lèi)主要用于獲取媒體信息彬碱,包括視頻、聲音等
    AVURLAsset* videoAsset = [[AVURLAsset alloc] initWithURL:inputURL options:nil];
    NSArray tracks = videoAsset.tracks;
    NSLog(@"所有軌道:%@\n",tracks);//打印出所有的資源軌道
    //創(chuàng)建AVMutableComposition對(duì)象來(lái)添加視頻音頻資源的AVMutableCompositionTrack
    // AVMutableComposition
    mixComposition = [AVMutableComposition composition];
    //視頻采集compositionVideoTrack 添加視頻軌道
    // AVMutableComposition * videoComposition = [self filterAVVideoCompositionInputSssset:videoAsset];
    //AVAssetExportSession用于合并文件奥洼,導(dǎo)出合并后文件巷疼,presetName文件的輸出類(lèi)型

    NSString *outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:@"convertVideo.mov"];
    // [self exportVideoSession:mixComposition outPath:outPutPath videoComposition:videoComposition completion:completionHandle];

// CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"];
AVVideoComposition *composition = [AVVideoComposition videoCompositionWithAsset:videoAsset applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest * _Nonnull request) {
CIImage *source = request.sourceImage.imageByClampingToExtent;
int currentTime = request.compositionTime.value / request.compositionTime.timescale;
if (currentTime <1 ) {
[request finishWithImage:source context:nil];
} else {
[filter setValue:source forKey:kCIInputImageKey];

        CIImage *output = [filter.outputImage imageByCroppingToRect:request.sourceImage.extent];
        [request finishWithImage:output context:nil];
    }
}];

//AVAssetExportSession用于合并文件,導(dǎo)出合并后文件灵奖,presetName文件的輸出類(lèi)型
AVAssetExportSession *assetExportSession = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetPassthrough];

//混合后的視頻輸出路徑
NSURL *outPutUrl = [NSURL fileURLWithPath:outPutPath];

if ([[NSFileManager defaultManager] fileExistsAtPath:outPutPath]){
    [[NSFileManager defaultManager] removeItemAtPath:outPutPath error:nil];
}
//輸出視頻格式 AVFileTypeMPEG4 AVFileTypeQuickTimeMovie...
assetExportSession.outputFileType = AVFileTypeQuickTimeMovie;
//    NSArray *fileTypes = assetExportSession.
assetExportSession.outputURL = outPutUrl;
//輸出文件是否網(wǎng)絡(luò)優(yōu)化
assetExportSession.shouldOptimizeForNetworkUse = YES;
//設(shè)置導(dǎo)出視頻的處理方案
if (composition) {
    assetExportSession.videoComposition = composition;
}

[assetExportSession exportAsynchronouslyWithCompletionHandler:^{

// dispatch_async(dispatch_get_main_queue(), ^{
if (assetExportSession.status == AVAssetExportSessionStatusCompleted) {
NSLog(@"轉(zhuǎn)碼完成");
completionHandle(outPutUrl,nil);

        }else {
            NSLog(@"%@",assetExportSession.error);
        }

// });
}];

}

  • (AVMutableComposition )filterAVVideoCompositionInputSssset:(AVURLAsset)videoAsset {

    CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"];
    AVVideoComposition *composition = [AVVideoComposition videoCompositionWithAsset:videoAsset applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest * _Nonnull request) {
    CIImage *source = request.sourceImage.imageByClampingToExtent;
    int currentTime = request.compositionTime.value / request.compositionTime.timescale;
    if (currentTime <1 ) {
    [request finishWithImage:source context:nil];
    } else {
    [filter setValue:source forKey:kCIInputImageKey];

          CIImage *output = [filter.outputImage imageByCroppingToRect:request.sourceImage.extent];
          [request finishWithImage:output context:nil];
      }
    

    }];

    return composition;
    }

//MARK: 相冊(cè)讀取之后照片路徑嚼沿,內(nèi)部會(huì)將相冊(cè)內(nèi)部的視頻緩存至沙盒路徑
/**
相冊(cè)讀取之后照片路徑,內(nèi)部會(huì)將相冊(cè)內(nèi)部的視頻緩存至沙盒路徑

@param info imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
@param result 緩存路徑
*/

  • (void)getVideoLocalPath:(NSDictionary<NSString *,id> *)info result:(void(^)(NSURL * videoURL))result {
    NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL]; // video path
    if (videoURL) {
    //處于沙盒下的 直接返回就可以了
    if (result) {
    result(videoURL);
    }
    } else {
    //先判斷權(quán)限
    [self checkVideoPermissionCompletionHandler:^(BOOL granted) {
    if (granted) {
    //相冊(cè)內(nèi)的視頻瓷患,需要先緩存到沙盒下面才能使用
    NSURL *imageURL = [info valueForKey:UIImagePickerControllerReferenceURL];
    PHFetchResult *fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[imageURL] options:nil];
    PHAsset *asset = fetchResult.firstObject;
    NSArray *assetResources = [PHAssetResource assetResourcesForAsset:asset];
    PHAssetResource resource;
    for (PHAssetResource assetRes in assetResources) {
    if (assetRes.type == PHAssetResourceTypePairedVideo ||
    assetRes.type == PHAssetResourceTypeVideo) {
    resource = assetRes;
    }
    }
    if (asset.mediaType == PHAssetMediaTypeVideo || asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) {
    PHVideoRequestOptions options = [[PHVideoRequestOptions alloc] init];
    options.version = PHImageRequestOptionsVersionCurrent;
    options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
    //#warning
    視頻路徑一定要放置cache路徑下面骡尽,不可以放在Temp路徑下,否則導(dǎo)出的時(shí)候會(huì)有問(wèn)題擅编。****
    NSString * PATH_MOVIE_FILE = [kCachesVideoStorageEdit stringByAppendingPathComponent:kTempAssetVideo];
    if ([[NSFileManager defaultManager]fileExistsAtPath:PATH_MOVIE_FILE]) {
    [[NSFileManager defaultManager]removeItemAtPath:PATH_MOVIE_FILE error:nil];
    }
    [[PHAssetResourceManager defaultManager] writeDataForAssetResource:resource
    toFile:[NSURL fileURLWithPath:PATH_MOVIE_FILE]
    options:nil
    completionHandler:^(NSError * _Nullable error) {
    if (error) {
    [JWHudLoadView alertMessage:error.domain];
    } else {
    NSLog(@"=====filePath:%@",PATH_MOVIE_FILE);
    if (result) {
    result([NSURL fileURLWithPath:PATH_MOVIE_FILE]);
    }
    }
    }];
    } else {
    [JWHudLoadView alertMessage:@"暫不支持該視頻爆阶,請(qǐng)選擇其他視頻!"];
    }
    }else {
    [JWHudLoadView alertMessage:@"需要您同意相冊(cè)權(quán)限"];
    }
    }];
    }
    }

//MARK: 清楚剪輯視頻緩存
/**
清楚剪輯視頻緩存
*/

  • (void)clearCutVideoCache {
    NSLog(@"-------清除上傳視頻資源");
    NSString *outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:kCutVideoPath];
    if ([[NSFileManager defaultManager] fileExistsAtPath:outPutPath]) {
    [[NSFileManager defaultManager] removeItemAtPath:outPutPath error:nil];
    }
    outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:kDubVideoPath];
    if ([[NSFileManager defaultManager] fileExistsAtPath:outPutPath]) {
    [[NSFileManager defaultManager] removeItemAtPath:outPutPath error:nil];
    }
    outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:kMergeVideoPath];
    if ([[NSFileManager defaultManager] fileExistsAtPath:outPutPath]) {
    [[NSFileManager defaultManager] removeItemAtPath:outPutPath error:nil];
    }

    outPutPath = [kCachesVideoStorageEdit stringByAppendingPathComponent:kMergeAudioPath];
    if ([[NSFileManager defaultManager] fileExistsAtPath:outPutPath]) {
    [[NSFileManager defaultManager] removeItemAtPath:outPutPath error:nil];
    }
    }

+(NSString )getNowTimeTimestamp2{
NSDate
dat = [NSDate dateWithTimeIntervalSinceNow:0];
NSTimeInterval a=[dat timeIntervalSince1970];
NSString*timeString = [NSString stringWithFormat:@"%0.f", a];//轉(zhuǎn)為字符型
return timeString;
}

@end

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沙咏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌班套,老刑警劉巖肢藐,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吱韭,居然都是意外死亡吆豹,警方通過(guò)查閱死者的電腦和手機(jī)鱼的,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)痘煤,“玉大人凑阶,你說(shuō)我怎么就攤上這事≈钥欤” “怎么了宙橱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蘸拔。 經(jīng)常有香客問(wèn)我师郑,道長(zhǎng),這世上最難降的妖魔是什么调窍? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任宝冕,我火速辦了婚禮,結(jié)果婚禮上邓萨,老公的妹妹穿的比我還像新娘地梨。我一直安慰自己,他們只是感情好缔恳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布宝剖。 她就那樣靜靜地躺著,像睡著了一般褐耳。 火紅的嫁衣襯著肌膚如雪诈闺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天铃芦,我揣著相機(jī)與錄音雅镊,去河邊找鬼。 笑死刃滓,一個(gè)胖子當(dāng)著我的面吹牛仁烹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咧虎,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼卓缰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了砰诵?” 一聲冷哼從身側(cè)響起征唬,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茁彭,沒(méi)想到半個(gè)月后总寒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡理肺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年摄闸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了善镰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡年枕,死狀恐怖炫欺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情熏兄,我是刑警寧澤品洛,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站霍弹,受9級(jí)特大地震影響毫别,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜典格,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一岛宦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耍缴,春花似錦砾肺、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蚁趁,卻和暖如春裙盾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背他嫡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工番官, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钢属。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓徘熔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親淆党。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酷师,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容