1. 程式人生 > >ffmpeg在iOS的使用

ffmpeg在iOS的使用

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的使用

初始化

self.video = [[VideoFrameExtractor alloc] initWithVideo:[Utilities bundlePath:@"sophie.mov"]];

	video.outputWidth = 426;
	video.outputHeight = 320;

播放

	[video seekTime:0.0];
	[NSTimer scheduledTimerWithTimeInterval:1.0/30
									 target:self
								   selector:@selector(displayNextFrame:)
								   userInfo:nil
									repeats:YES];
-(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中結構體很多。最關鍵的結構體他們之間的對應關係如下所示:

下面就是初始化的程式碼

-(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方法

獲取螢幕的寬和高

-(int)sourceWidth {
	return pCodecCtx->width;
}

-(int)sourceHeight {
	return pCodecCtx->height;
}

setupScaler方法

設定視訊播放檢視的尺寸

-(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方法

獲取音視訊檔案的總時間

-(double)duration {
	return (double)pFormatCtx->duration / AV_TIME_BASE;
}
currentTime方法

顯示音視訊當前播放的時間

-(double)currentTime {
    AVRational timeBase = pFormatCtx->streams[videoStream]->time_base;
    return packet.pts * (double)timeBase.num / timeBase.den;
}
seekTime:(double)seconds方法

直接跳到音視訊的第seconds秒進行播放,預設從第0.0秒開始

-(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方法

解碼視訊得到幀

-(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物件,以呈現當前播放的畫面

-(UIImage *)currentImage {
	if (!pFrame->data[0]) return nil;
	[self convertFrameToRGB];
	return [self imageFromAVPicture:picture width:outputWidth height:outputHeight];
}

convertFrameToRGB

轉換音視訊幀到RGB

-(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把音視訊畫面顯示出來

-(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