視頻編解碼基礎(chǔ)篇4-FFmpeg解碼播放

音視頻編解碼:視頻編解碼基礎(chǔ)1-FFmpeg結(jié)構(gòu)與API摘要
音視頻編解碼:視頻編解碼基礎(chǔ)2-FFmpeg解碼實(shí)踐
音視頻編解碼:視頻編解碼基礎(chǔ)篇3-FFmpeg轉(zhuǎn)碼實(shí)踐
音視頻編解碼:視頻編解碼基礎(chǔ)篇4-FFmpeg解碼播放
音視頻編解碼:視頻編解碼基礎(chǔ)篇5-FFmpeg水印添加

一.FFmpeg解碼播放

FFmpeg音視頻編碼流程.jpg

1首先接到上篇基礎(chǔ)篇3的轉(zhuǎn)碼基礎(chǔ)上繼續(xù).
在正常打開(kāi)音視頻流后開(kāi)啟讀取音視頻流線程(注意線程安全加解鎖的多個(gè)細(xì)節(jié)點(diǎn))

預(yù)制關(guān)鍵類和基本數(shù)據(jù)
 @interface DJMediaFrame : NSObject
 @property (nonatomic) CGFloat position;
 @property (nonatomic) CGFloat duration;
 @end

 @interface DJAudioFrame : DJMediaFrame
 @property (nonatomic)NSData *samples;
 @end

 @interface DJVideoFrame : DJMediaFrame
 @property (nonatomic) NSInteger width;
 @property (nonatomic) NSInteger height;
 @end
 
 @interface DJVideoFrameYUV : DJVideoFrame
 @property (nonatomic)NSInteger dataLenth;
 @property (nonatomic)UInt8* buffer;
/**
     *  互斥鎖
     */
    NSCondition*        _condition;
/**
     *  用于裝解碼后的音視頻數(shù)據(jù)
     */
    AVFrame             *_videoFrame;
    AVFrame             *_audioFrame;
/**
     *  解碼上下文
     */
    AVFormatContext     *_inFormatCtx;
/**
     *  音視頻數(shù)據(jù)流標(biāo)志
     */
    NSInteger           _videoStream;
    NSInteger           _audioStream;
/**
     *  非直播流控制播放位置進(jìn)度
     */
    CGFloat             _duration;
/**
     *  主被動(dòng)退出線程條件標(biāo)志
     */
    BOOL                _isErrorOccur;
    BOOL  volatile      _closeStream;
    BOOL                _decoderRunning;
讀取數(shù)據(jù)流線程操作
-(BOOL)startDecodeThread{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *readOperation = [[NSInvocationOperation alloc] initWithTarget:wself selector:@selector(readFramesThreadProc) object:nil];
    NSInvocationOperation *popOperation  = [[NSInvocationOperation alloc] initWithTarget:wself selector:@selector(popFramesThreadProc) object:nil];
    [queue addOperation:readOperation];
    [queue addOperation:popOperation];
}

/**
 *  讀流數(shù)據(jù),讀數(shù)據(jù)的同時(shí)解碼
 */
-(void)readFramesThreadProc{
    
    @autoreleasepool {
        
        _decoderRunning = true;
        _isErrorOccur   = false;
        AVPacket packet;
        av_init_packet(&packet);
        NSLog(@"讀數(shù)據(jù)流線程開(kāi)始") UTF8String]);
        while (!(_closeStream || _isErrorOccur)) {
            _readFrameStartTime = av_gettime();
            [_condition lock];
            //具體問(wèn)題具體分析可活動(dòng)配置視頻幀率
            while (_decodeFrames.count > 0x30)
                [_condition wait];
            
            if (_closeStream) {
                [_condition unlock];
                break;
            }
            //讀取數(shù)據(jù)
            int result = av_read_frame(_inFormatCtx, &packet);
            if (result < 0) {
                _isErrorOccur = true;
                [_condition unlock];
                NSLog(@"數(shù)據(jù)讀取錯(cuò)誤 error->,%s", av_err2str(result));
                break;
            }
            
            if (packet.stream_index ==_videoStream) {
                [self decodeVideo:&packet];//處理視頻數(shù)據(jù)包
            } else if (packet.stream_index == _audioStream) {
                [self decodeAudio:&packet];//處理音頻數(shù)據(jù)包
            }
            [_condition unlock];
            av_packet_unref(&packet);
        }
        _decoderRunning = false;
        if (_isErrorOccur){
            //如果出錯(cuò)做相應(yīng)處理通知外部控制器
        }
       NSLog(@"讀取數(shù)據(jù)線程結(jié)束蔫缸,是否正常結(jié)束:(1:正常結(jié)束  0:網(wǎng)絡(luò)錯(cuò)誤結(jié)束) %d", _closeStream);
    }
}
/**
 *  取流數(shù)據(jù)
 */
-(void)popFramesThreadProc{
    
    int64_t popCounts = 0;
    int64_t prePopCounts = 0;
    CGFloat playPosition = 0;
    int spaceCount = 15;
    @autoreleasepool {
        NSLog(@"送數(shù)據(jù)線程開(kāi)始");
        while (!(_closeStream || _isErrorOccur)) {
            @synchronized(_decodeFrames) {
                if (_decodeFrames.count) {
                    DJMediaFrame* frame = [_decodeFrames firstObject];
                    if (enumFrameTypeAudio == frame.type) {
                        [self popAudioFrame:(DJAudioFrame*)frame];
                        [_decodeFrames removeObject:frame];
                    } else if (enumFrameTypeVideo == frame.type) {
                            popCounts ++;
                            playPosition = frame.position;
                            //音視頻流同步操作暫不做說(shuō)明
                           // [self checkAVPosition:playPosition];
                            [self popVideoFrame:(DJVideoFrame *)frame];
                            @synchronized (_videoFramesRecycle) {
                                [_videoFramesRecycle addObject:frame];
                            }
                            [_decodeFrames removeObject:frame];
                    }
                }
                //鎖信號(hào)標(biāo)志與解碼同步30/s幀
                if (_decodeFrames.count < 0x30){
                    [_condition signal];
                }
            }
        }
        NSLog(@"送數(shù)據(jù)線程結(jié)束");
    }
}
音視頻數(shù)據(jù)解碼avcodec_send_packet avcodec_receive_frame

ffmpeg3版本的解碼接口做了不少調(diào)整统屈,之前的視頻解碼接口avcodec_decode_video2和avcodec_decode_audio4音頻解碼被設(shè)置為deprecated擅编,對(duì)這兩個(gè)接口做了合并叫挟,使用統(tǒng)一的接口赘来。并且將音視頻解碼步驟分為了兩步皮假,第一步avcodec_send_packet转锈,第二步avcodec_receive_frame胧弛,通過(guò)接口名字我們就可以知道第一步是發(fā)送編碼數(shù)據(jù)包,第二步是接收解碼后數(shù)據(jù)侠畔。
avcodec_send_packet avcodec_receive_frame成對(duì)出現(xiàn)但不是一一對(duì)應(yīng)

-(void)decodeVideo:(AVPacket*)packet{

    @autoreleasepool {
        
        int result = avcodec_send_packet(_videoCodecCtx, packet);
        if (result < 0) {
             NSLog(@"發(fā)送視頻編碼數(shù)據(jù)包錯(cuò)誤 error-> %s", av_err2str(result));
            return;
        }
        while (avcodec_receive_frame(_videoCodecCtx, _videoFrame) >= 0) {
//視頻錄制處理
/*            if (self.recorderbool) {
                _errorOccur = ![self.assetWriter writeVideoFrame:_videoFrame];
                if (_errorOccur) {
                }else{
                }
            }*/
             DJXVideoFrame *frame = [self handleVideoFrame];
            if (frame) {
                [self addDecodeDJMediaFrame:frame];
            }
        }
    }
}

-(void)decodeAudio:(AVPacket*)packet{
    @autoreleasepool {
        int result         = avcodec_send_packet(_audioCodecCtx, packet);
        if (result < 0){
            NSLog(@"發(fā)送音頻編碼數(shù)據(jù)包錯(cuò)誤 error-> %s", av_err2str(result));
            return;
        }
        while (avcodec_receive_frame(_audioCodecCtx, _audioFrame) >= 0) {
            DJMediaFrame * frame = [self handleAudioFrame];
            if (frame){
                [self addDecodeDJMediaFrame:frame];
            }
        }
    }
}
 
-(void)addDecodeDJMediaFrame:(DJMediaFrame*)frame{
    @synchronized (_decodeFrames) {
        NSInteger index = _decodeFrames.count - 1;
        for (; index >= 0; index --) {
            DJMediaFrame* tempFrame = [_decodeFrames objectAtIndex:index];
            if (tempFrame.position < frame.position) break;
        }
        //向緩存容器添加視頻幀
        [_decodeFrames insertObject:frame atIndex:index + 1];
    }
}
//不加 @autoreleasepool ,內(nèi)存會(huì)得不到及時(shí)釋放
-(void)popAudioFrame:(DJAudioFrame*)frame{
    
    @autoreleasepool {
       //1通過(guò)音頻單元 AudioUnit 播放
      //2通過(guò)音頻隊(duì)列AudioQueueRef播放
    }
}

-(void)popVideoFrame:(DJVideoFrame*)frame{
    @autoreleasepool {
       //1通過(guò)YUV轉(zhuǎn)RGB->UIimage 直接播放
      //2將YUV處理通過(guò)OpenGLES渲染播放
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末结缚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子践图,更是在濱河造成了極大的恐慌掺冠,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件码党,死亡現(xiàn)場(chǎng)離奇詭異德崭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)揖盘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)眉厨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人兽狭,你說(shuō)我怎么就攤上這事憾股。” “怎么了箕慧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵服球,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我颠焦,道長(zhǎng)斩熊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任伐庭,我火速辦了婚禮粉渠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘圾另。我一直安慰自己霸株,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布集乔。 她就那樣靜靜地躺著去件,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扰路。 梳的紋絲不亂的頭發(fā)上尤溜,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音幼衰,去河邊找鬼靴跛。 笑死缀雳,一個(gè)胖子當(dāng)著我的面吹牛渡嚣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼识椰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼绝葡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起腹鹉,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤藏畅,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后功咒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體愉阎,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年力奋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榜旦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡景殷,死狀恐怖溅呢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猿挚,我是刑警寧澤咐旧,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站绩蜻,受9級(jí)特大地震影響铣墨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辜羊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一踏兜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧八秃,春花似錦碱妆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至骤肛,卻和暖如春纳本,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腋颠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工繁成, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淑玫。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓巾腕,卻偏偏與公主長(zhǎng)得像面睛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尊搬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345