JPEG流封裝AVI視頻


前言:前幾天工作任務,要把JPEG流封裝為AVI視頻,就找了些AVI文件結構資料和示例代碼研究了下,現將學習總結及最終完成的可用代碼分享出來,由於本人也是現學現用,如有不恰當或錯誤之處,歡迎提出!

 

1  AVI文件結構

AVI采用RIFF文件結構方式,RIFF是微軟定義的一種用於管理windows環境中多媒體數據的文件格式,波形音頻wave、MIDI和數字視頻AVI都采用這種格式存儲,構造RIFF文件的基本單元叫做數據塊(Chunk),每個數據塊包含3個部分:

(1)4字節的數據塊標記(Chunk ID)

(2)4字節的數據塊大小

(3)數據

整個RIFF文件可以看成一個ID為RIFF的數據塊,RIFF塊包含一系列子塊,其中有一種子塊的ID為LIST,稱為LIST塊,LIST塊中可以再包含一系列子塊,但除了LIST塊的其他所有子塊都不能再包含子塊。

RIFF和LIST塊分別比普通的數據塊多一個被稱為形式類型(Form Type)和列表類型(List Type)的數據域,其組成如下:

(1)4字節的數據塊標記(Chunk ID)

(2)4字節的數據塊大小

(3)4字節的形式類型(對於RIFF塊)或列表類型(對於LIST塊)

(4)數據

 

AVI文件是最復雜的RIFF文件,它能夠同時存儲音頻和視頻數據(注:本文檔不涉及音頻相關內容,只針對視頻數據進行介紹),AVI文件RIFF塊的形式類型是AVI ,它包含以下3個子塊:

(1)信息塊,ID為hdrl的LIST塊,用於定義AVI文件的數據格式

(2)數據塊,ID為movi的LIST塊,用於存儲音視頻數據

(3)索引塊,ID為idxl的數據塊,用於定義音視頻數據的索引,是可選塊

 

AVI文件結構如圖1所示

                                    圖1  AVI文件結構

1.1 信息塊

信息塊包含兩個子塊:一個ID為avih的子塊和一個ID為strl的LIST塊。

1.1.1 avih塊

                                                  圖2  avih塊結構

avih塊可用如圖2所示的struct avi_avih_chunk結構體定義,圖中已對結構體各變量的含義進行了解釋,以下是對其中幾個變量的補充說明:

(1)max_bytes_per_sec

max_bytes_per_sec用於控制視頻的最大碼率,即每秒傳輸的最大數據量。但實際上,給這個變量賦值並不能影響視頻的碼率,原因如下:設JPEG流圖像總幀數為nframes,視頻幀率為fps,各幀圖像平均大小為len,則封裝的AVI視頻時長、文件大小和視頻碼率分別為

time = nframes / fps

video_size = nframes * len(實際大小還要加上文件頭和文件尾的數據)

rate = video_size / time = fps * len

由此可見,在固定的幀率fps下,視頻碼率完全取決於JPEG各幀圖像的大小,和max_bytes_per_sec的值沒有關系,所以這個變量設為0即可。

(2)flags

flags表示AVI文件的全局屬性,如是否含有索引塊、是否即有音頻數據又有視頻數據等,不進行任何標記時flags值為0,若含有索引塊,則flags值為0x00000010。

(3)init_frames

AVI文件若同時存儲了音頻和視頻數據,則音頻數據和視頻數據是交叉存儲的,init_frames僅在這種情況下使用,對於只有視頻流的情況,該變量的值為0。

(4)width、height

這里的width和height不是JPEG圖像的寬和高,而是用播放器打開AVI文件時視頻主窗口的寬和高,舉個例子,JPEG圖像大小為1920*1080,width和height分別設為960和540,用QQ影音打開AVI文件,則QQ影音會以960*540的窗口大小進行播放。

1.1.2 strl塊

strl塊由圖3所示的結構體定義,它包含strh和strf兩個子塊。

                                             圖3  strl塊結構

1、strh塊結構

                                                圖4  strh塊結構

圖4所示為strh塊結構定義,下面是對結構體內一些變量含義的補充說明:

(1)codec

codec是一個長度為4的字符數組,用於指定數據流的編碼格式,也就是播放器播放這個流時需要的解碼器,對於JPEG編碼的視頻流,codec數組內容就是'J', 'P', 'E', 'G',而不能隨意指定,否則播放器播放時會無法解碼。

(2)scale、rate

對於視頻流,rate除以scale等於視頻幀率,因此這兩個變量可賦值為scale = 1、rate = fps。

2、strf塊結構

strf塊結構根據strh塊中stream_type是視頻流還是音頻流而有所不同,對於視頻流,strf塊結構如圖5所示,其中bitcount表示每個圖像像素占的位數,其值根據視頻流的實際情況而定,但只能是1、4、8、16、24和32之一,常用的有1(黑白二值化圖像)、8(256階灰度圖)和24(RGB圖像)。

                                              圖5  針對視頻流的strf塊結構

1.2 數據塊

由圖1可知,數據塊是一個ID為movi的LIST列表,也稱為movi塊,在僅有視頻流時,該部分存儲的就是一幀一幀的圖像數據,圖6展示了視頻流movi塊的詳細結構。

                        圖6 僅有視頻流的movi塊結構

可以看到,movi塊首先是一個固定結構的LIST列表頭,包括塊ID、塊大小和塊類型,其中塊ID固定為LIST,塊類型固定為movi,塊大小為movi塊去掉開頭8字節后的大小。

然后是movi塊數據,也就是各幀視頻圖像對應的數據塊,每一幀圖像的數據塊都包含三部分:

(1)4字節ID:可以為00dc或00db,00dc表示壓縮的視頻數據,00db表示未壓縮的視頻數據,根據視頻流的實際情況來選擇賦值。

(2)4字節frame length:圖像數據長度(單位:字節),該長度必須是4的整數倍,如果不是,則需要將其修正到4的整數倍,比如frame length原始數據為99,則需將其加到100。

(3)frame data:真正的圖像數據。

1.3 索引塊

索引塊是AVI文件結構的可選部分,它是一個ID等於idxl的數據塊,索引塊提供了movi塊中存儲各幀圖像的數據塊在AVI文件中的位置索引,作用是提高AVI文件的讀寫速度,提高視頻播放時的體驗效果。

                                     圖7 索引塊結構

如圖7所示為索引塊結構,包括塊ID、塊大小和塊數據三部分,其中塊ID固定為idxl,塊大小等於索引塊數據的大小。

索引塊數據是movi塊中存儲各幀圖像數據塊的索引,每一幀圖像的索引都是一個16字節的數據結構,具體如下:

(1)4字節ChunkID:即movi塊各幀圖像數據塊的ID,00dc或00db

(2)4字節ChunkFlag:表示該幀圖像是否是關鍵幀,0x10代表關鍵幀,0x00代表非關鍵幀

(3)4字節ChunkOffset:圖像數據塊相對於“movi”標示符(圖6紅色箭頭所指處)的偏移量,由圖6可得,各幀圖像索引ChunkOffset的值為:

第一幀圖像索引àChunkOffset1 = 4;

第二幀圖像索引àChunkOffset2 = ChunkOffset1+8+第一幀圖像數據長度

第三幀圖像索引àChunkOffset3 = ChunkOffset2+8+第二幀圖像數據長度

…… (后面各幀圖像索引以此類推,其中各幀圖像數據長度指的是修正到4的整數倍后的長度)

(4)4字節ChunkLength:修正到4的整數倍后的各幀圖像數據長度

2  JPEG流封裝AVI步驟

JPEG流封裝AVI視頻的本質是按照AVI結構進行文件讀寫,操作流程大體上可分為三個步驟:

步驟1:創建空白AVI文件,設置文件偏移量到數據塊movi標示符后面

(1)創建AVI文件,以二進制寫方式打開

(2)計算文件偏移量offset,等於RIFF文件頭12字節 + hdrl塊大小 + movi LIST頭12字節

(3)設置AVI文件偏移量為offset

步驟2:從offset偏移量處開始,向AVI文件中逐幀寫入JPEG數據

(1)將當前JPEG圖像數據長度加到4的整數倍,用length表示

(2)JPEG圖像是壓縮過的圖像數據,故寫入'0', '0', 'd', 'c'

(3)寫入當前JPEG圖像數據長度length

(4)寫入當前JPEG圖像數據,寫入長度為length

(5)循環上述過程,完成逐幀圖像數據的寫入

步驟3:JPEG數據寫完后,先繼續向后寫索引塊,再定位到文件頭回填各塊數據

(1)寫索引塊

- 先寫塊ID  'i', 'd', 'x', 'l'

- 再寫塊大小 16 * nframes

- 最后寫各幀圖像的索引

(2)從文件頭開始,回填各塊數據

- 設置文件偏移量為0

- 按照AVI文件結構,寫入步驟1跳過的各塊數據

需要注意的是,步驟3寫索引塊時需要各幀圖像的數據長度和總幀數,回填各塊數據時也需要總幀數和所有幀的總大小,因此步驟2寫入JPEG數據時需要保存它們的值。

3  代碼分享

 我完成的代碼,是以若干張JPEG圖片作為JPEG流,先將圖片數據讀入內存,再寫入AVI文件,共包含五個文件:

1、list.h和list.c,雙向循環鏈表,作用是保存各幀圖像大小,用於寫索引塊

2、Jpeg2AVI.h和Jpeg2AVI.c,用於將JPEG流封裝為AVI視頻

3、main.c,測試程序

 

Jpeg2AVI.h

 1 #ifndef _JPEG2AVI_H_
 2 #define _JPEG2AVI_H_
 3 
 4 #include <stdio.h>
 5 
 6 void jpeg2avi_start(FILE *fp);
 7 void jpeg2avi_add_frame(FILE *fp, void *data, unsigned int len);
 8 void jpeg2avi_end(FILE *fp, int width, int height, int fps);
 9 
10 typedef struct avi_riff_head
11 {
12     unsigned char id[4];        
13     unsigned int size;           
14     unsigned char type[4];   
15 }AVI_RIFF_HEAD, AVI_LIST_HEAD;
16 
17 typedef struct avi_avih_chunk
18 {
19     unsigned char id[4];            //塊ID,固定為avih
20     unsigned int size;              //塊大小,等於struct avi_avih_chunk去掉id和size的大小
21     unsigned int us_per_frame;      //視頻幀間隔時間(以微秒為單位)
22     unsigned int max_bytes_per_sec; //AVI文件的最大數據率
23     unsigned int padding;           //設為0即可
24     unsigned int flags;             //AVI文件全局屬性,如是否含有索引塊、音視頻數據是否交叉存儲等
25     unsigned int total_frames;      //總幀數
26     unsigned int init_frames;       //為交互格式指定初始幀數(非交互格式應該指定為0)
27     unsigned int streams;           //文件包含的流的個數,僅有視頻流時為1
28     unsigned int suggest_buff_size; //指定讀取本文件建議使用的緩沖區大小,通常為存儲一楨圖像                                            //以及同步聲音所需的數據之和,不指定時設為0
29     unsigned int width;             //視頻主窗口寬度(單位:像素)
30     unsigned int height;            //視頻主窗口高度(單位:像素)
31     unsigned int reserved[4];       //保留段,設為0即可
32 }AVI_AVIH_CHUNK;
33 
34 typedef struct avi_rect_frame
35 {
36     short left;
37     short top;
38     short right;
39     short bottom;    
40 }AVI_RECT_FRAME;
41 
42 typedef struct avi_strh_chunk
43 {    
44     unsigned char id[4];            //塊ID,固定為strh
45     unsigned int size;              //塊大小,等於struct avi_strh_chunk去掉id和size的大小
46     unsigned char stream_type[4];   //流的類型,vids表示視頻流,auds表示音頻流
47     unsigned char codec[4];         //指定處理這個流需要的解碼器,如JPEG
48     unsigned int flags;             //標記,如是否允許這個流輸出、調色板是否變化等,一般設為0即可
49     unsigned short priority;        //流的優先級,視頻流設為0即可
50     unsigned short language;        //音頻語言代號,視頻流設為0即可
51     unsigned int init_frames;       //為交互格式指定初始幀數(非交互格式應該指定為0)
52     unsigned int scale;             //
53     unsigned int rate;              //對於視頻流,rate / scale = 幀率fps
54     unsigned int start;             //對於視頻流,設為0即可
55     unsigned int length;            //對於視頻流,length即總幀數
56     unsigned int suggest_buff_size; //讀取這個流數據建議使用的緩沖區大小
57     unsigned int quality;           //流數據的質量指標
58     unsigned int sample_size;       //音頻采樣大小,視頻流設為0即可
59     AVI_RECT_FRAME rcFrame;         //這個流在視頻主窗口中的顯示位置,設為{0,0,width,height}即可
60 }AVI_STRH_CHUNK;
61 
62 /*對於視頻流,strf塊結構如下*/
63 typedef struct avi_strf_chunk
64 {
65     unsigned char id[4];             //塊ID,固定為strf
66     unsigned int size;               //塊大小,等於struct avi_strf_chunk去掉id和size的大小
67     unsigned int size1;              //size1含義和值同size一樣
68     unsigned int width;              //視頻主窗口寬度(單位:像素)
69     unsigned int height;             //視頻主窗口高度(單位:像素)
70     unsigned short planes;           //始終為1  
71     unsigned short bitcount;         //每個像素占的位數,只能是1、4、8、16、24和32中的一個
72     unsigned char compression[4];    //視頻流編碼格式,如JPEG、MJPG等
73     unsigned int image_size;         //視頻圖像大小,等於width * height * bitcount / 8
74     unsigned int x_pixels_per_meter; //顯示設備的水平分辨率,設為0即可
75     unsigned int y_pixels_per_meter; //顯示設備的垂直分辨率,設為0即可
76     unsigned int num_colors;         //含義不清楚,設為0即可   
77     unsigned int imp_colors;         //含義不清楚,設為0即可
78 }AVI_STRF_CHUNK;
79 
80 typedef struct avi_strl_list
81 {
82     unsigned char id[4];    //塊ID,固定為LIST    
83     unsigned int size;      //塊大小,等於struct avi_strl_list去掉id和size的大小        
84     unsigned char type[4];  //塊類型,固定為strl
85     AVI_STRH_CHUNK strh;      
86     AVI_STRF_CHUNK strf;      
87 }AVI_STRL_LIST;
88 
89 typedef struct avi_hdrl_list
90 {
91     unsigned char id[4];    //塊ID,固定為LIST    
92     unsigned int size;      //塊大小,等於struct avi_hdrl_list去掉id和size的大小        
93     unsigned char type[4];  //塊類型,固定為hdrl
94     AVI_AVIH_CHUNK avih;
95     AVI_STRL_LIST  strl;
96 }AVI_HDRL_LIST;
97 
98 #endif
View Code

 

Jpeg2AVI.c

  1 #include "Jpeg2AVI.h"
  2 #include "list.h"
  3 #include <stdlib.h>
  4 #include <string.h>
  5 
  6 static int nframes;           //總幀數
  7 static int totalsize;         //幀的總大小
  8 static struct list_head list; //保存各幀圖像大小的鏈表,用於寫索引塊
  9 
 10 /*鏈表宿主結構,用於保存真正的圖像大小數據*/
 11 struct ListNode
 12 {
 13     int value;
 14     struct list_head head;
 15 };
 16 
 17 static void write_index_chunk(FILE *fp)
 18 {
 19     unsigned char index[4] = {'i', 'd', 'x', '1'};  //索引塊ID
 20     unsigned int index_chunk_size = 16 * nframes;   //索引塊大小
 21     unsigned int offset = 4;                        
 22     struct list_head *slider = NULL;
 23     struct list_head *tmpslider = NULL;
 24 
 25     fwrite(index, 4, 1, fp);
 26     fwrite(&index_chunk_size, 4, 1, fp);
 27 
 28     list_for_each_safe(slider, tmpslider, &list)
 29     {
 30         unsigned char tmp[4] = {'0', '0', 'd', 'c'};  //00dc = 壓縮的視頻數據
 31         unsigned int keyframe = 0x10;                 //0x10表示當前幀為關鍵幀
 32         struct ListNode *node = list_entry(slider, struct ListNode, head);
 33 
 34         fwrite(tmp, 4, 1, fp);
 35         fwrite(&keyframe, 4, 1, fp);    
 36         fwrite(&offset, 4, 1, fp);        
 37         fwrite(&node->value, 4, 1, fp);
 38         offset = offset + node->value + 8;
 39 
 40         list_del(slider);
 41         free(node);
 42     }
 43 }
 44 
 45 static void back_fill_data(FILE *fp, int width, int height, int fps)
 46 {
 47     AVI_RIFF_HEAD riff_head = 
 48     {
 49         {'R', 'I', 'F', 'F'},     
 50         4 + sizeof(AVI_HDRL_LIST) + sizeof(AVI_LIST_HEAD) + nframes * 8 + totalsize,  
 51         {'A', 'V', 'I', ' '}
 52     };
 53 
 54     AVI_HDRL_LIST hdrl_list = 
 55     {
 56         {'L', 'I', 'S', 'T'},
 57         sizeof(AVI_HDRL_LIST) - 8,
 58         {'h', 'd', 'r', 'l'},
 59         {
 60             {'a', 'v', 'i', 'h'},
 61             sizeof(AVI_AVIH_CHUNK) - 8,       
 62             1000000 / fps, 25000, 0, 0, nframes, 0, 1, 100000, width, height, 
 63             {0, 0, 0, 0}
 64         },
 65         {
 66             {'L', 'I', 'S', 'T'},
 67             sizeof(AVI_STRL_LIST) - 8,
 68             {'s', 't', 'r', 'l'},
 69             {
 70                 {'s', 't', 'r', 'h'},
 71                 sizeof(AVI_STRH_CHUNK) - 8,
 72                 {'v', 'i', 'd', 's'},
 73                 {'J', 'P', 'E', 'G'},
 74                 0, 0, 0, 0, 1, 23, 0, nframes, 100000, 0xFFFFFF, 0,
 75                 {0, 0, width, height}
 76             },
 77             {
 78                 {'s', 't', 'r', 'f'},
 79                 sizeof(AVI_STRF_CHUNK) - 8,
 80                 sizeof(AVI_STRF_CHUNK) - 8,
 81                 width, height, 1, 24,
 82                 {'J', 'P', 'E', 'G'},
 83                 width * height * 3, 0, 0, 0, 0
 84             }
 85         }
 86     };
 87 
 88     AVI_LIST_HEAD movi_list_head = 
 89     {
 90         {'L', 'I', 'S', 'T'},     
 91         4 + nframes * 8 + totalsize,           
 92         {'m', 'o', 'v', 'i'}    
 93     };
 94 
 95     //定位到文件頭,回填各塊數據
 96     fseek(fp, 0, SEEK_SET);
 97     fwrite(&riff_head, sizeof(riff_head), 1, fp);
 98     fwrite(&hdrl_list, sizeof(hdrl_list), 1, fp);
 99     fwrite(&movi_list_head, sizeof(movi_list_head), 1, fp);
100 }
101 
102 void jpeg2avi_start(FILE *fp)
103 {
104     int offset1 = sizeof(AVI_RIFF_HEAD);  //riff head大小
105     int offset2 = sizeof(AVI_HDRL_LIST);  //hdrl list大小 
106     int offset3 = sizeof(AVI_LIST_HEAD);  //movi list head大小
107 
108     //AVI文件偏移量設置到movi list head后,從該位置向后依次寫入JPEG數據
109     fseek(fp, offset1 + offset2 + offset3, SEEK_SET); 
110 
111     //初始化鏈表
112     list_head_init(&list);
113 
114     nframes = 0;
115     totalsize = 0;
116 }
117 
118 void jpeg2avi_add_frame(FILE *fp, void *data, unsigned int len)
119 {
120     unsigned char tmp[4] = {'0', '0', 'd', 'c'};  //00dc = 壓縮的視頻數據
121     struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));
122 
123     /*JPEG圖像大小4字節對齊*/
124     while (len % 4)
125     {
126         len++;
127     }
128 
129     fwrite(tmp, 4, 1, fp);    //寫入是否是壓縮的視頻數據信息    
130     fwrite(&len, 4, 1, fp);   //寫入4字節對齊后的JPEG圖像大小
131     fwrite(data, len, 1, fp); //寫入真正的JPEG數據
132 
133     nframes += 1;
134     totalsize += len;
135 
136     /*將4字節對齊后的JPEG圖像大小保存在鏈表中*/
137     if (node != NULL)
138     {
139         node->value = len;
140         list_add_tail(&node->head, &list);
141     }
142 }
143 
144 void jpeg2avi_end(FILE *fp, int width, int height, int fps)
145 { 
146     //寫索引塊
147     write_index_chunk(fp);
148 
149     //從文件頭開始,回填各塊數據
150     back_fill_data(fp, width, height, fps);
151 }
View Code

 

list.h

 1 #ifndef _LIST_H_
 2 #define _LIST_H_
 3 
 4 struct list_head
 5 {
 6     struct list_head *next;
 7     struct list_head *prev;
 8 };
 9 
10 void list_head_init(struct list_head *list);
11 void list_add_tail(struct list_head *_new, struct list_head *head);
12 void list_del(struct list_head *entry);
13 
14 #ifndef offsetof
15 #define offsetof(TYPE, MEMBER) \
16     ((size_t) &((TYPE *)0)->MEMBER)
17 #endif
18 
19 #ifndef container_of
20 #define container_of(ptr, type, member) \
21     ((type *)((char *)ptr - offsetof(type,member)))
22 #endif
23 
24 /**
25  * list_entry - get the struct for this entry
26  * @ptr:    the &struct list_head pointer.
27  * @type:    the type of the struct this is embedded in.
28  * @member:    the name of the list_struct within the struct.
29  */
30 #define list_entry(ptr, type, member) \
31     container_of(ptr, type, member)
32 
33 /**
34  * list_for_each_safe - iterate over a list safe against removal of list entry
35  * @pos:    the &struct list_head to use as a loop cursor.
36  * @n:        another &struct list_head to use as temporary storage
37  * @head:    the head for your list.
38  */
39 #define list_for_each_safe(pos, n, head) \
40     for (pos = (head)->next, n = pos->next; pos != (head); \
41         pos = n, n = pos->next)
42 
43 #endif //_LIST_H_
View Code

 

list.c

 1 #include "list.h"
 2 #include <stdio.h>
 3 
 4 static void __list_add(struct list_head *_new, struct list_head *prev, struct list_head *next)
 5 {
 6     next->prev = _new;
 7     _new->next = next;
 8     _new->prev = prev;
 9     prev->next = _new;
10 }
11 
12 static void __list_del(struct list_head *prev, struct list_head *next)
13 {
14     next->prev = prev;
15     prev->next = next;
16 }
17 
18 void list_head_init(struct list_head *list)
19 {
20     list->next = list;
21     list->prev = list;
22 }
23 
24 /**
25  * list_add_tail - insert a new entry before the specified head
26  * @_new: new entry to be added
27  * @head: list head to add it before
28  */
29 void list_add_tail(struct list_head *_new, struct list_head *head)
30 {
31     __list_add(_new, head->prev, head);
32 }
33 
34 /**
35  * list_del - deletes entry from list.
36  * @entry: the element to delete from the list.
37  */
38 void list_del(struct list_head *entry)
39 {
40     __list_del(entry->prev, entry->next);
41     entry->next = NULL;
42     entry->prev = NULL;
43 }
View Code

 

main.c

 1 #include "Jpeg2AVI.h"
 2 #include <string.h>
 3 
 4 #define JPEG_MAX_SIZE 100000   //JPEG圖像最大字節數
 5 #define JPEG_NUM 13800         //JPEG圖像數量
 6 
 7 int main()
 8 {
 9     FILE *fp_jpg;
10     FILE *fp_avi;  
11     int filesize;
12     unsigned char jpg_data[JPEG_MAX_SIZE];  
13     char filename[10];   
14     int i = 0;
15 
16     fp_avi = fopen("sample.avi","wb");
17 
18     jpeg2avi_start(fp_avi);
19 
20     for (i = 0; i < JPEG_NUM; i++)
21     {
22         memset(filename, 0, 10);
23         memset(jpg_data, 0, JPEG_MAX_SIZE);
24 
25         sprintf(filename, "%d.jpg", i + 1);
26         fp_jpg = fopen(filename, "rb");
27         
28         if (fp_jpg != NULL)
29         {
30             /*獲取JPEG數據大小*/
31             fseek(fp_jpg, 0, SEEK_END);
32             filesize = ftell(fp_jpg);
33             fseek(fp_jpg, 0, SEEK_SET);
34 
35             /*將JPEG數據讀到緩沖區*/
36             fread(jpg_data, filesize, 1, fp_jpg);
37 
38             /*將JPEG數據寫入AVI文件*/
39             jpeg2avi_add_frame(fp_avi, jpg_data, filesize);
40         }
41 
42         fclose(fp_jpg);
43     }
44 
45     jpeg2avi_end(fp_avi, 1920, 1080, 23);
46 
47     fclose(fp_avi);
48     printf("end\n");
49  
50     return 0;
51 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM