FFmpeg的使用

現(xiàn)狀:現(xiàn)在視頻直播非常的火流酬,所以在視頻直播開發(fā)中,使用的對(duì)視頻進(jìn)行遍解碼的框架顯得尤為重要了曹宴,其實(shí),這種框架蠻多的歉提,這次主要介紹一下FFmpeg視頻播放器的集成和使用笛坦,F(xiàn)Fmpeg是視頻編解碼的利器。

介紹:視頻播放過程

首先簡單介紹以下視頻文件的相關(guān)知識(shí)苔巨。我們平時(shí)看到的視頻文件有許多格式版扩,比如 avi, mkv侄泽, rmvb礁芦, mov, mp4等等,這些被稱為容器Container)柿扣, 不同的容器格式規(guī)定了其中音視頻數(shù)據(jù)的組織方式(也包括其他數(shù)據(jù)肖方,比如字幕等)。容器中一般會(huì)封裝有視頻和音頻軌未状,也稱為視頻流(stream)和音頻 流俯画,播放視頻文件的第一步就是根據(jù)視頻文件的格式,解析(demux)出其中封裝的視頻流司草、音頻流以及字幕(如果有的話)艰垂,解析的數(shù)據(jù)讀到包 (packet)中,每個(gè)包里保存的是視頻幀(frame)或音頻幀翻伺,然后分別對(duì)視頻幀和音頻幀調(diào)用相應(yīng)的解碼器(decoder)進(jìn)行解碼材泄,比如使用 H.264編碼的視頻和MP3編碼的音頻,會(huì)相應(yīng)的調(diào)用H.264解碼器和MP3解碼器吨岭,解碼之后得到的就是原始的圖像(YUV or RGB)和聲音(PCM)數(shù)據(jù)拉宗,然后根據(jù)同步好的時(shí)間將圖像顯示到屏幕上,將聲音輸出到聲卡辣辫,最終就是我們看到的視頻旦事。

FFmpeg的API就是根據(jù)這個(gè)過程設(shè)計(jì)的,因此使用FFmpeg來處理視頻文件的方法非常直觀簡單急灭。下面就一步一步介紹從視頻文件中解碼出圖片的過程姐浮。

屬性:聲明變量

AVFormatContext:保存需要讀入的文件的格式信息,比如流的個(gè)數(shù)以及流數(shù)據(jù)等

AVCodecCotext:保存了相應(yīng)流的詳細(xì)編碼信息葬馋,比如視頻的寬卖鲤、高,編碼類型等畴嘶。

pCodec:真正的編解碼器蛋逾,其中有編解碼需要調(diào)用的函數(shù)

AVFrame:用于保存數(shù)據(jù)幀的數(shù)據(jù)結(jié)構(gòu),這里的兩個(gè)幀分別是保存顏色轉(zhuǎn)換前后的兩幀圖像

AVPacket:解析文件時(shí)會(huì)將音/視頻幀讀入到packet中

一 本播放器原理:

通過ffmpeg對(duì)視頻進(jìn)行解碼,解碼出每一幀圖片,然后根據(jù)一定時(shí)間播放每一幀圖

二 如何集成 ffmpeg

下載腳本ffmpeg腳本

根據(jù)上面鏈接的 README 進(jìn)行編譯

大致步驟:

1. 下載腳本:https://github.com/kewlbear/FFmpeg-iOS-build-script

2. 解壓窗悯,找到文件 build-ffmpeg.sh

3. 進(jìn)入終端区匣,執(zhí)行服本文件:./build-ffmpeg.sh, 由于本人沒有事先安裝Yasm,執(zhí)行腳本文件會(huì)出錯(cuò)蒋院,提示Homebrew not found亏钩,Trying 頭install.....如圖:

根據(jù)提示,按下enter鍵進(jìn)行安裝并編譯靜態(tài)庫FFmpeg,如下圖:

這是編譯后的靜態(tài)庫欺旧,截圖如下:

集成到項(xiàng)目,新建工程,將編譯好的靜態(tài)庫以及頭文件導(dǎo)入工程(demo)

導(dǎo)入依賴庫

設(shè)置頭文件路徑,路徑一定要對(duì),不然胡找不到頭文件

我設(shè)置路徑如下圖:

先 command + B 編譯一下,確保能編譯成功

三 開始編寫代碼

新建一個(gè)OC文件

////SJMoiveObject.h//SJLiveVideo////Created by king on 16/6/16.//Copyright ? 2016年 king. All rights reserved.//#import"Common.h"#import#import"NSString+Extions.h"#include#include#include@interfaceSJMoiveObject : NSObject/*解碼后的UIImage*/@property (nonatomic, strong,readonly) UIImage *currentImage;/*視頻的frame高度*/@property (nonatomic, assign,readonly)intsourceWidth, sourceHeight;/*輸出圖像大小姑丑。默認(rèn)設(shè)置為源大小。*/@property (nonatomic,assign)intoutputWidth, outputHeight;/*視頻的長度辞友,秒為單位*/@property (nonatomic, assign,readonly)doubleduration;/*視頻的當(dāng)前秒數(shù)*/@property (nonatomic, assign,readonly)doublecurrentTime;/*視頻的幀率*/@property (nonatomic, assign,readonly)doublefps;/*視頻路徑栅哀。*/- (instancetype)initWithVideo:(NSString *)moviePath;/*切換資源*/- (void)replaceTheResources:(NSString *)moviePath;/*重?fù)?/- (void)redialPaly;/*從視頻流中讀取下一幀。返回假,如果沒有幀讀炔搿(視頻)。*/-(BOOL)stepFrame;/*尋求最近的關(guān)鍵幀在指定的時(shí)間*/- (void)seekTime:(double)seconds;@end開始實(shí)現(xiàn)API////SJMoiveObject.m//SJLiveVideo////Created by king on 16/6/16.//Copyright ? 2016年 king. All rights reserved.//#import"SJMoiveObject.h"@interfaceSJMoiveObject ()

@property (nonatomic, copy) NSString*cruutenPath;@end@implementationSJMoiveObject

{

AVFormatContext*SJFormatCtx;

AVCodecContext*SJCodecCtx;

AVFrame*SJFrame;

AVStream*stream;

AVPacket? ? ? ? ? ? packet;

AVPicture? ? ? ? ? picture;intvideoStream;doublefps;

BOOL? ? ? ? ? ? ? ? isReleaseResources;

}#pragmamark ------------------------------------#pragmamark? 初始化- (instancetype)initWithVideo:(NSString *)moviePath {if(!(self=[super init]))returnnil;if([self initializeResources:[moviePath UTF8String]]) {

self.cruutenPath=[moviePath copy];returnself;

}else{returnnil;

}

}- (BOOL)initializeResources:(constchar*)filePath {

isReleaseResources=NO;

AVCodec*pCodec;//注冊(cè)所有解碼器avcodec_register_all();

av_register_all();

avformat_network_init();//打開視頻文件if(avformat_open_input(&SJFormatCtx, filePath, NULL, NULL) !=0) {

SJLog(@"打開文件失敗");gotoinitError;

}//檢查數(shù)據(jù)流if(avformat_find_stream_info(SJFormatCtx, NULL) <0) {

SJLog(@"檢查數(shù)據(jù)流失敗");gotoinitError;

}//根據(jù)數(shù)據(jù)流,找到第一個(gè)視頻流if((videoStream =? av_find_best_stream(SJFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &pCodec,0)) <0) {

SJLog(@"沒有找到第一個(gè)視頻流");gotoinitError;

}//獲取視頻流的編解碼上下文的指針stream? ? ? = SJFormatCtx->streams[videoStream];

SJCodecCtx= stream->codec;#ifDEBUG//打印視頻流的詳細(xì)信息av_dump_format(SJFormatCtx, videoStream, filePath,0);#endifif(stream->avg_frame_rate.den && stream->avg_frame_rate.num) {

fps= av_q2d(stream->avg_frame_rate);

}else{ fps =30; }//查找解碼器pCodec = avcodec_find_decoder(SJCodecCtx->codec_id);if(pCodec ==NULL) {

SJLog(@"沒有找到解碼器");gotoinitError;

}//打開解碼器if(avcodec_open2(SJCodecCtx, pCodec, NULL) <0) {

SJLog(@"打開解碼器失敗");gotoinitError;

}//分配視頻幀SJFrame =av_frame_alloc();

_outputWidth= SJCodecCtx->width;

_outputHeight= SJCodecCtx->height;returnYES;

initError:returnNO;

}- (void)seekTime:(double)seconds {

AVRational timeBase= SJFormatCtx->streams[videoStream]->time_base;

int64_t targetFrame= (int64_t)((double)timeBase.den / timeBase.num *seconds);

avformat_seek_file(SJFormatCtx,

videoStream,0,

targetFrame,

targetFrame,

AVSEEK_FLAG_FRAME);

avcodec_flush_buffers(SJCodecCtx);

}-(BOOL)stepFrame {intframeFinished =0;while(!frameFinished && av_read_frame(SJFormatCtx, &packet) >=0) {if(packet.stream_index ==videoStream) {

avcodec_decode_video2(SJCodecCtx,

SJFrame,&frameFinished,&packet);

}

}if(frameFinished ==0&& isReleaseResources ==NO) {

[self releaseResources];

}returnframeFinished !=0;

}- (void)replaceTheResources:(NSString *)moviePath {if(!isReleaseResources) {

[self releaseResources];

}

self.cruutenPath=[moviePath copy];

[self initializeResources:[moviePath UTF8String]];

}- (void)redialPaly {

[self initializeResources:[self.cruutenPath UTF8String]];

}#pragmamark ------------------------------------#pragmamark? 重寫屬性訪問方法-(void)setOutputWidth:(int)newValue {if(_outputWidth == newValue)return;

_outputWidth=newValue;

}-(void)setOutputHeight:(int)newValue {if(_outputHeight == newValue)return;

_outputHeight=newValue;

}-(UIImage *)currentImage {if(!SJFrame->data[0])returnnil;return[self imageFromAVPicture];

}-(double)duration {return(double)SJFormatCtx->duration /AV_TIME_BASE;

}- (double)currentTime {

AVRational timeBase= SJFormatCtx->streams[videoStream]->time_base;returnpacket.pts * (double)timeBase.num /timeBase.den;

}- (int)sourceWidth {returnSJCodecCtx->width;

}- (int)sourceHeight {returnSJCodecCtx->height;

}- (double)fps {returnfps;

}#pragmamark --------------------------#pragmamark - 內(nèi)部方法- (UIImage *)imageFromAVPicture

{

avpicture_free(&picture);

avpicture_alloc(&picture, AV_PIX_FMT_RGB24, _outputWidth, _outputHeight);structSwsContext * imgConvertCtx = sws_getContext(SJFrame->width,

SJFrame->height,

AV_PIX_FMT_YUV420P,

_outputWidth,

_outputHeight,

AV_PIX_FMT_RGB24,

SWS_FAST_BILINEAR,

NULL,

NULL,

NULL);if(imgConvertCtx == nil)returnnil;

sws_scale(imgConvertCtx,

SJFrame->data,

SJFrame->linesize,0,

SJFrame->height,

picture.data,

picture.linesize);

sws_freeContext(imgConvertCtx);

CGBitmapInfo bitmapInfo=kCGBitmapByteOrderDefault;

CFDataRef data=CFDataCreate(kCFAllocatorDefault,

picture.data[0],

picture.linesize[0] *_outputHeight);

CGDataProviderRef provider=CGDataProviderCreateWithCFData(data);

CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();

CGImageRef cgImage=CGImageCreate(_outputWidth,

_outputHeight,8,24,

picture.linesize[0],

colorSpace,

bitmapInfo,

provider,

NULL,

NO,

kCGRenderingIntentDefault);

UIImage*image =[UIImage imageWithCGImage:cgImage];

CGImageRelease(cgImage);

CGColorSpaceRelease(colorSpace);

CGDataProviderRelease(provider);

CFRelease(data);returnimage;

}#pragmamark --------------------------#pragmamark - 釋放資源- (void)releaseResources {

SJLog(@"釋放資源");

SJLogFunc

isReleaseResources=YES;//釋放RGBavpicture_free(&picture);//釋放frameav_packet_unref(&packet);//釋放YUV frameav_free(SJFrame);//關(guān)閉解碼器if(SJCodecCtx) avcodec_close(SJCodecCtx);//關(guān)閉文件if(SJFormatCtx) avformat_close_input(&SJFormatCtx);

avformat_network_deinit();

}@end

為了方便,在SB 拖一個(gè) UIImageView 控件 和按鈕 ?并連好線

////ViewController.m//SJLiveVideo////Created by king on 16/6/14.//Copyright ? 2016年 king. All rights reserved.//#import"ViewController.h"#import"SJMoiveObject.h"#import#import"SJAudioObject.h"#import"SJAudioQueuPlay.h"#defineLERP(A,B,C) ((A)*(1.0-C)+(B)*C)@interfaceViewController ()

@property (weak, nonatomic) IBOutlet UIImageView*ImageView;

@property (weak, nonatomic) IBOutlet UILabel*fps;

@property (weak, nonatomic) IBOutlet UIButton*playBtn;

@property (weak, nonatomic) IBOutlet UIButton*TimerBtn;

@property (weak, nonatomic) IBOutlet UILabel*TimerLabel;

@property (nonatomic, strong) SJMoiveObject*video;

@property (nonatomic, strong) SJAudioObject*audio;

@property (nonatomic, strong) SJAudioQueuPlay*audioPlay;

@property (nonatomic, assign)floatlastFrameTime;@end@implementationViewController@synthesizeImageView, fps, playBtn, video;- (void)viewDidLoad {

[super viewDidLoad];

self.video= [[SJMoiveObject alloc] initWithVideo:[NSString bundlePath:@"Dalshabet.mp4"]];//self.video = [[SJMoiveObject alloc] initWithVideo:@"/Users/king/Desktop/Stellar.mp4"];//self.video = [[SJMoiveObject alloc] initWithVideo:@"/Users/king/Downloads/Worth it - Fifth Harmony ft.Kid Ink - May J Lee Choreography.mp4"];//self.video = [[SJMoiveObject alloc] initWithVideo:@"/Users/king/Downloads/4K.mp4"];//self.video = [[SJMoiveObject alloc] initWithVideo:@"http://wvideo.spriteapp.cn/video/2016/0328/56f8ec01d9bfe_wpd.mp4"];//video.outputWidth = 800;//video.outputHeight = 600;self.audio = [[SJAudioObject alloc] initWithVideo:@"/Users/king/Desktop/Stellar.mp4"];

NSLog(@"視頻總時(shí)長>>>video duration: %f",video.duration);

NSLog(@"源尺寸>>>video size: %d x %d", video.sourceWidth, video.sourceHeight);

NSLog(@"輸出尺寸>>>video size: %d x %d", video.outputWidth, video.outputHeight);////[self.audio seekTime:0.0];//SJLog(@"%f", [self.audio duration])//AVPacket *packet = [self.audio readPacket];//SJLog(@"%ld", [self.audio decode])inttns, thh, tmm, tss;

tns=video.duration;

thh= tns /3600;

tmm= (tns %3600) /60;

tss= tns %60;//NSLog(@"fps --> %.2f", video.fps);////? ? ? ? [ImageView setTransform:CGAffineTransformMakeRotation(M_PI)];//NSLog(@"%02d:%02d:%02d",thh,tmm,tss);}- (IBAction)PlayClick:(UIButton *)sender {

[playBtn setEnabled:NO];

_lastFrameTime= -1;//seek to 0.0 seconds[video seekTime:0.0];

[NSTimer scheduledTimerWithTimeInterval:1/video.fps

target:self

selector:@selector(displayNextFrame:)

userInfo:nil

repeats:YES];

}- (IBAction)TimerCilick:(id)sender {//NSLog(@"current time: %f s",video.currentTime);//[video seekTime:150.0];//[video replaceTheResources:@"/Users/king/Desktop/Stellar.mp4"];if(playBtn.enabled) {

[video redialPaly];

[self PlayClick:playBtn];

}

}-(void)displayNextFrame:(NSTimer *)timer {

NSTimeInterval startTime=[NSDate timeIntervalSinceReferenceDate];//self.TimerLabel.text = [NSString stringWithFormat:@"%f s",video.currentTime];self.TimerLabel.text? =[self dealTime:video.currentTime];if(![video stepFrame]) {

[timer invalidate];

[playBtn setEnabled:YES];return;

}

ImageView.image=video.currentImage;floatframeTime =1.0/ ([NSDate timeIntervalSinceReferenceDate] -startTime);if(_lastFrameTime <0) {

_lastFrameTime=frameTime;

}else{

_lastFrameTime= LERP(frameTime, _lastFrameTime,0.8);

}

[fps setText:[NSString stringWithFormat:@"fps %.0f",_lastFrameTime]];

}- (NSString *)dealTime:(double)time {inttns, thh, tmm, tss;

tns=time;

thh= tns /3600;

tmm= (tns %3600) /60;

tss= tns %60;//[ImageView setTransform:CGAffineTransformMakeRotation(M_PI)];return[NSString stringWithFormat:@"%02d:%02d:%02d",thh,tmm,tss];

}@end

運(yùn)程序 ,點(diǎn)擊播放

我的測試結(jié)果如下:

原文地址:http://bbs.520it.com/forum.php?mod=viewthread&tid=707&page=1&extra=#pid3821

我集成后的demo:github源碼下載:https://github.com/xiayuanquan/FFmpegDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茵瀑,一起剝皮案震驚了整個(gè)濱河市间驮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌马昨,老刑警劉巖竞帽,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鸿捧,居然都是意外死亡屹篓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門匙奴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堆巧,“玉大人,你說我怎么就攤上這事泼菌〉簦” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵哗伯,是天一觀的道長荒揣。 經(jīng)常有香客問我,道長焊刹,這世上最難降的妖魔是什么系任? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮虐块,結(jié)果婚禮上俩滥,老公的妹妹穿的比我還像新娘。我一直安慰自己非凌,他們只是感情好举农,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著敞嗡,像睡著了一般颁糟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喉悴,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天棱貌,我揣著相機(jī)與錄音,去河邊找鬼箕肃。 笑死婚脱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播障贸,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼错森,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了篮洁?” 一聲冷哼從身側(cè)響起涩维,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袁波,沒想到半個(gè)月后瓦阐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡篷牌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年睡蟋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枷颊。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡戳杀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出偷卧,到底是詐尸還是另有隱情豺瘤,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布听诸,位于F島的核電站坐求,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏晌梨。R本人自食惡果不足惜桥嗤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仔蝌。 院中可真熱鬧泛领,春花似錦、人聲如沸敛惊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞧挤。三九已至锡宋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間特恬,已是汗流浹背执俩。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留癌刽,地道東北人役首。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓尝丐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親衡奥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子爹袁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

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

  • 原文鏈接http://www.cnblogs.com/kenshincui/p/4186022.html 音頻在i...
    Hyman0819閱讀 21,702評(píng)論 4 74
  • 教程一:視頻截圖(Tutorial 01: Making Screencaps) 首先我們需要了解視頻文件的一些基...
    90后的思維閱讀 4,695評(píng)論 0 3
  • 最近因?yàn)橐纛l處理的研究,音頻開發(fā),但是有一個(gè)難題就是怎么解析與提取音頻的數(shù)據(jù),于是就找到了FFmpeg确虱『眩基本上只...
    知曰閱讀 231,945評(píng)論 9 96
  • 使用FFmepg快速切割視頻 優(yōu)點(diǎn):速度夠快缺點(diǎn):如果切割的時(shí)間點(diǎn)不屬于關(guān)鍵幀則會(huì)出現(xiàn)切割的視頻開始部分停頓幾秒命...
    SomeAs小波閱讀 3,381評(píng)論 0 2
  • 夜裝進(jìn)秋的匣里,暮濃成墨 雨絲絞纏著路面 路面扭成風(fēng)梨的臉 一個(gè)身影走進(jìn)雨里 滴入樹技的眼 遠(yuǎn)方的一點(diǎn)亮光 藏不住...
    山上人家123閱讀 296評(píng)論 11 11