音視頻編解碼:視頻編解碼基礎(chǔ)1-FFmpeg結(jié)構(gòu)與API摘要
音視頻編解碼:視頻編解碼基礎(chǔ)2-FFmpeg解碼實(shí)踐
音視頻編解碼:視頻編解碼基礎(chǔ)篇3-FFmpeg轉(zhuǎn)碼實(shí)踐
音視頻編解碼:視頻編解碼基礎(chǔ)篇4-FFmpeg解碼播放
音視頻編解碼:視頻編解碼基礎(chǔ)篇5-FFmpeg水印添加
一.FFmpeg解碼播放
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渲染播放
}
}