iFrameExtractor地址:https://github.com/lajos/iFrameExtractor
ffmpeg的簡介
FFmpeg是一套可以用來記錄、轉換數字音訊、視訊,並能將其轉化為流的開源計算機程式。
“FFmpeg”這個單詞中的”FF”指的是”Fast Forward”。
ffmpeg支援的格式
ASF
AVI
BFI
FLV
GXF, General eXchange Format, SMPTE 360M
IFF
RL2
ISO base media file format(包括QuickTime, 3GP和MP4)
Matroska(包括WebM)
Maxis XA
MPEG program stream
MPEG transport stream(including AVCHD)
MXF, Material eXchange Format, SMPTE 377M
MSN Webcam stream
Ogg
OMA
TXD
WTV
ffmpeg支援的協議
IETF標準:TCP, UDP, Gopher, HTTP, RTP, RTSP和SDP
蘋果公司的相關標準:HTTP Live Streaming
RealMedia的相關標準:RealMedia RTSP/RDT
Adobe的相關標準:RTMP, RTMPT(由librtmp實現),RTMPE(由librtmp實現),RTMPTE(由librtmp)和RTMPS(由librtmp實現)
微軟的相關標準:MMS在TCP上和MMS在HTTP上
iFrameExtractor的使用
初始化
1 2 3 4 |
self.video = [[VideoFrameExtractor alloc] initWithVideo:[Utilities bundlePath:@"sophie.mov"]]; video.outputWidth = 426; video.outputHeight = 320; |
播放
1 2 3 4 5 6 |
[video seekTime:0.0]; [NSTimer scheduledTimerWithTimeInterval:1.0/30 target:self selector:@selector(displayNextFrame:) userInfo:nil repeats:YES]; |
1 2 3 4 5 6 7 8 |
-(void)displayNextFrame:(NSTimer *)timer { if (![video stepFrame]) { return; } imageView.image = video.currentImage; } |
VideoFrameExtractor類解析
initWithVideo:(NSString *)moviePath方法
VideoFrameExtractor的初始化,主要是配置三個全域性的結構體變數。
AVFormatContext型別的pFormatCtx,AVFormatContext主要儲存視音訊封裝格式中包含的資訊;AVInputFormat儲存輸入視音訊使用的封裝格式。每種視音訊封裝格式都對應一個AVInputFormat 結構。
AVCodecContext型別的pCodecCtx ,每個AVStream儲存一個視訊/音訊流的相關資料;每個AVStream對應一個AVCodecContext,儲存該視訊/音訊流使用解碼方式的相關資料;每個AVCodecContext中對應一個AVCodec,包含該視訊/音訊對應的解碼器。每種解碼器都對應一個AVCodec結構。
AVFrame型別的pFrame,視訊的話,每個結構一般是存一幀,音訊可能有好幾幀。解碼前資料是AVPacket,解碼後資料是AVFrame。
FMPEG中結構體很多。最關鍵的結構體他們之間的對應關係如下所示:
圖片來自:FFMPEG中最關鍵的結構體之間的關係
下面就是初始化的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
-(id)initWithVideo:(NSString *)moviePath { if (!(self=[super init])) return nil; AVCodec *pCodec; // Register all formats and codecs avcodec_register_all(); av_register_all(); // Open video file if(avformat_open_input(&pFormatCtx, [moviePath cStringUsingEncoding:NSASCIIStringEncoding], NULL, NULL) != 0) { av_log(NULL, AV_LOG_ERROR, "Couldn't open file\n"); goto initError; } // Retrieve stream information if(avformat_find_stream_info(pFormatCtx,NULL) < 0) { av_log(NULL, AV_LOG_ERROR, "Couldn't find stream information\n"); goto initError; } // Find the first video stream if ((videoStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &pCodec, 0)) < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n"); goto initError; } // Get a pointer to the codec context for the video stream pCodecCtx = pFormatCtx->streams[videoStream]->codec; // Find the decoder for the video stream pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec == NULL) { av_log(NULL, AV_LOG_ERROR, "Unsupported codec!\n"); goto initError; } // Open codec if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n"); goto initError; } // Allocate video frame pFrame = avcodec_alloc_frame(); outputWidth = pCodecCtx->width; self.outputHeight = pCodecCtx->height; return self; initError: [self release]; return nil; } |
sourceWidth和sourceHeight方法
獲取螢幕的寬和高
1 2 3 4 5 6 7 |
-(int)sourceWidth { return pCodecCtx->width; } -(int)sourceHeight { return pCodecCtx->height; } |
setupScaler方法
設定視訊播放檢視的尺寸
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-(void)setupScaler { // Release old picture and scaler avpicture_free(&picture); sws_freeContext(img_convert_ctx); // Allocate RGB picture avpicture_alloc(&picture, PIX_FMT_RGB24, outputWidth, outputHeight); // Setup scaler static int sws_flags = SWS_FAST_BILINEAR; img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, outputWidth, outputHeight, PIX_FMT_RGB24, sws_flags, NULL, NULL, NULL); } |
duration方法
獲取音視訊檔案的總時間
1 2 3 |
-(double)duration { return (double)pFormatCtx->duration / AV_TIME_BASE; } |
currentTime方法
顯示音視訊當前播放的時間
1 2 3 4 |
-(double)currentTime { AVRational timeBase = pFormatCtx->streams[videoStream]->time_base; return packet.pts * (double)timeBase.num / timeBase.den; } |
seekTime:(double)seconds方法
直接跳到音視訊的第seconds秒進行播放,預設從第0.0秒開始
1 2 3 4 5 6 |
-(void)seekTime:(double)seconds { AVRational timeBase = pFormatCtx->streams[videoStream]->time_base; int64_t targetFrame = (int64_t)((double)timeBase.den / timeBase.num * seconds); avformat_seek_file(pFormatCtx, videoStream, targetFrame, targetFrame, targetFrame, AVSEEK_FLAG_FRAME); avcodec_flush_buffers(pCodecCtx); } |
stepFrame方法
解碼視訊得到幀
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
-(BOOL)stepFrame { // AVPacket packet; int frameFinished=0; while(!frameFinished && av_read_frame(pFormatCtx, &packet)>=0) { // Is this a packet from the video stream? if(packet.stream_index==videoStream) { // Decode video frame avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); } } return frameFinished!=0; } |
currentImage方法
獲取當前的UIImage物件,以呈現當前播放的畫面
1 2 3 4 5 |
-(UIImage *)currentImage { if (!pFrame->data[0]) return nil; [self convertFrameToRGB]; return [self imageFromAVPicture:picture width:outputWidth height:outputHeight]; } |
convertFrameToRGB
轉換音視訊幀到RGB
1 2 3 4 5 |
-(void)convertFrameToRGB { sws_scale (img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, picture.data, picture.linesize); } |
(UIImage *)imageFromAVPicture:(AVPicture)pict width:(int)width height:(int)height方法
把AVPicture轉換成UIImage把音視訊畫面顯示出來
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
-(UIImage *)imageFromAVPicture:(AVPicture)pict width:(int)width height:(int)height { CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pict.data[0], pict.linesize[0]*height,kCFAllocatorNull); CGDataProviderRef provider = CGDataProviderCreateWithCFData(data); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGImageRef cgImage = CGImageCreate(width, height, 8, 24, pict.linesize[0], colorSpace, bitmapInfo, provider, NULL, NO, kCGRenderingIntentDefault); CGColorSpaceRelease(colorSpace); UIImage *image = [UIImage imageWithCGImage:cgImage]; CGImageRelease(cgImage); CGDataProviderRelease(provider); CFRelease(data); return image; } |
Reference
ElevenPlayer: 這是我用ffmpeg寫的iOS萬能播放器。
FFMPEG結構體分析-系列文章:包括AVFrame、AVFormatContext、AVCodecContext、AVIOContext、AVCodec、AVStream、AVPacket