1. 程式人生 > >Unity3D-iOS建立本地視訊

Unity3D-iOS建立本地視訊

在遊戲中,有時候需要將一些簡單的圖片組合成視訊,然後讓使用者分享出去,當然僅僅是那種很簡單的圖片組合視訊(這是我自己專案的需求,不需要錄製UI等其他東西)。本來想找外掛的,後面看到大部分外掛都是通過攝像機去錄屏,但是我們專案本來就一個對圖片操作的遊戲,因此沒法用外掛。

尋思著自己寫一個,還好iOS方面的東西還是很齊全的,在查閱了很多資料和部落格之後,終於被我弄出來的。

原理也簡單,就是通過寫iOS原生的視訊生成程式碼,然後通過Unity去呼叫的方式去做的,Unity本身並不能生成視訊!

這裡說明下,我寫的程式碼都是在我原來部落格寫的一個類裡面新增的,可以先看看我之前的類:原生分享

新增一個將圖片合成視訊的方法(這個方法是我在網上找的,自己並不會oc - -!)

- (void) combineImageToVideo2:(NSString*)mainPath byName:(NSString*)videoName imageCount:(int)count
{
    NSLog(@"path = %@, video name = %@", mainPath, videoName);
    imageArray = [[NSMutableArray alloc]init];
    for(int i=0;i<count;i++){
        NSString* _pathImage = [mainPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%dtv.png", i]];

        NSData* data = [NSData dataWithContentsOfFile:_pathImage];

        UIImage* image = [UIImage imageWithData:data];

        [imageArray addObject:image];
    }
    
    
    NSString* moviePath = [mainPath stringByAppendingPathComponent:videoName];//@"outputVideo.mov"
    self.theMainPath = moviePath;
    self.theVideoName = videoName;
    //定義視訊的大小 256 256 倍數
    CGSize size = CGSizeMake(256, 256);
    
    NSError *error = nil;
    //    轉成UTF-8編碼
    unlink([moviePath UTF8String]);
    //     iphone提供了AVFoundation庫來方便的操作多媒體裝置,AVAssetWriter這個類可以方便的將影象和音訊寫成一個完整的視訊檔案
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:moviePath] fileType:AVFileTypeQuickTimeMovie error:&error];
    NSParameterAssert(videoWriter);
    if (error){
        NSLog(@"error = %@", [error localizedDescription]);
        UnitySendMessage("GJCNativeShare", "OnCreateVideoFailed", [GJC_DataConvertor NSStringToChar:@"error: videoWriter error"]);
    }
    //mov的格式設定 編碼格式 寬度 高度
    NSDictionary *videoSettings =[NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264,AVVideoCodecKey,
                                  [NSNumber numberWithInt:size.width],AVVideoWidthKey,
                                  [NSNumber numberWithInt:size.height],AVVideoHeightKey,nil];
    AVAssetWriterInput *writerInput =[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
    NSDictionary*sourcePixelBufferAttributesDictionary =[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32ARGB],kCVPixelBufferPixelFormatTypeKey,nil];
    //    AVAssetWriterInputPixelBufferAdaptor提供CVPixelBufferPool例項,
    //    可以使用分配畫素緩衝區寫入輸出檔案。使用提供的畫素為緩衝池分配通常
    //    是更有效的比新增畫素緩衝區分配使用一個單獨的池
    AVAssetWriterInputPixelBufferAdaptor *adaptor =[AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];
    NSParameterAssert(writerInput);
    NSParameterAssert([videoWriter canAddInput:writerInput]);
    if ([videoWriter canAddInput:writerInput])
    {
        NSLog(@"can add input");
//        UnitySendMessage("GJCNativeShare", "OnCreateVideoWork", [GJC_DataConvertor NSStringToChar:@"write: videoWriter can add input"]);
    }
    else
    {
        NSLog(@"can not add input");
        UnitySendMessage("GJCNativeShare", "OnCreateVideoFailed", [GJC_DataConvertor NSStringToChar:@"error: videoWriter can not add input"]);
    }
    [videoWriter addInput:writerInput];
    [videoWriter startWriting];
    [videoWriter startSessionAtSourceTime:kCMTimeZero];
    
    //合成多張圖片為一個視訊檔案
    dispatch_queue_t dispatchQueue =dispatch_queue_create("mediaInputQueue",NULL);
    int __block frame =0;
    [writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{
        
        BOOL blnError = NO;
        while([writerInput isReadyForMoreMediaData])
        {
            if(++frame >= [imageArray count])
            {
                [writerInput markAsFinished];
//                [videoWriter finishWriting];
//                [videoWriter finishWritingWithCompletionHandler:nil];
                [videoWriter finishWritingWithCompletionHandler:^{
                    NSLog(@"creat video finished!");
                }];
                
                if (blnError){
                    UnitySendMessage("GJCNativeShare", "OnCreateVideoFailed", [GJC_DataConvertor NSStringToChar:@"Failed"]);
                }else{
                    UnitySendMessage("GJCNativeShare", "OnCreateVideoSuccess", [GJC_DataConvertor NSStringToChar:@"Success"]);
                }
                break;
            }
            CVPixelBufferRef buffer = NULL;
            int idx = frame;
            NSLog(@"idx==%d",idx);
            buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:[[imageArray objectAtIndex:idx] CGImage] size:size];
            
            if (buffer)
            {
                //設定每秒鐘播放圖片的個數
                if(![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, 12)])
                {
                    NSLog(@"FAIL");
//                    UnitySendMessage("GJCNativeShare", "OnCreateVideoWork", [GJC_DataConvertor NSStringToChar:@"error: adaptor add error"]);
                    blnError = YES;
                }
                else
                {
//                    NSLog(@"OK");
//                    UnitySendMessage("GJCNativeShare", "OnCreateVideoWork", [GJC_DataConvertor NSStringToChar:@"write: adaptor add ok."]);
                }
                CFRelease(buffer);
            }
        }
    }];
}

- (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image size:(CGSize)size
{
    NSDictionary *options =[NSDictionary dictionaryWithObjectsAndKeys:
                            [NSNumber numberWithBool:YES],kCVPixelBufferCGImageCompatibilityKey,
                            [NSNumber numberWithBool:YES],kCVPixelBufferCGBitmapContextCompatibilityKey,nil];
    CVPixelBufferRef pxbuffer =NULL;
    CVReturn status =CVPixelBufferCreate(kCFAllocatorDefault,size.width,size.height,kCVPixelFormatType_32ARGB,(__bridge CFDictionaryRef) options,&pxbuffer);
    
    NSParameterAssert(status ==kCVReturnSuccess && pxbuffer !=NULL);
    
    CVPixelBufferLockBaseAddress(pxbuffer,0);
    
    void *pxdata =CVPixelBufferGetBaseAddress(pxbuffer);
    NSParameterAssert(pxdata !=NULL);
    CGColorSpaceRef rgbColorSpace=CGColorSpaceCreateDeviceRGB();
    //    當你呼叫這個函式的時候,Quartz建立一個位圖繪製環境,也就是點陣圖上下文。當你向上下文中繪製資訊時,Quartz把你要繪製的資訊作為點陣圖資料繪製到指定的記憶體塊。一個新的點陣圖上下文的畫素格式由三個引數決定:每個元件的位數,顏色空間,alpha選項
    CGContextRef context =CGBitmapContextCreate(pxdata,size.width,size.height,8,4*size.width,rgbColorSpace,kCGImageAlphaPremultipliedFirst);
    NSParameterAssert(context);
    
    //使用CGContextDrawImage繪製圖片  這裡設定不正確的話 會導致視訊顛倒
    //    當通過CGContextDrawImage繪製圖片到一個context中時,如果傳入的是UIImage的CGImageRef,因為UIKit和CG座標系y軸相反,所以圖片繪製將會上下顛倒
    CGContextDrawImage(context,CGRectMake(0,0,CGImageGetWidth(image),CGImageGetHeight(image)), image);
    // 釋放色彩空間
    CGColorSpaceRelease(rgbColorSpace);
    // 釋放context
    CGContextRelease(context);
    // 解鎖pixel buffer
    CVPixelBufferUnlockBaseAddress(pxbuffer,0);
    
    return pxbuffer;
}

再添加個C方法:

extern "C" {
    void _GJC_CombineImageToVideo(char* mainPath, char* videoName, int count) {
        NSString *savePath = [GJC_DataConvertor charToNSString:mainPath];
        NSString *saveName = [GJC_DataConvertor charToNSString:videoName];
        [[GJCSocialShare sharedInstance] combineImageToVideo2:savePath byName:saveName imageCount:count];
    }
}

Unity新增方法:

[DllImport ("__Internal")]
	private static extern void _GJC_CombineImageToVideo(string savePath, string videoName, int count);

封裝的方法:

//視訊圖片臨時存放位置
	public static string filePath{
		get{
			return Application.persistentDataPath + "/TempVideo";
		}
	}
	public static string videoPath{
		get{
			return GJCNativeShare.filePath + "/" + videoName;
		}
	}
	public static string videoName = "outputVideo.mov";
	/// <summary>
	/// 需要將圖片儲存在filePath資料夾下,然後命名為 i + tv.png,從序號0開始
	/// 圖片的尺寸為 256x256
	/// 最終在檔案目錄下得到一個 outputVideo.mov 的檔案,這個就是我們需要的視訊檔案了,可以將這個檔案的地址分享出去
	/// 或者儲存到相簿中去
	/// </summary>
	/// <param name="count"></param>
	public void CombineImageToVideo(string videoname, int count){
		// Debug.Log("Native Combine Image to video");
		#if UNITY_IPHONE && !UNITY_EDITOR
			_GJC_CombineImageToVideo(filePath + "/", videoname, count);
		#endif
	}

添加回調:

private void OnCreateVideoFailed(string result){
		// Debug.Log("OnCreateVideoFailed: " + result);
		if (onCreateVideo != null){
			onCreateVideo("Failed");
		}
	}
	private void OnCreateVideoWork(string result){
		// Debug.Log("OnCreateVideoWork: " + result);
		if (onCreateVideo != null){
			onCreateVideo("Work");
		}
	}
	private void OnCreateVideoSuccess(string result){
		// Debug.Log("OnCreateVideoSuccess: " + result);
		if (onCreateVideo != null){
			onCreateVideo("Success");
		}
	}

外部呼叫:

/// <summary>
	/// 將固定路徑下的圖片,合成視訊
	/// </summary>
	/// <param name="count">傳遞圖片數量</param>
	/// <param name="cb">回撥函式</param>
	public void CreateVideo(string videoName, int count, OnNativeCallback cb){
		createVideoCB = cb;
		if (Application.platform == RuntimePlatform.IPhonePlayer){
			GJCNativeShare.Instance.CombineImageToVideo(videoName, count);
		}else{
			OnCreateVideo("Success");
		}
	}

上面GJCNativeShare是我寫的接入類,可以參考上面的連結。

我這個是設定的1秒12幀,具體需要多少,可以在程式碼裡面找到這句話

[adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, 12)]

如果需要在某一幀停留長一些,可以將這個圖片重複生成一張,插在序列中,這樣就能達到目的。我這裡的實現方式,是在Unity端先把圖片生成好,儲存在一個路徑下面,我是儲存在 Application.persistentDataPath 的一個臨時資料夾裡面,圖片的名字按照規定好的形式命名,然後將路徑和圖片的數量傳遞到iOS端,這邊將圖片檔案的讀取並且經過合成之後,返回結果(兩邊儲存的視訊的名字我這裡也是寫死的“outputVideo.mov”,可以根據需要,在傳遞引數到iOS的時候可以增加一個,我是覺得臨時弄的,就沒加了,看個人需求了)

之後在Unity這邊就能拿到這個視訊了,可以通過video play載入展現出來,也可以將這個本地視訊分享出去。至於分享,如果是利用的原生彈框,只需要將這個視訊的本地路徑傳遞進去就好了。如果想分享到Instagram上,可以看我的另一個文章,可以直接分享到Instagram的編輯狀態,很好用,傳送:分享到Instagram

--------------底線-------------

相關推薦

Unity3D-iOS建立本地視訊

在遊戲中,有時候需要將一些簡單的圖片組合成視訊,然後讓使用者分享出去,當然僅僅是那種很簡單的圖片組合視訊(這是我自己專案的需求,不需要錄製UI等其他東西)。本來想找外掛的,後面看到大部分外掛都是通過攝像機去錄屏,但是我們專案本來就一個對圖片操作的遊戲,因此沒法用外掛。 尋思

iOS-播放本地視訊

開張第一篇,以前總是伸手黨,這次就來試下。 這次要寫的是iOS播放本地視訊,內容不多,就簡單幾行,就是呼叫了系統的視訊播放。 首先匯入框架 MediaPlayer.framework , 然後標頭檔案,並設定了一個property #import <MediaPlay

iOS 超好用的本地視訊播放器推薦!

本地播放器作為日常生活中不可或缺的一款工具類APP,Windows、Android等平臺不乏一些功能與體驗兼優的產品,但 iOS 平臺的使用者就沒有那麼幸運了,優秀的產品鳳毛麟角,且多數收費。 這源於 iOS 平臺的特殊性,完美支援各種視訊格式並不容易,幾乎沒有大廠在這方面投入,QQ影音

iOS簡單直播實現(一:建立本地rtmp伺服器)Mac上搭建直播伺服器Nginx+rtmp

簡介 nginx是非常優秀的開源伺服器,用它來做hls或者rtmp流媒體伺服器是非常不錯的選擇,本人在網上整理了安裝流程,分享給大家並且作備忘。 步驟安裝 Homebrew簡稱brew,是Mac OSX上的軟體包管理工具,能在Mac中方便的安裝軟體或者解除安裝軟體

iOS iTunes匯入本地視訊(檔案共享)

完成效果: 用iTunes向app匯入視訊後,不用手動重新整理,編寫的工具類會實時監聽複製狀態,複製完成後會自動重新整理UI. 大坑:因為檔案共享是共享的Document資料夾下所有資源,所以你需要把非共享的檔案,如:資料庫檔案、快取檔案等,存放到除

iOS 使用GPUImage為本地視訊新增濾鏡

這裡介紹使用GPUImage為本地視訊新增濾鏡,下面會對GPUImage使用過程進行介紹並說明一些需要注意的東西: 工程中需要的檔案可以到文章最後連結下載 首先把gpuimage.a和放置GPUImage標頭檔案的資料夾usr新增到專案中 然後需要在inf

iOS開發--本地通知與遠程通知

授權 atom nbsp sel bject 面試 tar 生效 nat iOS開發--本地通知與遠程通知 作者 雷潮 關註 2016.02.01 00:18* 字數 1921 閱讀 8898評論 1喜歡 41 這裏是指推送通知跟NSNotification有區別

iOS 圖片本地存儲、本地獲取、本地刪除

remove fff 地圖 ltm nts document pat manager file 在iOS開發中、經常用到圖片的本地化。 iOS 圖片本地存儲、本地獲取、本地刪除,可以通過以下類方法實現。 p.p1 { margin: 0.0px 0.0px 0.0px

iOS開發本地緩存(數據離線緩存、讀取、釋放)

ssa stat ati 同時 eal sar search elf ems 為了節約流量,同時也是為了更好的用戶體驗,目前很多應用都使用本地緩存機制,其中以網易新聞的緩存功能最為出色。我自己的應用也想加入本地緩存的功能,於是我從網上查閱了相關的資料,發現總體上說有兩種方法

Ubuntu16.04建立本地更新源

multi -m ubuntu 其中 ack 64位 自動 sudo ip地址 公司有多臺Ubuntu機器,而且不能連接互聯網,導致安裝軟件和更新都比較麻煩,需要建立一臺本地更新源服務器。 1.安裝apt-mirror工具 sudo apt-get install -y

CentOS7如何建立本地阿裏yum源,並安裝htop文件?

repos solved ble nsa depend AR centos lang base 版本centos 7.01、[root@localhost yum.repos.d]# mv CentOS-Base.repo CentOS-Base.repo.bak 將原文件

json-server+faker模組批量建立本地模擬資料

JSON-Server 簡單來說,JSON-Server是一個Node模組,執行Express伺服器,你可以指定一個json檔案作為api的資料來源。 只用json-server可以在很短的時間內搭建一個Rest API, 讓前端在不依賴後端的情況下進行開發。 全域性安裝JSO

手機本地視訊怎麼投屏到電腦

我們經常會在手機上錄製一些好玩的視訊,但是有時候在手機看的不是很舒服,於是想在電腦上檢視,那麼手機怎麼將本地的視訊投屏到電腦上呢?其實很簡單,有款迅捷錄屏大師便能夠輕鬆做到了。下面小編便來分享投屏的具體方法給大家,希望能對大家有所幫助。   迅捷錄屏大師http://www.xunjiesh

簡單直播實現(一:建立本地rtmp伺服器)Mac上搭建直播伺服器Nginx+rtmp

簡介 nginx是非常優秀的開源伺服器,用它來做hls或者rtmp流媒體伺服器是非常不錯的選擇,本人在網上整理了安裝流程,分享給大家並且作備忘。 步驟安裝 1、安裝Homebrow Homebrew簡稱brew,是Mac OSX上的軟體包管理工具,能在Mac中方便的安裝軟體

Git學習系列3 建立本地版本庫及新增檔案

在上一節 Git學習系列2 初配置及結構 完成之後,本節學習建立版本庫及新增檔案。 本節的演示環境為windows(10)系統。 一 選擇建立的路徑 選擇一個沒有中文的路徑。我的選擇的路徑為F:\github\git-learn. 在開始選單中啟動Git Ba

ionic 本地視訊上傳與預覽圖擷取上傳

input file 觸發 <input type="file" accept="video/avi,video/mp4,video/flv,video/3gp,video/swf" onchange="angular.element(this).scope().onFileChange

利用nodeJs 建立本地伺服器環境-加手機訪問本地專案

本地啟個伺服器,方便呼叫帶資料的測試 1首先去nodeJs官網下載最新版nodeJs     https://nodejs.org/en/ 2安裝成功後win+r開啟cmd 輸入node -help 或者node -v檢視是否安裝成功  3裝好後輸入 npm

Git建立本地分支並關聯遠端分支(二)

建立本地分支git branch 分支名 例如:git branch dev,這條命令是基於當前分支建立的本地分支,假設當前分支是master(遠端分支),則是基於master分支建立的本地分支dev。 切換到本地分支git checkout 分支名 例如:git checkout dev,這條命令表示

ios建立單例中的@synchronized和dispatch_once

@synchronized和dispatch_once 在單例的使用如下: static LvSingleClass * singleClass = nil; + (LvSingleClass *)sharedSingleClass { static dispatch_once

iOS調取本地相簿更換頭像

把調取相簿獲取圖片與controller分離開,寫成一個單獨的類,在這裡命名為NOVSelectPhotoManager .h檔案中 //宣告一個協議NOVSelectPhotoManagerDeleagte @protocol NOVSelectPhotoManagerDeleagte <