1. 程式人生 > >ffmpeg的那點小事兒--ffmpeg的匯入和視訊解碼,YUV儲存(ffmpeg4.0.2)

ffmpeg的那點小事兒--ffmpeg的匯入和視訊解碼,YUV儲存(ffmpeg4.0.2)

一、ffmpeg開發的基本知識瞭解

       第一點:一個視訊播放流程

             通常看到視訊格式:mp4、mov、flv、wmv等等…

             稱之為:封裝格式

             

    第二點:視訊播放器

兩種模式播放器

        第一種:視覺化介面播放器(直接使用者直觀操作->簡單易懂)

        e.g:騰訊視訊、愛奇藝視訊、QQ影音、暴風影音、快播、優酷等等…

        第二種:非視覺化介面播放器->命令操作播放器->使用者看不懂,使用起來非常麻煩

        e.g:FFmpeg->ffplay(命令)播放器(內建播放器)

               vlc播放器、mplayer播放器

        第三點:播放器資訊檢視工具

                整個視訊資訊:MediaInfo工具->幫助我們檢視視訊完整資訊

                二進位制檢視資訊:直接檢視視訊二進位制資料(0101010)->UItraEdit

                視訊單項資訊

                封裝格式資訊工具->Elecard Format Analyzer

                視訊編碼資訊工具->Elecard Stream Eye

                視訊畫素資訊工具->YUVPlayer

                音訊取樣資料工具->Adobe Audition

         第四點:音視訊->封裝格式

1、封裝格式:mp4、mov、flv、wmv等等…

2、封裝格式作用

視訊流+音訊流按照格式進行儲存在一個檔案中

3、MPEG2-TS格式

視訊壓縮資料格式:MPEG2-TS

特定:資料排版,不包含標頭檔案,資料大小固定(188byte)的TS-Packet

4、FLV格式

優勢:由於它形成的檔案極小、載入速度極快,使得網路觀看視訊檔案成為可能,它的出現有效地解決了視訊檔案匯入Flash後,使匯出的SWF檔案體積龐大,不能在網路上很好的使用等問題。

  檔案結構:FLV是一個二進位制檔案,由檔案頭(FLV header)和很多tag組成。tag又可以分成三類:audio,video,script,分別代表音訊流視訊流,指令碼流(關鍵字或者檔案資訊之類)。

FLV檔案=FLV標頭檔案+ tag1+tag內容1 + tag2+tag內容2 + ...+... + tagN+tag內容N。

FLV標頭檔案:(9位元組)

1-3個位元組: 前3個位元組是檔案格式標識(FLV 0x46 0x4C 0x56).

第4個位元組: 第4個位元組是版本(0x01)

第5個位元組: 第5個位元組的前5個bit是保留的必須是0.

6-9個位元組: 第6-9的四個位元組還是保留的.其資料為 00000009 .

整個檔案頭的長度,一般是9(3+1+1+4)

第五點:視訊編碼資料瞭解一下

1、視訊編碼作用

將視訊畫素資料(YUV、RGB)進行壓縮成為視訊碼流,從而降低視訊資料量。(減小記憶體暫用)

2、視訊編碼格式有哪些

MJPEG

MJPEG(Motion JPEG)壓縮技術,主要是基於靜態視訊壓縮發展起來的技術,它的主要特點是基本不考慮視訊流中不同幀之間的變化,只單獨對某一幀進行壓縮。

MJPEG壓縮技術可以獲取清晰度很高的視訊影象,可以動態調整幀率、解析度。但由於沒有考慮到幀間變化,造成大量冗餘資訊被重複儲存,因此單幀視訊的佔用空間較大,流行的MJPEG技術監控與視訊編碼最好的也只能做到3K位元組/幀,通常要8~20K!

MPEG-1/2

MPEG-1標準主要針對SIF標準解析度(NTSC製為352X240;PAL製為352X288)的影象進行壓縮. 壓縮位率主要目標為1.5Mb/s.較MJPEG技術,MPEG1在實時壓縮、每幀資料量、處理速度上有顯著的提高。但MPEG1也有較多不利地方:儲存容量還是過大、清晰度不夠高和網路傳輸困難。

MPEG-2 在MPEG-1基礎上進行了擴充和提升,和MPEG-1向下相容,主要針對儲存媒體、數字電視、高清晰等應用領域,解析度為:低(352x288),中(720x480),次高(1440x1080),高(1920x1080)。MPEG-2視訊相對MPEG-1提升了解析度,滿足了使用者高清晰的要求,但由於壓縮效能沒有多少提高,使得儲存容量還是太大,也不適合網路傳輸

MPEG-4

MPEG-4視訊壓縮演算法相對於MPEG-1/2在低位元率壓縮上有著顯著提高,在CIF(352*288)或者更高清晰度(768*576)情況下的視訊壓縮,無論從清晰度還是從儲存量上都比MPEG1具有更大的優勢,也更適合網路傳輸。另外MPEG-4可以方便地動態調整幀率、位元率,以降低儲存量

MPEG-4由於系統設計過於複雜,使得MPEG-4難以完全實現並且相容,很難在視訊會議、可視電話等領域實現,這一點有點偏離原來地初衷。另外對於中國企業來說還要面臨高昂的專利費問題,規定:

- 每臺解碼裝置需要交給MPEG-LA 0.25美元。

編碼/解碼裝置還需要按時間交費(4美分/天=1.2美元/月 =14.4美元/年)。

H.264/AVC

視訊壓縮國際標準主要有由ITU-T制定的H.261、H.262、H.263、H.264和由MPEG制定的MPEG-1、MPEG-2、MPEG-4,其中H.262/MPEG-2和H.264/MPEG-4 AVC由ITU-T與MPEG聯合制定。

從簡單來說H.264就是一種視訊編碼技術,與微軟的WMV9都屬於同一種技術也就是壓縮動態影象資料的“編解碼器”程式。

3、H.264視訊壓縮資料格式

非常複雜演算法->壓縮->佔用記憶體那麼少?(例如:幀間預測、幀內預測…)->提高壓縮效能

第六點:音訊編碼資料?

1、音訊編碼作用?

將音訊取樣資料(PCM格式)進行壓縮成為音訊碼流,從而降低音訊資料量。(減小記憶體暫用)

2、音訊編碼飛逝有哪些

AAC、MP3、WMV等等…

3、AAC格式

AAC,全稱Advanced Audio Coding,是一種專為聲音資料設計的檔案壓縮格式。與MP3不同,它採用了全新的演算法進行編碼,更加高效,具有更高的“價效比”。利用AAC格式,可使人感覺聲音質量沒有明顯降低的前提下,更加小巧。蘋果ipod諾基亞手機支援AAC格式的音訊檔案。

優點:相對於mp3,AAC格式的音質更佳,檔案更小。

不足:AAC屬於有失真壓縮的格式,與時下流行的APEFLAC等無損格式相比音質存在“本質上”的差距。加之,傳輸速度更快的USB3.0和16G以上大容量MP3正在加速普及,也使得AAC頭上“小巧”的光環不復存在。

①提升的壓縮率:可以以更小的檔案大小獲得更高的音質;

②支援多聲道:可提供最多48個全音域聲道;

③更高的解析度:最高支援96KHz的取樣頻率;

④提升的解碼效率:解碼播放所佔的資源更少;

第七點:視訊畫素資料

1、作用

儲存了螢幕上面每一個畫素點的值

2、視訊畫素資料格式種類?

常見格式:RGB24、RGB32、YUV420P、YUV422P、YUV444P等等…一般最常見:YUV420P

3、視訊畫素資料檔案大小計算?

例如:RGB24高清視訊體積?(1個小時時長)

體積:3600 * 25 * 1920 * 1080 * 3 = 559GB(非常大)

假設:幀率25HZ,取樣精度8bit,3個位元組

4、YUV播放器

人類:對色度不敏感,對亮度敏感

Y表示:亮度

UV表示:色度

第八點:音訊取樣資料格式

1、作用

儲存了音訊中的每一個取樣點值

2、音訊取樣資料檔案大小計算?

例如:1分鐘PCM格式歌曲

體積:60 * 44100 * 2 * 2 = 11MB

分析:60表示時間,44100表示取樣率(一般情況下,都是這個取樣率,人的耳朵能夠分辨的聲音),2表示聲道數量,2表示取樣精度16位 = 2位元組

3、音訊取樣資料檢視工具

4、PCM格式

儲存順序?

第九點:FFmepg應用?

提供了一套比較完整程式碼->開源免費

核心架構設計思想:(核心 + 外掛)設計

重要命令學習

1、ffmpeg.exe(視訊壓縮->轉碼來完成)

作用:用於對視訊進行轉碼

將mp4->mov,mov->mp4,wmv->mp4等等…

命令格式:ffmpeg -i {指定輸入檔案路徑} -b:v {輸出視訊位元速率} {輸出檔案路徑}

測試執行:將Test.mov->Test.mp4

時間格式:如何指定

擷取視訊: ./ffmpeg -i Test.mov -ss 00:00:05 -t 00:00:10 sub.mov

從視訊的第5秒開始擷取,然後往後10秒結束,這個視訊長度為10秒

2、ffplay.exe

作用:播放視訊

格式:ffplay {檔案路徑}

例如:./ffplay Test.mov

案例:視訊,轉為高質量 GIF 動圖?

命令:./ffmpeg -ss 00:00:03 -t 3 -i Test.mov -s 640x360 -r “15” dongtu.gif

解釋:

1、ffmpeg 是你剛才安裝的程式;

2、-ss 00:00:03 表示從第 00 分鐘 03 秒開始製作 GIF,如果你想從第 9 秒開始,則輸入 -ss 00:00:09,或者 -ss 9,支援小數點,所以也可以輸入 -ss 00:00:11.3,或者 -ss 34.6 之類的,如果不加該命令,則從 0 秒開始製作;

3、-t 3 表示把持續 3 秒的視訊轉換為 GIF,你可以把它改為其他數字,例如 1.5,7 等等,時間越長,GIF 體積越大,如果不加該命令,則把整個視訊轉為 GIF;

4、-i 表示 invert 的意思吧,轉換;

5、Test.mov 就是你要轉換的視訊,名稱最好不要有中文,不要留空格,支援多種視訊格式;

6、-s 640x360 是 GIF 的解析度,視訊解析度可能是 1080p,但你製作的 GIF 可以轉為 720p 等,允許自定義,解析度越高體積越大,如果不加該命令,則保持解析度不變;

7、-r “15” 表示幀率,網上下載的視訊幀率通常為 24,設為 15 效果挺好了,幀率越高體積越大,如果不加該命令,則保持幀率不變;

8、dongtu.gif:就是你要輸出的檔案,你也可以把它命名為 hello.gif 等等。

二、將ffmpeg匯入到工程裡面

1.建立一個工程

2.建立ffmpeg匯入到工程

新增依賴庫,這裡的依賴庫都是多媒體相關的:CoreMedia.framework, VideoToolbox.framework, AudioToolbox.framework, CoreGraphics.framework, libz.tbd, libiconv.tbd, libbz2.tbd

配置Header Search Paths 路徑,這個路徑可以將Library Search Paths:將ffmpeg.a庫檔案lib資料夾路徑加進去,只需將lib改為存放標頭檔案的資料夾即可

三、測試ffmpeg的可行性

這裡用三個方法去測試

建立一個AVAFFmpegConfigure類

.h檔案程式碼如下

//
//  AVAFFmpegConfigure.h
//  Avalanching_FFmpeg_Project
//
//  Created by Avalanching on 2018/9/12.
//  Copyright © 2018年 Avalanching. All rights reserved.
//

#import <Foundation/Foundation.h>
// 匯入標頭檔案
#import <libavcodec/avcodec.h>
// 匯入封裝格式庫
#import <libavformat/avformat.h>
// 工具包
#import <libavutil/imgutils.h>
// 視訊畫素格式哭
#import <libswscale/swscale.h>

@interface AVAFFmpegConfigure : NSObject

+ (void)ffmpegConfigurationTest;

+ (void)ffmpegOpenVideoFile:(NSString *)filePath;

+ (void)ffmpegDecodecWithFilePath:(NSString *)filepath;

@end

.m檔案程式碼如下

//
//  AVAFFmpegConfigure.m
//  Avalanching_FFmpeg_Project
//
//  Created by Avalanching on 2018/9/12.
//  Copyright © 2018年 Avalanching. All rights reserved.
//

#import "AVAFFmpegConfigure.h"

@implementation AVAFFmpegConfigure

+ (void)ffmpegConfigurationTest {
    const char *configuration = avcodec_configuration();
    NSLog(@"%s", configuration);
}

+ (void)ffmpegOpenVideoFile:(NSString *)filePath {
    // 第一步註冊組建
    // 舊的介面:av_register_all();
    // 這不需要註冊組建
    // 4.0.0 API大量更換,讓很多初學者很難去學習
    // 詳細介面替換可以檢視下載的原始碼目錄下的doc/APIChange 這個檔案
    // 具體的用法就需要檢視方法註釋
    
    // 第二步開啟封裝格式檔案
    // param1:封裝格式的上下文
    AVFormatContext *formatContext = avformat_alloc_context();
    // param2:視訊檔案地址
    const char *url = [filePath UTF8String];
    // param3:指定輸入的封裝格式
    // param4:指定預設配置資訊
    
    int result = avformat_open_input(&formatContext, url, NULL, NULL);
    if (result != 0) {
        // 這裡是開啟失敗
        NSLog(@"open false!!");
    } else {
        NSLog(@"open success!!");
    }
}

+ (void)ffmpegDecodecWithFilePath:(NSString *)filepath {
    // 第一步:組冊元件
    // av_register_all() 4.0.0之前的介面
    // 新版的API,此處不需要註冊組建
    // 例如:編碼器、解碼器等等…
    
    // 第二步:開啟封裝格式->開啟檔案
    // 例如:.mp4、.mov、.wmv檔案等等...
    // avformat_open_input();
    AVFormatContext *formatContext = avformat_alloc_context();
    const char *url = [filepath UTF8String];
    int avformat_result = avformat_open_input(&formatContext, url, NULL, NULL);
    
    if (avformat_result != 0) {
        NSLog(@"open false");
        return;
    }
    
    // 第三步:查詢視訊流
    // 如果是視訊解碼,那麼查詢視訊流,如果是音訊解碼,那麼就查詢音訊流
    // avformat_find_stream_info();
    // AVProgram 視訊的相關資訊
    int avformat_find_stream_result = avformat_find_stream_info(formatContext, NULL);
    if (avformat_find_stream_result < 0) {
        NSLog(@"search video stream is false!!!");
        return;
    }
    
    // 第四步:查詢視訊解碼器
    // 1、查詢視訊流索引位置
    int avformat_stream_index = 0;
    for (int i = 0; i < formatContext->nb_streams; i++) {
        // 判斷流的型別
        // 舊的介面 formatContext->streams[i]->codec->codec_type
        // 4.0.0以後新加入的型別用於替代codec
        // codec -> codecpar
        enum AVMediaType mediatype = formatContext->streams[i]->codecpar->codec_type;
        if (mediatype == AVMEDIA_TYPE_VIDEO) {
            // 視訊流
            avformat_stream_index = i;
            break;
        } else if (mediatype == AVMEDIA_TYPE_AUDIO) {
            // 音訊流
            
        } else {
            // 其他流
        }
    }
    // 2、根據視訊流索引,獲取解碼器上下文
    // 舊的介面 拿到上下文,formatContext->streams[i]->codec
    // 4.0.0以後新加入的型別用於替代codec
    // codec -> codecpar 此處新介面不需要上下文
    AVCodecParameters *avcodecParameters = formatContext->streams[avformat_stream_index]->codecpar;
    enum AVCodecID codeid = avcodecParameters->codec_id;
    
    // 3、根據解碼器上下文,獲得解碼器ID,然後查詢解碼器
    // avcodec_find_encoder(enum AVCodecID id) 編碼器
    AVCodec *codec = avcodec_find_decoder(codeid);
    
    // 第五步:開啟解碼器
    // avcodec_open2();
    // 舊介面直接使用codec作為上下文傳入
    // formatContext->streams[avformat_stream_index]->codec被遺棄
    // 新介面如下
    AVCodecContext *avCodecContext = avcodec_alloc_context3(NULL);
    if (avCodecContext == NULL) {
        // 建立解碼器上下文失敗
        NSLog(@"create codecContext is false");
        return;
    }
    // avcodec_parameters_to_context(AVCodecContext *codec, const AVCodecParameters *par)
    // 將新的API中的 codecpar 轉成 AVCodecContext
    avcodec_parameters_to_context(avCodecContext, avcodecParameters);
    
    int avcodec_open2_result = avcodec_open2(avCodecContext, codec, NULL);
    if (avcodec_open2_result != 0) {
        NSLog(@"open decodec is false");
        return;
    }
    NSLog(@"decodec name: %s", codec->name);
    
    // 第六步:讀取視訊壓縮資料->迴圈讀取
    // av_read_frame(AVFormatContext *s, AVPacket *pkt)
    // s: 封裝格式上下文
    // pkt: 一幀的壓縮資料
    AVPacket *avpacket = (AVPacket *)av_mallocz(sizeof(AVPacket));
    
    // 用於存放解碼之後的畫素資料
    AVFrame *avFrame_in = av_frame_alloc();
    int avcodec_receive_frame_result = 0;
    // sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param)
    // 原始資料
    // scrW: 原始格式寬度
    // scrH: 原始格式高度
    // scrFormat: 原始資料格式
    // 目標資料
    // dstW: 目標格式寬度
    // dstH: 目標格式高度
    // dstFormat: 目標資料格式
    // 當遇到Assertion desc failed at src/libswscale/swscale_internal.h:668
    // 這個問題就是獲取元資料的高度有問題
    struct SwsContext *swsContext = sws_getContext(avcodecParameters->width,
                                                   avcodecParameters->height,
                                                   avCodecContext->pix_fmt,
                                                   avcodecParameters->width,
                                                   avcodecParameters->height,
                                                   AV_PIX_FMT_YUV420P,
                                                   SWS_BITEXACT, NULL, NULL, NULL);
    
    // 建立緩衝區
    //建立一個yuv420視訊畫素資料格式緩衝區(一幀資料)
    AVFrame* avframe_yuv420p = av_frame_alloc();
    //給緩衝區設定型別->yuv420型別
    //得到YUV420P緩衝區大小
    // av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align)
    //pix_fmt: 視訊畫素資料格式型別->YUV420P格式
    //width: 一幀視訊畫素資料寬 = 視訊寬
    //height: 一幀視訊畫素資料高 = 視訊高
    //align: 位元組對齊方式->預設是1
    int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
                                               avCodecContext->width,
                                               avCodecContext->height,
                                               1);
    //  開闢一塊記憶體空間
    uint8_t *out_buffer = (uint8_t *)av_malloc(buffer_size);
    //  向avframe_yuv420p->填充資料
    // av_image_fill_arrays(uint8_t **dst_data, int *dst_linesize, const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align)
    //dst_data: 目標->填充資料(avframe_yuv420p)
    //dst_linesize: 目標->每一行大小
    //src: 原始資料
    //pix_fmt: 目標->格式型別
    //width: 寬
    //height: 高
    //align: 位元組對齊方式
    av_image_fill_arrays(avframe_yuv420p->data,
                         avframe_yuv420p->linesize,
                         out_buffer,
                         AV_PIX_FMT_YUV420P,
                         avCodecContext->width,
                         avCodecContext->height,
                         1);
    
    int y_size, u_size, v_size;
    
    //5.2 將yuv420p資料寫入.yuv檔案中
    //開啟寫入檔案
    //獲得應用程式沙盒的Documents目錄
    NSString* doc_path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    //建立檔案管理器物件
    NSString* filename = [doc_path stringByAppendingPathComponent:@"output.yuv"];
    //建立目錄
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ( ![fileManager fileExistsAtPath:filename]) {
        [fileManager createFileAtPath:filename contents:nil attributes:nil];
    }
    
    const char *fileYUV = [filename UTF8String];
    /*
     * fopen的引數rb+和wb+都是操作可讀可寫的二進位制檔案
     區別是
     對於rb+如果開啟的檔案不存在 會報錯(返回NULL)
     對於wb+ 如果檔案不存在則會建立,如果檔案存在 會覆蓋
     */
    FILE* file_yuv420p = fopen(fileYUV,"rb+"); //
    
    if (file_yuv420p == NULL) {
        NSLog(@"file open is false");
    }
    
    int current_index = 0;

    while (av_read_frame(formatContext, avpacket) >= 0) {
        
        // 判斷是不是視訊
        if (avpacket->stream_index == avformat_stream_index) {
            // 每讀取一幀資料,立馬解碼一幀資料
            // 解碼之後得到視訊的畫素資料->YUV
            // avcodec_send_packet(AVCodecContext *avctx, AVPacket *pkt)
            // avctx: 解碼器上下文
            // pkt: 獲取到資料包
            // 獲取一幀資料
            avcodec_send_packet(avCodecContext, avpacket);
            
            // 解碼
            avcodec_receive_frame_result = avcodec_receive_frame(avCodecContext, avFrame_in);
            if (avcodec_receive_frame_result == 0) {
                // 解碼成功
                // 此處無法保證視訊的畫素格式是一定是YUV格式
                // 將解碼出來的這一幀資料,統一轉型別為YUV
                // sws_scale(struct SwsContext *c, const uint8_t *const *srcSlice, const int *srcStride, int srcSliceY, int srcSliceH, uint8_t *const *dst, const int *dstStride)
                // SwsContext *c: 視訊畫素格式的上下文
                // srcSlice: 原始視訊輸入資料
                // srcStride: 原資料每一行的大小
                // srcSliceY: 輸入畫面的開始位置,一般從0開始
                // srcSliceH: 原始資料的長度
                // dst: 輸出的視訊格式
                // dstStride: 輸出的畫面大小
                sws_scale(swsContext, 
                          (const uint8_t *const *)avFrame_in->data, 
                          avFrame_in->linesize, 
                          0, 
                          avCodecContext->height,
                          avframe_yuv420p->data, 
                          avframe_yuv420p->linesize);
                
                //方式一:直接顯示視訊上面去
                //方式二:寫入yuv檔案格式
                //5、將yuv420p資料寫入.yuv檔案中
                //5.1 計算YUV大小
                //分析一下原理?
                //Y表示:亮度
                //UV表示:色度
                //有規律
                //YUV420P格式規範一:Y結構表示一個畫素(一個畫素對應一個Y)
                //YUV420P格式規範二:4個畫素點對應一個(U和V: 4Y = U = V)
                y_size = avCodecContext->width * avCodecContext->height;
                u_size = y_size / 4;
                v_size = y_size / 4;
                //5.2 寫入.yuv檔案
                //首先->Y資料
                fwrite(avframe_yuv420p->data[0], 1, y_size, file_yuv420p);
                //其次->U資料
                fwrite(avframe_yuv420p->data[1], 1, u_size, file_yuv420p);
                //再其次->V資料
                fwrite(avframe_yuv420p->data[2], 1, v_size, file_yuv420p);
                current_index++;
                NSLog(@"當前解碼%d幀", current_index);
            }
        }
        
    }
    // 第八步:關閉解碼器->解碼完成
    av_packet_free(&avpacket);
    fclose(file_yuv420p);
    av_frame_free(&avFrame_in);
    av_frame_free(&avframe_yuv420p);
    free(out_buffer);
    avcodec_close(avCodecContext);
    avformat_free_context(formatContext);
}

@end

總結: 

1、新的ffmpeg庫不需要集中初始化的組建

2、新的API中將AVStream結構體中codec作了遺棄處理,當需要解碼器上下文的時候,需要用AVCodecParameters去轉化,解決方案是如下:

AVCodecContext *avCodecContext = avcodec_alloc_context3(NULL);
if (avCodecContext == NULL) {
return;
}
// avcodec_parameters_to_context(AVCodecContext *codec, const AVCodecParameters *par)
// 將新的API中的 codecpar 轉成 AVCodecContext
// 此處的avcodecParameters 是AVStream中的codecpar屬性,codecpar裡面包含了視訊基礎資訊,
// 但是此處不能直接使用codecpar中的width和height,否者會報錯誤,錯誤如下
// Assertion desc failed at src/libswscale/swscale_internal.h:668
avcodec_parameters_to_context(avCodecContext, avcodecParameters);

3、av_mallocz(size_t size)動態記憶體空間,C/C++知識點,動態去請求記憶體空間,需要多少開闢多少。

4、處理結束需要按照使用的物件,結構體的層級關係去釋放記憶體。

5、儘量使用新的API