《AVFoundation》官方文档 05--Editing

Editing(编辑)

AVFoundation框架提供了一组功能丰富的类,以便于编辑视听资料。AVFoundation的编辑API的核心是组合。组合仅仅是来自一个或多个不同媒体asset的曲目的集合。的AVMutableComposition类提供用于插入和移除轨道,以及管理他们的时间排序的接口。图3-1显示了新组合如何从现有asset的组合中拼接成一个新的asset。如果您想要做的是将多个asset合并到一个文件中,那就是您需要的细节。如果您想在作曲中的轨道上执行任何自定义音频或视频处理,则需要分别包含音频混合或视频构图。

图3-1 AVMutableComposition将Asset组合在一起

使用AVMutableAudioMix该类,您可以对构图中的音轨执行自定义音频处理,如图3-2所示。目前,您可以为音轨指定最大音量或设置音量斜坡。

图3-2 AVMutableAudioMix执行音频混合

您可以使用AVMutableVideoComposition课程直接与组合中的视频轨道一起工作,以进行编辑,如图3-3所示。使用单个视频构图,您可以为输出视频指定所需的渲染大小和缩放以及帧持续时间。通过视频作品的指示(由AVMutableVideoCompositionInstruction类表示),您可以修改视频的背景颜色并应用图层说明。这些层指令(由AVMutableVideoCompositionLayerInstruction类表示)可用于将转换,变换斜坡,不透明度和不透明度斜坡应用于组合中的视频轨道。视频构图类还使您能够使用该animationTool属性将核心动画框架的效果引入到您的视频中。
图3-3 AVMutableVideoComposition

要将您的作品与音频混合和视频合成相结合,可以使用AVAssetExportSession对象,如图3-4所示。您可以使用您的作品初始化导出会话,然后分别将音频混合和视频组合分配给该属性audioMixvideoComposition属性。

图3-4 使用AVAssetExportSession将媒体元素组合到输出文件中

Creating a Composition

要创建自己的构图,您可以使用AVMutableComposition该类。要将媒体数据添加到组合中,您必须添加一个或多个由AVMutableCompositionTrack类表示的组合曲目。最简单的情况是创建一个具有一个视频轨道和一个音轨的可变组合:

AVMutableComposition * mutableComposition = [AVMutableComposition composition];

//创建视频构图轨道。

AVMutableCompositionTrack * mutableCompositionVideoTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

//创建音频合成音轨。

AVMutableCompositionTrack * mutableCompositionAudioTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

Options for Initializing a Composition Track

添加新曲目时,您必须同时提供媒体类型和曲目ID。虽然音频和视频是最常用的媒体类型,但您也可以指定其他媒体类型,例如AVMediaTypeSubtitleAVMediaTypeText

与某些视听数据相关联的每个轨道具有称为轨迹ID的唯一标识符。如果您指定kCMPersistentTrackID_Invalid为首选轨道ID,则会为您自动生成并与轨道相关联的唯一标识符。

Adding Audiovisual Data to a Composition(将视听数据添加到组合)

一旦您有一个或多个曲目的作品,您可以开始将媒体数据添加到相应的曲目。要将媒体数据添加到合成曲目,您需要访问AVAsset媒体数据所在的对象。您可以使用可变成分跟踪界面将同一底层媒体类型的多个轨道放在同一轨道上。以下示例说明如何依次添加两个不同的视频资源轨道到同一个组合轨道:

// You can retrieve AVAssets from a number of places, like the camera roll for example.

AVAsset *videoAsset = <#AVAsset with at least one video track#>;

AVAsset *anotherVideoAsset = <#another AVAsset with at least one video track#>;

// Get the first video track from each asset.

AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

AVAssetTrack *anotherVideoAssetTrack = [[anotherVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

// Add them both to the composition.

[mutableCompositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,videoAssetTrack.timeRange.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:nil];

[mutableCompositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,anotherVideoAssetTrack.timeRange.duration) ofTrack:anotherVideoAssetTrack atTime:videoAssetTrack.timeRange.duration error:nil];

Retrieving Compatible Composition Tracks(检索兼容的构图轨迹)

在可能的情况下,每个媒体类型应该只有一个组合轨。兼容的asset轨道的统一导致最少的资源使用量。当连续呈现媒体数据时,您应该将相同类型的任何媒体数据放在同一作曲轨道上。您可以查询可变组合来确定是否有与所需asset轨道兼容的组合曲目:

AVMutableCompositionTrack * compatibleCompositionTrack = [mutableComposition mutableTrackCompatibleWithTrack:<#the AVAssetTrack you want to insert#>];
if(compatibleCompositionTrack){
//执行继续。
}
注意 将多个视频片段放置在同一作曲轨道上可能会导致在视频片段(特别是嵌入式设备)之间的转换时放弃播放帧。为视频片段选择合成曲目的数量完全取决于您的应用程序的设计及其预期平台。

Generating a Volume Ramp(生成音量斜坡)

单个AVMutableAudioMix对象可以单独对组合中的所有音轨进行自定义音频处理。您可以使用audioMix类方法创建音频混合,并使用AVMutableAudioMixInputParameters该类的实例将音频混合与组合中的特定曲目相关联。可以使用音频混合来改变音轨的音量。以下示例显示如何在特定音轨上设置音量斜坡,以在组合的持续时间内缓慢淡出音频:

AVMutableAudioMix * mutableAudioMix = [AVMutableAudioMix audioMix];

//创建音频混合输入参数对象。

音频混合输入参数

//设置音量斜坡在组合的持续时间内慢慢淡出音频输出。

[mixParameters setVolumeRampFromStartVolume:1.f toEndVolume:0.f timeRange:CMTimeRangeMake(kCMTimeZero,mutableComposition.duration)];

//将输入参数附加到音频混合。

mutableAudioMix.inputParameters = @ [mixParameters];

Performing Custom Video Processing(执行自定义视频处理)

与音频组合一样,您只需要一个AVMutableVideoComposition
对象来对您的作品的视频轨道执行所有自定义视频处理。使用视频构图,您可以直接设置合成视频轨道的相应渲染大小,缩放比例和帧速率。有关为这些属性设置适当值的详细示例,请参阅设置渲染大小和帧持续时间

Changing the Composition’s Background Color(改变作品的背景颜色)

所有视频构图也必须AVVideoCompositionInstruction包含至少包含一个视频构图指令的对象数组。您可以使用AVMutableVideoCompositionInstruction该类创建自己的视频构图说明。使用视频合成指令,您可以修改组合的背景颜色,指定是否需要后处理或应用图层指令。
以下示例说明如何创建将整个构图的背景颜色更改为红色的视频合成指令

AVMutableVideoCompositionInstruction * mutableVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

mutableVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,mutableComposition.duration);

mutableVideoCompositionInstruction.backgroundColor = [[UIColor redColor] CGColor];

Applying Opacity Ramps(应用不透明度斜坡)

视频合成指令也可用于应用视频合成图层指令。一个AVMutableVideoCompositionLayerInstruction对象可以应用变换,组合物内的变换坡道,不透明度和不透明度坡道到一定视频道。在视频合成指令的layerInstructions阵列中的层指令的顺序决定了在构图指令的持续时间内,来自源轨道的视频帧应如何分层和组合。以下代码片段显示了如何设置不透明度斜坡以在转换到第二个视频之前缓慢淡出组合中的第一个视频:

AVAsset * firstVideoAssetTrack = <#AVAssetTrack表示组合#中播放的第一个视频段;

AVAsset * secondVideoAssetTrack = <#AVAssetTrack表示组合#中播放的第二个视频段;

//创建第一个视频构图指令。

AVMutableVideoCompositionInstruction * firstVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

//设置其时间范围以跨越第一个视频轨道的持续时间。

firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration);

//创建图层指令并将其与合成视频轨道相关联。

AVMutableVideoCompositionLayerInstruction * firstVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:mutableCompositionVideoTrack];

//创建不透明度斜坡以在整个持续时间内淡出第一个视频轨道。

[firstVideoLayerInstruction setOpacityRampFromStartOpacity:1.f toEndOpacity:0.f timeRange:CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration)];

//创建第二个视频合成指令,使第二个视频轨道不透明。

AVMutableVideoCompositionInstruction * secondVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

//设置其时间范围以跨越第二个视频轨道的持续时间。

secondVideoCompositionInstruction.timeRange = CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration));

//创建第二层指令并将其与合成视频轨道相关联。

AVMutableVideoCompositionLayerInstruction * secondVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:mutableCompositionVideoTrack];

//将第一层指令附加到第一个视频构图指令。

firstVideoCompositionInstruction.layerInstructions = @ [firstVideoLayerInstruction];

//将第二层指令附加到第二个视频构图指令。

secondVideoCompositionInstruction.layerInstructions = @ [secondVideoLayerInstruction];

//将两个视频构图指令附加到视频构图。

AVMutableVideoComposition * mutableVideoComposition = [AVMutableVideoComposition videoComposition];

mutableVideoComposition.instructions = @ [firstVideoCompositionInstruction,secondVideoCompositionInstruction];

Incorporating Core Animation Effects(结合核心动画效果)

视频构图可以通过animationTool属性添加Core Animation的功能。通过这个动画工具,您可以完成诸如水印视频和添加标题或动画叠加等任务。核心动画可以以两种不同的方式与视频作品一起使用:您可以添加一个核心动画图层作为自己的个人作品轨迹,或者您可以将核心动画效果(使用核心动画图层)直接渲染到构图中的视频帧中。以下代码通过向视频的中心添加水印来显示后一个选项:

CALayer * watermarkLayer = <#CALayer表示所需的水印图像#>;

CALayer * parentLayer = [CALayer layer];

CALayer * videoLayer = [CALayer layer];

parentLayer.frame = CGRectMake(0,0,mutableVideoComposition.renderSize.width,mutableVideoComposition.renderSize.height);

videoLayer.frame = CGRectMake(0,0,mutableVideoComposition.renderSize.width,mutableVideoComposition.renderSize.height);

[parentLayer addSublayer:videoLayer];

watermarkLayer.position = CGPointMake(mutableVideoComposition.renderSize.width / 2,mutableVideoComposition.renderSize.height / 4);

[parentLayer addSublayer:watermarkLayer];

mutableVideoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];

Putting It All Together: Combining Multiple Assets and Saving the Result to the Camera Roll(放在一起:组合多个asset并将结果保存到相机卷)

这个简短的代码示例说明了如何组合两个视频asset轨道和音频asset轨道来创建单个视频文件。它显示如何:

注意: 为了专注于最相关的代码,本示例省略了一个完整的应用程序的几个方面,如内存管理和错误处理。要使用AVFoundation,您将有足够的经验与可可推断丢失的部分。

Creating the Composition(创建构图)

要使用单独的资源组合曲目,您可以使用AVMutableComposition对象。创建构图并添加一个音频和一个视频轨道。

AVMutableComposition * mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack * videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:
AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack * audioCompositionTrack = [mutableComposition addMutableTrackWithMediaType:
AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

Adding the Assets(添加Asset)

一个空的作文你没有好处。将两个视频asset轨道和音频asset轨道添加到组合。
AVAssetTrack * firstVideoAssetTrack = [[firstVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack * secondVideoAssetTrack = [[secondVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration)ofTrack:firstVideoAssetTrack atTime:kCMTimeZero error:nil];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,secondVideoAssetTrack.timeRange.duration)ofTrack:secondVideoAssetTrack atTime:firstVideoAssetTrack.timeRange.duration error:nil];
[audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration))ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil];
注意: 这假设您有两个asset每个至少包含一个视频轨道,第三个asset至少包含一个音轨。可以从相机胶卷中检索视频,并且可以从音乐库或视频本身检索音轨。

Checking the Video Orientations(检查视频方向)

将视频和音轨添加到组合中后,您需要确保两个视频轨道的方向正确。默认情况下,所有视频轨道都假定为横向模式。如果您的视频轨道是以纵向模式拍摄的,导出时视频将不会正确定向。同样,如果您尝试将以纵向模式拍摄的视频与风景模式下的视频拍摄相结合,导出会话将无法完成。

BOOL isFirstVideoPortrait = NO;

CGAffineTransform firstTransform = firstVideoAssetTrack.preferredTransform;

// Check the first video track's preferred transform to determine if it was recorded in portrait mode.

if (firstTransform.a == 0 && firstTransform.d == 0 && (firstTransform.b == 1.0 || firstTransform.b == -1.0) && (firstTransform.c == 1.0 || firstTransform.c == -1.0)) {

isFirstVideoPortrait = YES;

}

BOOL isSecondVideoPortrait = NO;

CGAffineTransform secondTransform = secondVideoAssetTrack.preferredTransform;

// Check the second video track's preferred transform to determine if it was recorded in portrait mode.

if (secondTransform.a == 0 && secondTransform.d == 0 && (secondTransform.b == 1.0 || secondTransform.b == -1.0) && (secondTransform.c == 1.0 || secondTransform.c == -1.0)) {

isSecondVideoPortrait = YES;

}

if ((isFirstVideoAssetPortrait && !isSecondVideoAssetPortrait) || (!isFirstVideoAssetPortrait && isSecondVideoAssetPortrait)) {

UIAlertView *incompatibleVideoOrientationAlert = [[UIAlertView alloc] initWithTitle:@"Error!" message:@"Cannot combine a video shot in portrait mode with a video shot in landscape mode." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];

[incompatibleVideoOrientationAlert show];

return;

}

Applying the Video Composition Layer Instructions(应用视频组合层说明)

一旦知道视频片段具有兼容的方向,您可以对每个视频片段应用必要的图层指令,并将这些图层指令添加到视频构图。

AVMutableVideoCompositionInstruction * firstVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//设置第一个指令的时间范围,以跨越第一个视频轨道的持续时间。
firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration);
AVMutableVideoCompositionInstruction * secondVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//设置第二条指令的时间范围,以跨越第二个视频轨道的持续时间。
secondVideoCompositionInstruction.timeRange = CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration));
AVMutableVideoCompositionLayerInstruction * firstVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
//将第一层指令的变换设置为第一个视频轨道的首选变换。
[firstVideoLayerInstruction setTransform:firstTransform atTime:kCMTimeZero];
AVMutableVideoCompositionLayerInstruction * secondVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
//将第二层指令的变换设置为第二视频轨道的首选变换。
[secondVideoLayerInstruction setTransform:secondTransform atTime:
firstVideoAssetTrack.timeRange.duration];
firstVideoCompositionInstruction.layerInstructions = @ [firstVideoLayerInstruction];
secondVideoCompositionInstruction.layerInstructions = @ [secondVideoLayerInstruction];
AVMutableVideoComposition * mutableVideoComposition = [AVMutableVideoComposition videoComposition];
mutableVideoComposition.instructions = @ [firstVideoCompositionInstruction,secondVideoCompositionInstruction];

所有AVAssetTrack对象都具有preferredTransform包含该asset轨道的方向信息的属性。每当asset轨迹显示在屏幕上时,就会应用此转换。
在之前的代码中,层指令的变换设置为asset轨道的变换,以便在调整渲染大小后,新构图中的视频将正确显示。

Setting the Render Size and Frame Duration(设置渲染大小和帧持续时间)

要完成视频导向修复,您必须相应地调整renderSize属性。您还应该为该frameDuration属性选择合适的值,例如1/30秒(或30帧/秒)。默认情况下,renderScale属性设置为1.0,适用于此组合。

CGSize naturalSizeFirst,naturalSizeSecond;

//如果第一个视频asset以纵向模式拍摄,那么第二个视频资源如果我们在这里拍摄,

if(isFirstVideoAssetPortrait){

//反转视频轨道的宽度和高度,以确保它们正确显示。

naturalSizeFirst = CGSizeMake(firstVideoAssetTrack.naturalSize.height,firstVideoAssetTrack.naturalSize.width);

naturalSizeSecond = CGSizeMake(secondVideoAssetTrack.naturalSize.height,secondVideoAssetTrack.naturalSize.width);

}

else {

//如果视频没有以纵向模式拍摄,我们只能使用自然尺寸。

naturalSizeFirst = firstVideoAssetTrack.naturalSize;

naturalSizeSecond = secondVideoAssetTrack.naturalSize;

}

float renderWidth,renderHeight;

//将renderWidth和renderHeight设置为两个视频宽度和高度的最大值。

if(naturalSizeFirst.width> naturalSizeSecond.width){

renderWidth = naturalSizeFirst.width;

}

 else {

renderWidth = naturalSizeSecond.width;

}

if(naturalSizeFirst.height> naturalSizeSecond.height){

renderHeight = naturalSizeFirst.height;

}

else {

renderHeight = naturalSizeSecond.height;

}

mutableVideoComposition.renderSize = CGSizeMake(renderWidth,renderHeight);

//将帧持续时间设置为适当的值(即视频为30帧/秒)。

mutableVideoComposition.frameDuration = CMTimeMake(1,30);

Exporting the Composition and Saving it to the Camera Roll(导出构图并将其保存到相机胶卷)

此过程的最后一步是将整个组合导出到单个视频文件中,并将该视频保存到相机卷。您可以使用AVAssetExportSession对象创建新的视频文件,并将其传送给输出文件所需的URL。然后,您可以使用ALAssetsLibrary该类将生成的视频文件保存到相机胶卷。

// Create a static date formatter so we only have to initialize it once.
static NSDateFormatter *kDateFormatter;
if (!kDateFormatter) {
kDateFormatter = [[NSDateFormatter alloc] init];
kDateFormatter.dateStyle = NSDateFormatterMediumStyle;
kDateFormatter.timeStyle = NSDateFormatterShortStyle;
}
// Create the export session with the composition and set the preset to the highest quality.
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition presetName:AVAssetExportPresetHighestQuality];
// Set the desired output URL for the file created by the export process.
exporter.outputURL = [[[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:@YES error:nil] URLByAppendingPathComponent:[kDateFormatter stringFromDate:[NSDate date]]] URLByAppendingPathExtension:CFBridgingRelease(UTTypeCopyPreferredTagWithClass((CFStringRef)AVFileTypeQuickTimeMovie, kUTTagClassFilenameExtension))];
// Set the output file type to be a QuickTime movie.
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
exporter.videoComposition = mutableVideoComposition;
// Asynchronously export the composition to a video file and save this file to the camera roll once export completes.
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
    if (exporter.status == AVAssetExportSessionStatusCompleted) {
        ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
        if ([assetsLibrary videoAtPathIsCompatibleWithSavedPhotosAlbum:exporter.outputURL]) {
            [assetsLibrary writeVideoAtPathToSavedPhotosAlbum:exporter.outputURL completionBlock:NULL];
        }
    }
});
}];

下一页
上一页

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容