短视频录制-仿映客

项目中集成了短视频录制,UI和映客很像,在这里贴下代码搬运流程[迷之微笑]
录制.png
播放.png
基于系统提供框架

#import <AVFoundation/AVFoundation.h>

初始化必要属性
/**
 *  AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
 */
@property (nonatomic, strong) AVCaptureSession* session;
/**
 *  视频输入设备
 */
@property (nonatomic, strong) AVCaptureDeviceInput* videoInput;
/**
 *  声音输入
 */
@property (nonatomic, strong) AVCaptureDeviceInput* audioInput;
/**
 *  视频输出流
 */
@property(nonatomic,strong)AVCaptureMovieFileOutput *movieFileOutput;
/**
 *  预览图层
 */
@property (nonatomic, strong) AVCaptureVideoPreviewLayer* previewLayer;
#pragma mark - private mehtod
- (void)initAVCaptureSession{
    
    self.session = [[AVCaptureSession alloc] init];
    
    //这里根据需要设置  可以设置4K
    self.session.sessionPreset = AVCaptureSessionPreset1280x720;
    
    NSError *error;
    
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    //更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
    [device lockForConfiguration:nil];
    //设置闪光灯为自动
    [device setFlashMode:AVCaptureFlashModeAuto];
    [device unlockForConfiguration];
    
    self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
    
    self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio] error:nil];
    
    if (error) {
        DLog(@"error%@",error);
    }
    
    self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
    
    if ([self.session canAddInput:self.videoInput]) {
        [self.session addInput:self.videoInput];
    }
    
    if ([self.session canAddInput:self.audioInput]) {
        
        [self.session addInput:self.audioInput];
    }
    
    if ([self.session canAddOutput:self.movieFileOutput]) {
        
        [self.session addOutput:self.movieFileOutput];
    }
    
    [self basicViewSet];
}
开始录制前先要获取下权限
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
    {
        
        return;
    }
    
    //判断用户是否允许访问麦克风权限
    authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
    if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
    {
        //无权限
        [SVProgressHUD showWithStatus:@"未授予麦克风权限"];
        return;
    }

到了重点了-开始录制

- (void)startSession{
    
    if (![self.session isRunning]) {
        
        [self.session startRunning];
    }
}

AVCaptureConnection *movieConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
    AVCaptureVideoOrientation avcaptureOrientation = AVCaptureVideoOrientationPortrait;
    [movieConnection setVideoOrientation:avcaptureOrientation];
    [movieConnection setVideoScaleAndCropFactor:1.0];
    //    保存按URL
    self.url = [[RAFileManager defaultManager] filePathUrlWithUrl:[self getVideoSaveFilePathString]];
    
    [self.movieFileOutput startRecordingToOutputFileURL:self.url recordingDelegate:self];
按路径保存的方法是自己写的
- (NSURL*)filePathUrlWithUrl:(NSString*)url
{
    return [NSURL fileURLWithPath:[self filePathWithUrl:url]];
}
呵呵 其实是调用的系统方法

到这里录制的部分已经完了,视频已经按路径保存了

如果录制完成后就做些操作的话可以用这个
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error{}
这个代理

<AVCaptureFileOutputRecordingDelegate>
解释说是 调用此方法时,文件输出已经写完所有数据到一个文件的记录了。。。来自某道翻译

怪我咯.jpg

就先这样吧

除了视频的压缩上传、反复播放、圆环动画就没有了呼呼呼

有机会再接着搬。。。

老板要加短拍美颜-_-!,我就把我原生的代码都一块搬过来,做个纪念

#import "XNLMakVideoController.h"

#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import "XNLVideoPlayerController.h"
#import "RAFileManager.h"
#define TIMER_INTERVAL 0.05 //定时器间隔
#define VIDEO_RECORDER_MAX_TIME 60 //视频最大时长 (单位/秒)
#define VIDEO_RECORDER_MIN_TIME 30 //最短视频时长 (单位/秒)

@interface XNLMakVideoController ()<AVCaptureFileOutputRecordingDelegate>

@property (nonatomic) dispatch_queue_t sessionQueue;
/**
 *  AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
 */
@property (nonatomic, strong) AVCaptureSession* session;
/**
 *  视频输入设备
 */
@property (nonatomic, strong) AVCaptureDeviceInput* videoInput;
/**
 *  声音输入
 */
@property (nonatomic, strong) AVCaptureDeviceInput* audioInput;
/**
 *  视频输出流
 */
@property(nonatomic,strong)AVCaptureMovieFileOutput *movieFileOutput;
/**
 *  预览图层
 */
@property (nonatomic, strong) AVCaptureVideoPreviewLayer* previewLayer;
/**
 *  记录录制时间
 */
@property (nonatomic, strong) NSTimer* timer;

/**
 录制完成的视频路径
 */
@property (nonatomic,strong)NSURL *url;

/**
 开始View
 */
@property (nonatomic,strong) UIView *begView;
/**
 圆环的底环及填充环
 */
@property (strong, nonatomic)CAShapeLayer *ovalSShapeLayer;
@property (strong, nonatomic)CAShapeLayer *redShapeLayer;
@property (nonatomic,strong)UIView *centerCircle;// 中心圆

@end

@implementation XNLMakVideoController{
    //时间长度
    CGFloat timeLength;
    
}

- (void)viewWillAppear:(BOOL)animated{
    
    [super viewWillAppear:YES];
    
    [self startSession];
    
    //开启定时器
    [self.timer setFireDate:[NSDate distantPast]];
}

//页面消失,进入后台不显示该页面,关闭定时器
-(void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    //关闭定时器
    [self.timer setFireDate:[NSDate distantFuture]];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    [self initAVCaptureSession];

}

#pragma mark - private mehtod
- (void)initAVCaptureSession{
    
    self.session = [[AVCaptureSession alloc] init];
    
    //这里根据需要设置  可以设置4K
    
    if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//设置分辨率
        self.session.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    
    NSError *error;
    
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    //更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
    [device lockForConfiguration:nil];
    //设置闪光灯为自动
    [device setFlashMode:AVCaptureFlashModeAuto];
    [device unlockForConfiguration];
    
    self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
    
    self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio] error:nil];
    
    if (error) {
        DLog(@"error%@",error);
    }
    
    self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
    
    //它的默认值就是10秒。将这个值禁用后就好了--- 坑我不轻-_-!
    self.movieFileOutput.movieFragmentInterval = kCMTimeInvalid;

    
    if ([self.session canAddInput:self.videoInput]) {
        [self.session addInput:self.videoInput];
    }
    
    if ([self.session canAddInput:self.audioInput]) {
        
        [self.session addInput:self.audioInput];
    }
    
    if ([self.session canAddOutput:self.movieFileOutput]) {
        
        [self.session addOutput:self.movieFileOutput];
    }
    
    
    [self basicViewSet];
}


- (void)startSession{
    
    if (![self.session isRunning]) {
        
        [self.session startRunning];
    }
}

- (void)stopSession{
    
    if ([self.session isRunning]) {
        
        [self.session stopRunning];
    }
}

#pragma mark - 视频输出
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error{
    
    if (CMTimeGetSeconds(captureOutput.recordedDuration) < VIDEO_RECORDER_MIN_TIME) {
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"时间少于30s,不予保存"
                                                        message:nil
                                                       delegate:self
                                              cancelButtonTitle:@"确定"
                                              otherButtonTitles:nil, nil];
        [alert show];
        return;
    }
    WS(weakSelf);
    XNLVideoPlayerController *playerVC = [XNLVideoPlayerController new];
    playerVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    playerVC.url = self.url;
    playerVC.videoURL = ^(){
//        圆环重置
        weakSelf.redShapeLayer.strokeEnd = 0;
        weakSelf.centerCircle.backgroundColor = kRGBA(255, 255, 255, 0.8);
        // 删除视频
        [[RAFileManager defaultManager] removeFileWithUrl:weakSelf.url block:^(BOOL success) {
            DLog(@"----删除视频-----success-----%d",success);
        }];
    };
    [self presentViewController:playerVC animated:NO completion:^{}];
}

#pragma mark -- 定时器设置
- (void)timerFired{
    
    timeLength = 0;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(timerRecord) userInfo:nil repeats:YES];
}
- (void)timerStop{
    
    if ([self.timer isValid]) {
        
        [self.timer invalidate];
        self.timer = nil;
    }
}

#pragma mark - response method 录制按钮点击触发
- (void)takeVideoButtonPress:(UILongPressGestureRecognizer *)sender {
    
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
    {
        [SMGlobalMethod showViewCenter:kKeyWindow.center message:@"未授予摄像头权限"];
        return;
    }
    
    //判断用户是否允许访问麦克风权限
    authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
    if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
    {
        //无权限
        [SVProgressHUD showWithStatus:@"未授予麦克风权限"];
        return;
    }
    
    switch (sender.state) {
        case UIGestureRecognizerStateBegan:
            [self startVideoRecorder];
            break;
        case UIGestureRecognizerStateCancelled:
            [self stopVideoRecorder];
            break;
        case UIGestureRecognizerStateEnded:
            [self stopVideoRecorder];
            break;
        case UIGestureRecognizerStateFailed:
            [self stopVideoRecorder];
            break;
        default:
            break;
    }
    
}

#pragma 视频名以当前日期为名
- (NSString*)getVideoSaveFilePathString
{
    NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyyMMddHHmmss";
    NSString* nowTimeStr = [formatter stringFromDate:[NSDate dateWithTimeIntervalSinceNow:0]];
    
    return nowTimeStr;
}

#pragma mark 开始录制和结束录制
- (void)startVideoRecorder{
   
    
    AVCaptureConnection *movieConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
    AVCaptureVideoOrientation avcaptureOrientation = AVCaptureVideoOrientationPortrait;
    [movieConnection setVideoOrientation:avcaptureOrientation];
    [movieConnection setVideoScaleAndCropFactor:1.0];
    //    保存按URL
    self.url = [[RAFileManager defaultManager] filePathUrlWithUrl:[self getVideoSaveFilePathString]];
    
    [self.movieFileOutput startRecordingToOutputFileURL:self.url recordingDelegate:self];
    
    
    // 圆环弹性动画
    [UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:15 options:UIViewAnimationOptionLayoutSubviews animations:^{
        self.begView.transform = CGAffineTransformScale(self.begView.transform, 1.2, 1.2);
    } completion:^(BOOL finished) {
        
    }];
    
    self.redShapeLayer.strokeEnd = 0;
    
    [self timerFired];
    
}
// 录制结束后的方法
- (void)stopVideoRecorder{
    
    [self.movieFileOutput stopRecording];
    
    [self timerStop];
    
    self.begView.transform = CGAffineTransformIdentity;
    //strokeStart为0时是从3点钟方向开始,故将其旋转270度从12点钟方向开始
    self.begView.transform = CGAffineTransformMakeRotation((M_PI * 2) / 4 * 3);

}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
// 定时器调用方法
- (void)timerRecord{
    
    timeLength += (TIMER_INTERVAL/VIDEO_RECORDER_MAX_TIME);
    
    self.redShapeLayer.strokeEnd = timeLength;
    
    self.centerCircle.backgroundColor = kRGBA(255, 255*(1-timeLength), 255*(1-timeLength), 0.8);

    if (timeLength >= 1.0) {
        
        [self stopVideoRecorder];
        
        [self timerStop];
    }
}


#pragma mark -- 前后摄像头切换
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition) position {

    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];

    for (AVCaptureDevice *device in devices) {

        if ([device position] == position) {
            return device;
        }
    }
    return nil;
}
- (void)swapFrontAndBackCameras {
    // Assume the session is already running
    NSArray *inputs = self.session.inputs;
    for ( AVCaptureDeviceInput *input in inputs ) {
        AVCaptureDevice *device = input.device;
        if ( [device hasMediaType:AVMediaTypeVideo] ) {
            AVCaptureDevicePosition position = device.position;
            AVCaptureDevice *newCamera = nil;
            AVCaptureDeviceInput *newInput = nil;
            
            if (position == AVCaptureDevicePositionFront)
                newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
            else
                newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
            newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
            
            // beginConfiguration ensures that pending changes are not applied immediately
            [self.session beginConfiguration];
            
            [self.session removeInput:input];
            [self.session addInput:newInput];
            
            // Changes take effect once the outermost commitConfiguration is invoked.
            [self.session commitConfiguration];
            break;
        }
    } 
}
#pragma  mark -- 关闭
- (void)shutDownClick
{
    [self stopSession];
    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark -- 几个控件的布局
- (void)basicViewSet
{
    //初始化预览图层
    self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
    //    _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
    self.previewLayer.frame = CGRectMake(0, 0,kScreenWidth, kScreenHeight);
    
    [self.view.layer insertSublayer:self.previewLayer atIndex:0];
    
//    圆环、圆的父View
    self.begView = [[UIView alloc]initWithFrame:CGRectMake(kScreenWidth/2-48*kScreenW, kScreenHeight-130*kScreenH, 96*kScreenW, 96*kScreenW)];
    [self.view addSubview:self.begView];
    
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(takeVideoButtonPress:)];
    [self.begView addGestureRecognizer:longPress];
//    圆环
    [self creatLayer];
//    摄像头切换
    UIButton *fan = [UIButton buttonWithType:UIButtonTypeCustom];
    [fan setImage:kGetImage(@"shortvideo_icon_down_switch") forState:UIControlStateNormal];
    [self.view addSubview:fan];
    [fan mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(self.view).mas_offset(kScreenWidth/2 - 40*kScreenW);
        make.bottom.mas_equalTo(self.view.mas_bottom).mas_offset(-20*kScreenH);
        make.size.mas_equalTo(CGSizeMake(60*kScreenW, 60*kScreenW));
    }];
    [fan addTarget:self action:@selector(swapFrontAndBackCameras) forControlEvents:UIControlEventTouchUpInside];
    
//    关闭
    UIButton *shutDown = [UIButton buttonWithType:UIButtonTypeCustom];
    [shutDown setImage:kGetImage(@"XNLlive_yemian5_close") forState:UIControlStateNormal];
    [self.view addSubview:shutDown];
    [shutDown mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(self.view).mas_offset(kScreenWidth/2 - 40*kScreenW);
        make.top.mas_equalTo(self.view.mas_top).mas_offset(20*kScreenW);
        make.size.mas_equalTo(CGSizeMake(50*kScreenW, 50*kScreenW));
    }];
    [shutDown addTarget:self action:@selector(shutDownClick) forControlEvents:UIControlEventTouchUpInside];
}

// 圆环的设置
-(void)creatLayer
{
    //添加底层白色圆环
    self.ovalSShapeLayer = [[CAShapeLayer alloc]init];
    //圆环颜色
    self.ovalSShapeLayer.strokeColor = DEFAULT_BACKGROUND_COLOR.CGColor;
    //内部填充颜色
    self.ovalSShapeLayer.fillColor = [UIColor clearColor].CGColor;
    //线宽
    self.ovalSShapeLayer.lineWidth = 6*kScreenW;
    //半径
    CGFloat ovalRadius = self.begView.frame.size.height/2;
    self.ovalSShapeLayer.path =[UIBezierPath bezierPathWithOvalInRect:CGRectMake(self.begView.frame.size.width/2 - ovalRadius, self.begView.frame.size.height/2 - ovalRadius, ovalRadius * 2, ovalRadius * 2)].CGPath;
    self.ovalSShapeLayer.lineCap = kCALineCapRound;
    [self.begView.layer addSublayer:self.ovalSShapeLayer];
    
    //添加填充圆环
    self.redShapeLayer = [[CAShapeLayer alloc]init];
    self.redShapeLayer.strokeColor = [UIColor redColor].CGColor;
    self.redShapeLayer.fillColor = [UIColor clearColor].CGColor;
    self.redShapeLayer.lineWidth = 6*kScreenW;
    self.redShapeLayer.cornerRadius = 3.0*kScreenW;
    self.redShapeLayer.lineCap = kCALineCapRound;  
    
    self.redShapeLayer.path =[UIBezierPath bezierPathWithOvalInRect:CGRectMake(self.begView.frame.size.width/2 - ovalRadius, self.begView.frame.size.height/2 - ovalRadius, ovalRadius * 2, ovalRadius * 2)].CGPath;
    self.redShapeLayer.strokeStart = 0;
    self.redShapeLayer.strokeEnd = 0;
    
    [self.begView.layer addSublayer:self.redShapeLayer];
    
    // 中心圆
    self.centerCircle = [UIView new];
    self.centerCircle.backgroundColor = kRGBA(255, 255, 255, 0.8);
    self.centerCircle.layer.cornerRadius = 40*kScreenW;
    self.centerCircle.clipsToBounds = YES;
    [self.begView addSubview:self.centerCircle];
    [self.centerCircle mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(self.begView);
        make.size.mas_equalTo(CGSizeMake(80*kScreenW, 80*kScreenW));
    }];
    
    self.begView.transform = CGAffineTransformMakeRotation((M_PI * 2) / 4 * 3);
}
其中应该有用到一些大神的代码,像RAFileManager.h,如有不对请联系我
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容