[AVFoundation]播放

原文:AVFoundation Programming Guide

寫在前面

簡單翻譯一下AVFoundation的相關(guān)文檔鸟辅,本人英語水平一般,有不對的歡迎大家留言指正。

播放

可以使用AVPlayer對象來控制assets的播放宅此。在播放中,你可以使用AVPlayerItem實例來管理整個asset的呈現(xiàn)狀態(tài)爬范;使用AVPlayerItemTrack對象來管理一個獨立的track的呈現(xiàn)狀態(tài)父腕。你可以使用AVPlayerLayer對象來顯示視頻。

播放Assets

一個player是一個控制器對象青瀑,你可以使用它來管理asset的播放璧亮,例如控制開始和結(jié)束以及查找一個指定的時間位置萧诫。你使用一個AVPlayer實例來播放一個單獨的asset。你可以使用一個AVQueuePlayer(AVQueuePlayerAVPlayer子類)對象來播放一系列的項目枝嘶。在OS X上你可以選擇使用AVKit frameworkAVPlayerView帘饶。

一個player為你提供了有關(guān)播放狀態(tài)的信息。因此群扶,如果你需要的話及刻,你可以根據(jù)player的狀態(tài)來同步的處理你的用戶界面。你通常會直接將一個player輸出到一個特殊的核心動畫層(一個AVPlayerLayer對象或AVSynchronizedLayer對象)竞阐。更多的可以參考Core Animation Programming Guide

多播放層:你可以給一個單獨的AVPlayer實例創(chuàng)建多個AVPlayerLayer對象缴饭,但是只有最后一個創(chuàng)建的才會顯示視頻內(nèi)容。

盡管最終你想播放一個asset馁菜,但你不需要給AVPlayer對象直接提供assets茴扁。你需要提供一個AVPlayerItem對象實例。一個player item管理他所包含的asset的呈現(xiàn)狀態(tài)汪疮。一個player item包含player item tracks(AVPlayerItemTrack實例)它們對應(yīng)了asset中的tracks峭火。關(guān)系可參考Figure 2-1

Figure 2-1 Playing an asset

這代表你可以通過不同的player同時的播放一個給定的asset,在每一個player中呈現(xiàn)不同的效果智嚷。Figure 2-2展示了一種可能性卖丸,兩個不同的player通過不同的設(shè)置來播放同一個asset。使用item tracks盏道,你可以在播放中禁用一個特殊的track(如你可能不想播放聲音稍浆。)

Figure 2-2 Playing the same asset in different ways

你可以使用存在的asset來初始化一個player item,或者你也可以使用一個URL來初始猜嘱。同AVAsset一樣衅枫,簡單的初始化一個player item并不意味著它們可以播放了。你可以檢測(使用KVO)item的狀態(tài)屬性status來判斷它們是否可以播放的朗伶。

操作不同類型的Asset

你怎么配置一個asset的播放取決于你想播放的asset類型弦撩。一般的說,主要有兩種類型:基于文件的assets论皆,那些你可以隨意訪問的(如本地相冊或者媒體庫中的本地文件)益楼,和基于流的assets(HLS格式的)。

加載和播放本地asset点晴。步驟如下:

  • 使用AVURLAsset創(chuàng)建asset感凤。
  • 使用asset創(chuàng)建一個AVPlayerItem對象實例。
  • 關(guān)聯(lián)player item到一個AVPlayer對象粒督。
  • 等待item的狀態(tài)表明它是可以播放的(使用KVO來監(jiān)聽狀態(tài)變化)

可以參照Putting It All Together: Playing a Video File Using AVPlayerLayer.

創(chuàng)建和準備播放HLS陪竿。可以使用URL初始化一個AVPlayerItem的實例屠橄。(你不能直接創(chuàng)建一個AVAsset實例來表示HLS中的媒體對象族跛。)

NSURL *url = [NSURL URLWithString:@"<#Live stream URL#>];
// You may find a test stream at <http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8>.
self.playerItem = [AVPlayerItem playerItemWithURL:url];
[playerItem addObserver:self forKeyPath:@"status" options:0 context:&ItemStatusContext];
self.player = [AVPlayer playerWithPlayerItem:playerItem];

當你關(guān)聯(lián)一個player item到一個player的時候捐康,它開始準備變?yōu)榭梢砸圆シ诺臓顟B(tài)。當它可以播放的時候庸蔼,player item會創(chuàng)建AVAssetAVAssetTrack實例解总,你可以用來檢查直播流的內(nèi)容。

你可以監(jiān)聽player item的duration屬性來得到流對象的時長姐仅。當item可以播放的時候花枫,這個屬性會更新成正確的值。

注意:使用duration屬性需要iOS 4.3或者更高的系統(tǒng)才可以使用掏膏。一個在所有iOS版本中都支持的方法是監(jiān)聽player item的status屬性劳翰。當status變?yōu)?a target="_blank" rel="nofollow">AVPlayerItemStatusReadyToPlay的時候,你可以使用下面代碼獲得它的時長馒疹。

[[[[[playerItem tracks] objectAtIndex:0] assetTrack] asset] duration];

如果你只是想簡單的播放直播流佳簸,你可以使用一個快捷的方法來直接通過一個URL來創(chuàng)建player 代碼如下:

self.player = [AVPlayer playerWithURL:<#Live stream URL#>];
[player addObserver:self forKeyPath:@"status" options:0 context:&PlayerStatusContext];

使用assets和items來初始化player都不意味著他們是可以播放的。你應(yīng)該監(jiān)聽player的status屬性颖变,當它變?yōu)?a target="_blank" rel="nofollow">AVPlayerStatusReadyToPlay的時候就表明它是可以播放的了生均。你也可以監(jiān)聽currentItem屬性來訪問通過流創(chuàng)建的player item。

如果你不知道你使用的URL是那種類型的腥刹,你可以使用如下步驟:

1.嘗試通過URL來創(chuàng)建AVURLAsset马胧,然后加載它的tracks。
如果tracks加載成功的話衔峰,你就可以使用這個asset來創(chuàng)建一個player item了佩脊。
2.如果1失敗了,直接使用URL來創(chuàng)建AVPlayerItem垫卤。
監(jiān)聽player的status屬性來判斷他是否是可以播放的威彰。

如果任何一個成功了,你就可以結(jié)束判斷然后將palyer item關(guān)聯(lián)到player上了穴肘。

播放Item

你可以通過發(fā)送一個play的消息來開始播放歇盼。

- (IBAction)play:sender {
    [player play];
}

除了簡單的播放外,你還可以管理播放的其他方面梢褐,如播放速率和播放頭的位置旺遮。你也可以檢測player的播放狀態(tài)赵讯;如果你需要的話盈咳,這是很有用的,如根據(jù)asset的狀態(tài)同步改變用戶界面的呈現(xiàn)狀態(tài)边翼,可以參考Monitoring Playback鱼响。

改變播放速率

你可以通過設(shè)置player的rate屬性來改變播放速率。

aPlayer.rate = 0.5;
aPlayer.rate = 2.0;

1.0表示”以當前Item的正常速率來播放“组底。設(shè)置rate為0.0相當于暫停播放丈积。你當然也可以使用pause方法筐骇。

items支持逆向播放,你可以通過設(shè)置rate屬性為負數(shù)來實現(xiàn)江滨。你可以使用playerItem的canPlayReverse(支持設(shè)置rate為-1.0)铛纬,canPlaySlowReverse(支持設(shè)置rate為0.0~-1.0),canPlayFastReverse(支持設(shè)置rate小于-1.0)屬性來判斷是否支持逆向播放唬滑。

查找--重定播放頭位置

你可以使用seekToTime來重新設(shè)置播放的開始位置到一個特定的時間告唆。

CMTime fiveSecondsIn = CMTimeMake(5, 1);
[player seekToTime:fiveSecondsIn];

seekToTime方法并不精確。如果你需要精確的移動播放頭晶密,你可以使用seekToTime:toleranceBefore:toleranceAfter: 方法擒悬。代碼如下:

CMTime fiveSecondsIn = CMTimeMake(5, 1);
[player seekToTime:fiveSecondsIn toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];

使用0公差可能會使framework解碼大量的數(shù)據(jù)。你應(yīng)該只有當你需要使用的時候再使用稻艰。例如:編寫一個復(fù)雜的媒體編輯程序需要精確的控制懂牧。

播放完成后,player的播放頭會被設(shè)置到item的末尾尊勿,此時再次調(diào)用play方法會沒有任何反應(yīng)僧凤。你需要將播放頭重新設(shè)置到item的開始位置。你可以監(jiān)聽AVPlayerItemDidPlayToEndTimeNotification這個通知元扔。在這個通知的回調(diào)方法中你可以使用seekToTime:方法拼弃,參數(shù)設(shè)置為kCMTimeZero

// Register with the notification center after creating the player item.
    [[NSNotificationCenter defaultCenter]
        addObserver:self
        selector:@selector(playerItemDidReachEnd:)
        name:AVPlayerItemDidPlayToEndTimeNotification
        object:<#The player item#>];
 
- (void)playerItemDidReachEnd:(NSNotification *)notification {
    [player seekToTime:kCMTimeZero];
}
播放多個Items

你可以使用AVQueuePlayer對象來序列化的播放一組items摇展。AVQueuePlayer類是AVPlayer的子類吻氧。你可以使用一組player items來初始化一個queue player

NSArray *items = <#An array of player items#>;
AVQueuePlayer *queuePlayer = [[AVQueuePlayer alloc] initWithItems:items];

你可以使用play方法來播放AVQueuePlayer咏连,就像一個AVPlayer一樣盯孙。隊列會依次播放每一個item。如果你想跳到下一個item祟滴,你可以發(fā)送advanceToNextItem消息振惰。

你可以使用insertItem:afterItem:, removeItem:removeAllItems來改變隊列。當你添加一個新的item的時候垄懂,你應(yīng)該檢查它是否可以添加到隊列中骑晶,通過使用方法canInsertItem:afterItem:來判斷。你可以給第二個參數(shù)設(shè)置為nil來判斷新的item是否可以添加到隊列草慧。

AVPlayerItem *anItem = <#Get a player item#>;
if ([queuePlayer canInsertItem:anItem afterItem:nil]) {
    [queuePlayer insertItem:anItem afterItem:nil];
}
監(jiān)控播放

你可以監(jiān)測多個方面的內(nèi)容桶蛔,包括player的呈現(xiàn)狀態(tài)和播放中的player item。當狀態(tài)的改變不是在你直接控制下的時候漫谷,這是非常有用的仔雷。如:

  • 當用戶使用多任務(wù)來切換不同的應(yīng)用的時候,player的rate屬性會調(diào)整為0.0。
  • 如果你正在播放遠程媒體碟婆,player item的loadedTimeRangesseekableTimeRanges屬性會隨著數(shù)據(jù)的增加而變化电抚。
    這些屬性告訴你player item的時間軸中哪部分是可以使用的。
  • player的currentItem屬性會改變竖共,當使用HTTP直播流創(chuàng)建一個player item的時候蝙叛。
  • player item的tracks屬性在播放一個HTTP直播流的時候可能會改變。
    這個可能發(fā)生在播放流支持不同的編碼的時候公给;當選擇不同的編碼的時候tracks會發(fā)生變化甥温。
  • player或者player item的狀態(tài)屬性status當因為某些原因播放失敗的時候會發(fā)生變化。

你可以使用KVO來監(jiān)測這些屬性的變化妓布。

重要事項:
你應(yīng)該在主線程中注冊和注銷KVO變化通知姻蚓。這個避免了接收部分通知如果這個改變發(fā)生在其他線程中。AV Foundation會在主線程中觸發(fā)observeValueForKeyPath:ofObject:change:context: 方法匣沼,即使這些變化發(fā)生在其他線程狰挡。

響應(yīng)狀態(tài)變化

當player或者player item的狀態(tài)發(fā)生變化的時候,它發(fā)出了一個KVO變化的通知释涛。如果一個對象由于某些原因不能播放(如媒體服務(wù)被重置)加叁,狀態(tài)會變成AVPlayerStatusFailed或者AVPlayerItemStatusFailed。在這種情況下唇撬,這個對象的error屬性會變?yōu)槊枋鰧ο鬄槭裁床荒懿シ诺膃rror對象它匕。

AV Foundation 并不指定發(fā)出通知的線程。如果你想更新用戶的界面窖认,你必須確保相關(guān)的代碼在主線程中被執(zhí)行豫柬。下面的代碼使用dispatch_async在主線程中執(zhí)行代碼。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context {
 
    if (context == <#Player status context#>) {
        AVPlayer *thePlayer = (AVPlayer *)object;
        if ([thePlayer status] == AVPlayerStatusFailed) {
            NSError *error = [<#The AVPlayer object#> error];
            // Respond to error: for example, display an alert sheet.
            return;
        }
        // Deal with other status change if appropriate.
    }
    // Deal with other change notifications if appropriate.
    [super observeValueForKeyPath:keyPath ofObject:object
           change:change context:context];
    return;
}
Tracking Readiness for Visual Display

你可以觀察AVPlayerLayer對象的readyForDisplay屬性扑浸,這個屬性當layer有用戶可以觀看的內(nèi)容的時候會發(fā)生變化烧给。通常,你應(yīng)該只有當有內(nèi)容可以供用戶觀看的時候才將player的layer插入到layer層中喝噪。

跟蹤時間

你可以使用addPeriodicTimeObserverForInterval:queue:usingBlock或者addBoundaryTimeObserverForTimes:queue:usingBlock:來跟蹤AVPlayer對象的播放頭位置的變化础嫡。你可以更新用戶界面的信息如已經(jīng)播放的時間和剩余的時間,或者做一下其他的用戶界面的同步處理酝惧。

上面兩個方法都返回一個不透明對象可以作為一個觀察者缺亮。你必須對返回值保持一個強引用直到你想要player觸發(fā)block的時間翁涤。你必須相應(yīng)的調(diào)用removeTimeObserver:方法。

對于這些方法萌踱, AV Foundation并不能保證在每一個時間間隔或者邊界都調(diào)用相應(yīng)的block葵礼。AV Foundation不會在前一個block沒有完成的時候再次調(diào)用block。你必須確保你要執(zhí)行的block不會太消耗系統(tǒng)并鸵。

// Assume a property: @property (strong) id playerObserver;
 
Float64 durationSeconds = CMTimeGetSeconds([<#An asset#> duration]);
CMTime firstThird = CMTimeMakeWithSeconds(durationSeconds/3.0, 1);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 1);
NSArray *times = @[[NSValue valueWithCMTime:firstThird], [NSValue valueWithCMTime:secondThird]];
 
self.playerObserver = [<#A player#> addBoundaryTimeObserverForTimes:times queue:NULL usingBlock:^{
 
    NSString *timeDescription = (NSString *)
        CFBridgingRelease(CMTimeCopyDescription(NULL, [self.player currentTime]));
    NSLog(@"Passed a boundary at %@", timeDescription);
}];
播放結(jié)束

Item在播放完成的時候會發(fā)生一個AVPlayerItemDidPlayToEndTimeNotification通知鸳粉。

[[NSNotificationCenter defaultCenter] addObserver:<#The observer, typically self#>
                                         selector:@selector(<#The selector name#>)
                                             name:AVPlayerItemDidPlayToEndTimeNotification
                                           object:<#A player item#>];
使用AVPlayerLayer播放視頻文件

這個簡單的代碼示例說明了如何使用一個AVPlayer對象播放一個視頻文件。步驟如下:

  • 使用AVPlayerLayerlayer配置view
  • 創(chuàng)建一個AVPlayer對象
  • 使用基于文件的asset創(chuàng)建一個AVPlayerItem對象园担,并且使用KVC監(jiān)聽status屬性
  • 當item可以播放的時候激活一個button
  • 播放item届谈,然后重置到開始位置

注意:為了關(guān)注最主要的代碼,這個示例忽略了一些東西弯汰,如內(nèi)存管理艰山、注銷一個觀察者等。為了使用AV Foundation咏闪,你需要對使用Cocoa框架有豐富的經(jīng)驗來推斷出忽略的代碼曙搬。關(guān)于playback的概念可以參考Playing Assets

The Player View

為了播放一個asset鸽嫂,你需要有一個view來包含一個AVPlayerLayer層來輸出纵装,你可以簡單的使用一個UIView的子類來處理它:

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
 
@interface PlayerView : UIView
@property (nonatomic) AVPlayer *player;
@end
 
@implementation PlayerView
+ (Class)layerClass {
    return [AVPlayerLayer class];
}
- (AVPlayer*)player {
    return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
    [(AVPlayerLayer *)[self layer] setPlayer:player];
}
@end
A Simple View Controller

假設(shè)你有一個簡單的ViewController,如下:

@class PlayerView;
@interface PlayerViewController : UIViewController
 
@property (nonatomic) AVPlayer *player;
@property (nonatomic) AVPlayerItem *playerItem;
@property (nonatomic, weak) IBOutlet PlayerView *playerView;
@property (nonatomic, weak) IBOutlet UIButton *playButton;
- (IBAction)loadAssetFromFile:sender;
- (IBAction)play:sender;
- (void)syncUI;
@end

syncUI方法用來根據(jù)player的狀態(tài)來同步改變button的狀態(tài)据某。

- (void)syncUI {
    if ((self.player.currentItem != nil) &&
        ([self.player.currentItem status] == AVPlayerItemStatusReadyToPlay)) {
        self.playButton.enabled = YES;
    }
    else {
        self.playButton.enabled = NO;
    }
}

你可以在viewDidLoad中調(diào)用syncUI方法來確保在view第一次顯示的時候用戶界面的一致性橡娄。

- (void)viewDidLoad {
    [super viewDidLoad];
    [self syncUI];
}

其他的一些屬性和方法在剩下的內(nèi)容中介紹。

Creating the Asset

你可以使用AVURLAsset的方法來通過一個URL來創(chuàng)建asset癣籽。下面的方法假設(shè)你的工程中包含一個合適的視頻文件挽唉。

- (IBAction)loadAssetFromFile:sender {
 
    NSURL *fileURL = [[NSBundle mainBundle]
        URLForResource:<#@"VideoFileName"#> withExtension:<#@"extension"#>];
 
    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
    NSString *tracksKey = @"tracks";
 
    [asset loadValuesAsynchronouslyForKeys:@[tracksKey] completionHandler:
     ^{
         // The completion block goes here.
     }];
}

在完成的block中你可以創(chuàng)建一個AVPlayerItem對象,并將它設(shè)置到player view上筷狼。簡單的創(chuàng)建一個asset并不意味著它是可用的橱夭。為了判斷什么時候它是可用的,你可以監(jiān)聽item的status屬性桑逝。

// Define this constant for the key-value observation context.
static const NSString *ItemStatusContext;
 
// Completion handler block.
         dispatch_async(dispatch_get_main_queue(),
            ^{
                NSError *error;
                AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&error];
 
                if (status == AVKeyValueStatusLoaded) {
                    self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
                     // ensure that this is done before the playerItem is associated with the player
                    [self.playerItem addObserver:self forKeyPath:@"status"
                                options:NSKeyValueObservingOptionInitial context:&ItemStatusContext];
                    [[NSNotificationCenter defaultCenter] addObserver:self
                                                              selector:@selector(playerItemDidReachEnd:)
                                                                  name:AVPlayerItemDidPlayToEndTimeNotification
                                                                object:self.playerItem];
                    self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
                    [self.playerView setPlayer:self.player];
                }
                else {
                    // You should deal with the error appropriately.
                    NSLog(@"The asset's tracks were not loaded:\n%@", [error localizedDescription]);
                }
            });
處理Player item的狀態(tài)變化

當player item的狀態(tài)發(fā)生變化的時候棘劣,view controller接收到一個KVO變化的通知。AV Foundation并不指定發(fā)送消息的線程楞遏。如果你需要更新UI茬暇,你必須確保你的方法是在主線程中被執(zhí)行的。這個示例使用了dispatch_async來實現(xiàn)在主線程中更新UI寡喝。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context {
 
    if (context == &ItemStatusContext) {
        dispatch_async(dispatch_get_main_queue(),
                       ^{
                           [self syncUI];
                       });
        return;
    }
    [super observeValueForKeyPath:keyPath ofObject:object
           change:change context:context];
    return;
}
Playing the Item
- (IBAction)play:sender {
    [player play];
}

這個item只會播放一次糙俗。在播放完成后,player的播放頭被設(shè)置到了item的末尾预鬓,以后再次調(diào)用play方法都不會再有作用了巧骚。可以重置播放頭到item的開始位置,你可以以通過接收AVPlayerItemDidPlayToEndTimeNotification來處理劈彪。在通知的回調(diào)方法中竣蹦,調(diào)用seekToTime:方法,參數(shù)設(shè)置為kCMTimeZero.

// Register with the notification center after creating the player item.
    [[NSNotificationCenter defaultCenter]
        addObserver:self
        selector:@selector(playerItemDidReachEnd:)
        name:AVPlayerItemDidPlayToEndTimeNotification
        object:[self.player currentItem]];
 
- (void)playerItemDidReachEnd:(NSNotification *)notification {
    [self.player seekToTime:kCMTimeZero];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沧奴,一起剝皮案震驚了整個濱河市痘括,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滔吠,老刑警劉巖纲菌,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疮绷,居然都是意外死亡翰舌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門冬骚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灶芝,“玉大人,你說我怎么就攤上這事唉韭∫固椋” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵属愤,是天一觀的道長女器。 經(jīng)常有香客問我,道長住诸,這世上最難降的妖魔是什么驾胆? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮贱呐,結(jié)果婚禮上丧诺,老公的妹妹穿的比我還像新娘。我一直安慰自己奄薇,他們只是感情好驳阎,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著馁蒂,像睡著了一般呵晚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沫屡,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天饵隙,我揣著相機與錄音,去河邊找鬼沮脖。 笑死金矛,一個胖子當著我的面吹牛芯急,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播驶俊,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼娶耍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了废睦?” 一聲冷哼從身側(cè)響起伺绽,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤养泡,失蹤者是張志新(化名)和其女友劉穎嗜湃,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澜掩,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡购披,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肩榕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刚陡。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖株汉,靈堂內(nèi)的尸體忽然破棺而出筐乳,到底是詐尸還是另有隱情,我是刑警寧澤乔妈,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布蝙云,位于F島的核電站,受9級特大地震影響路召,放射性物質(zhì)發(fā)生泄漏勃刨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一股淡、第九天 我趴在偏房一處隱蔽的房頂上張望身隐。 院中可真熱鬧,春花似錦唯灵、人聲如沸贾铝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忌傻。三九已至,卻和暖如春搞监,著一層夾襖步出監(jiān)牢的瞬間水孩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工琐驴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俘种,地道東北人秤标。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像宙刘,于是被迫代替她去往敵國和親苍姜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

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