1. 程式人生 > >iOS開發技巧之: FFmpeg的使用一

iOS開發技巧之: FFmpeg的使用一

複製程式碼
//
//  SJMoiveObject.h
//  SJLiveVideo
//
//  Created by king on 16/6/16.
//  Copyright © 2016年 king. All rights reserved.
//
 
#import "Common.h"
#import <UIKit/UIKit.h>
#import "NSString+Extions.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale//swscale.h>
@interface SJMoiveObject : NSObject /* 解碼後的UIImage */ @property (nonatomic, strong, readonly) UIImage *currentImage; /* 視訊的frame高度 */ @property (nonatomic, assign, readonly) int sourceWidth, sourceHeight; /* 輸出影象大小。預設設定為源大小。 */ @property (nonatomic,assign) int outputWidth, outputHeight; /* 視訊的長度,秒為單位
*/ @property (nonatomic, assign, readonly) double duration; /* 視訊的當前秒數 */ @property (nonatomic, assign, readonly) double currentTime; /* 視訊的幀率 */ @property (nonatomic, assign, readonly) double fps; /* 視訊路徑。 */ - (instancetype)initWithVideo:(NSString *)moviePath; /* 切換資源 */ - (void)replaceTheResources:(NSString *)moviePath;
/* 重撥 */ - (void)redialPaly; /* 從視訊流中讀取下一幀。返回假,如果沒有幀讀取(視訊)。 */ - (BOOL)stepFrame; /* 尋求最近的關鍵幀在指定的時間 */ - (void)seekTime:(double)seconds; @end 開始實現API // // SJMoiveObject.m // SJLiveVideo // // Created by king on 16/6/16. // Copyright © 2016年 king. All rights reserved. // #import "SJMoiveObject.h" @interface SJMoiveObject () @property (nonatomic, copy) NSString *cruutenPath; @end @implementation SJMoiveObject { AVFormatContext *SJFormatCtx; AVCodecContext *SJCodecCtx; AVFrame *SJFrame; AVStream *stream; AVPacket packet; AVPicture picture; int videoStream; double fps; BOOL isReleaseResources; } #pragma mark ------------------------------------ #pragma mark 初始化 - (instancetype)initWithVideo:(NSString *)moviePath { if (!(self=[super init])) return nil; if ([self initializeResources:[moviePath UTF8String]]) { self.cruutenPath = [moviePath copy]; return self; } else { return nil; } } - (BOOL)initializeResources:(const char *)filePath { isReleaseResources = NO; AVCodec *pCodec; // 註冊所有解碼器 avcodec_register_all(); av_register_all(); avformat_network_init(); // 開啟視訊檔案 if (avformat_open_input(&SJFormatCtx, filePath, NULL, NULL) != 0) { SJLog(@"開啟檔案失敗"); goto initError; } // 檢查資料流 if (avformat_find_stream_info(SJFormatCtx, NULL) < 0) { SJLog(@"檢查資料流失敗"); goto initError; } // 根據資料流,找到第一個視訊流 if ((videoStream = av_find_best_stream(SJFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &pCodec, 0)) < 0) { SJLog(@"沒有找到第一個視訊流"); goto initError; } // 獲取視訊流的編解碼上下文的指標 stream = SJFormatCtx->streams[videoStream]; SJCodecCtx = stream->codec; #if DEBUG // 列印視訊流的詳細資訊 av_dump_format(SJFormatCtx, videoStream, filePath, 0); #endif if(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(@"沒有找到解碼器"); goto initError; } // 開啟解碼器 if(avcodec_open2(SJCodecCtx, pCodec, NULL) < 0) { SJLog(@"開啟解碼器失敗"); goto initError; } // 分配視訊幀 SJFrame = av_frame_alloc(); _outputWidth = SJCodecCtx->width; _outputHeight = SJCodecCtx->height; return YES; initError: return NO; } - (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 { int frameFinished = 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]; } return frameFinished != 0; } - (void)replaceTheResources:(NSString *)moviePath { if (!isReleaseResources) { [self releaseResources]; } self.cruutenPath = [moviePath copy]; [self initializeResources:[moviePath UTF8String]]; } - (void)redialPaly { [self initializeResources:[self.cruutenPath UTF8String]]; } #pragma mark ------------------------------------ #pragma mark 重寫屬性訪問方法 -(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]) return nil; return [self imageFromAVPicture]; } -(double)duration { return (double)SJFormatCtx->duration / AV_TIME_BASE; } - (double)currentTime { AVRational timeBase = SJFormatCtx->streams[videoStream]->time_base; return packet.pts * (double)timeBase.num / timeBase.den; } - (int)sourceWidth { return SJCodecCtx->width; } - (int)sourceHeight { return SJCodecCtx->height; } - (double)fps { return fps; } #pragma mark -------------------------- #pragma mark - 內部方法 - (UIImage *)imageFromAVPicture { avpicture_free(&picture); avpicture_alloc(&picture, AV_PIX_FMT_RGB24, _outputWidth, _outputHeight); struct SwsContext * 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) return nil; 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); return image; } #pragma mark -------------------------- #pragma mark - 釋放資源 - (void)releaseResources { SJLog(@"釋放資源"); SJLogFunc isReleaseResources = YES; // 釋放RGB avpicture_free(&picture); // 釋放frame av_packet_unref(&packet); // 釋放YUV frame av_free(SJFrame); // 關閉解碼器 if (SJCodecCtx) avcodec_close(SJCodecCtx); // 關閉檔案 if (SJFormatCtx) avformat_close_input(&SJFormatCtx); avformat_network_deinit(); } @end
複製程式碼