1 /*************************************************************************
2  * mkv.c - Matroska reading code [part of AVInfo 1.x]
3  * data format from
4  * http://matroska.org/
5  *
6  * Copyright (c) 2004 George Shuklin
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program (see the file COPYING); if not, write to the Free
20  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA  02111-1307  USA  or visit http://www.gnu.org/copyleft/gpl.html
22  *
23  *************************************************************************/
24 
25 
26  /* use a tabulation size 4 spaces for normal editig*/
27 #include "mkv.h"
28 #define BUFFER_SIZE 0x4000
29 #define MAX_STRING_SIZE 1024
30 
VINTparse(const unsigned char * buffer,const int start,const int end,int64 * result,const int flag)31 int VINTparse(const unsigned char* buffer, const int start, const int end, int64 *result,const int flag){
32 /*���������� �� VINT (�� 㪠������� ᬥ饭��) � int64, �����頥� ࠧ��� (ᤢ��), flag=0 - elementID, 1 - size*/
33     const unsigned char mask[8]={0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1};
34     const unsigned char imask[8]={0x7F,0x3F,0x1F,0xF,0x7,0x3,0x1,00};
35 	int VINT_WIDTH;
36 	int c;
37 	if(end-start<2) {printf("debug: oops, out of buffer(%d)\n",start);return 0; /*ops*/}
38 	VINT_WIDTH=0;
39     for(c=0;c<8;c++)
40     	if( !(buffer[start]&mask[c]) ) VINT_WIDTH++;
41     	else break;
42     if(VINT_WIDTH>=8 || VINT_WIDTH+start+1>=end) {printf("debug: oops2\n");return 0;}
43 	*result=0;
44     for(c=0;c<VINT_WIDTH;c++)
45     	*result+=buffer[start+VINT_WIDTH-c]<<(c*8);
46     if(flag)
47     	 *result+=(buffer[start]&imask[VINT_WIDTH])<<(VINT_WIDTH*8);
48     else *result+=(buffer[start])<<(VINT_WIDTH*8);
49 	return VINT_WIDTH+1;
50 }
51 
ElementRead(const unsigned char * buffer,const int start,const int end,unsigned int * ID,int64 * size)52 int ElementRead(const unsigned char* buffer, const int start, const int end, unsigned int* ID, int64* size){
53 	int64 tempID;
54 	int64 tempsize;
55 	int ID_offset,size_offset;
56 	ID_offset=VINTparse(buffer,start,end,&tempID,0);
57 	if(!ID_offset) return 0;
58 	size_offset=VINTparse(buffer,start+ID_offset,end,&tempsize,1);
59 	if(!size_offset) return 0;
60 	*ID=(int)tempID; /*id must be <4 and must to feet in uint*/
61 	*size=tempsize;
62 	return ID_offset+size_offset;
63 }
64 
GetInt(const unsigned char * buffer,const int start,const int size)65 int64  GetInt(const unsigned char* buffer,const int start, const int size){
66 /*return a int [8-64], from buffer, Big Endian*/
67 	int64 result=0;
68 	int c;
69 	for(c=1;c<=size;c++){
70 		result+=buffer[start+c-1]<<(8*(size-c));
71 	}
72 	return result;
73 }
74 
GetFloat(const unsigned char * buffer,const int start,const int size)75 float GetFloat(const unsigned char* buffer, const int start, const int size){
76 	float result=0;
77 	unsigned char tmp[4];
78 	if(size==sizeof(float)) {
79 		tmp[0]=buffer[start+3];
80 		tmp[1]=buffer[start+2];
81 		tmp[2]=buffer[start+1];
82 		tmp[3]=buffer[start];
83 		result=*((float*)(tmp));
84 	}
85 	return result;
86 }
87 
88 const unsigned int MKV_Parse_list[]={/*Elements, containing requed information (sub-elements), see enum in mkv.h for values*/
89 	MKVID_Segment,
90 	MKVID_Info,
91 	MKVID_Video,
92 	MKVID_Audio,
93 	MKVID_TrackEntry,
94 	MKVID_Tracks
95 };
96 
97 const char stream_type_letters[]="!vat";
98 
mkvparse(FILE * file,int s)99 int mkvparse(FILE* file, int s){
100 
101 	unsigned char* buffer=malloc(BUFFER_SIZE+128);
102 	unsigned int video[MAX_STREAMS][VIDEO_INFO_SIZE];
103 	unsigned int audio[MAX_STREAMS][AUDIO_INFO_SIZE];
104 	char* lang[MAX_STREAMS][4];/*1 - video, 2 - audio, 3 - text, 0 unused*/
105 	char* codec[MAX_STREAMS][4];/*1 - video, 2 - audio, 3 - text, 0 unused*/
106 	int p; /*pointer in buffer*/
107 	int c,c2; /*counter in some loops*/
108 	int readed_bytes;
109 	unsigned int eID; /*element ID*/
110 	int64 eSize; /*Size of element content*/
111    	int offs;
112 	char* title=NULL;
113 	int64 timescale=1000000;
114 	float Duration=0;
115 	char* tstr1, *tstr2; /*temp strings*/
116 	int64 DefaultDuration=0;
117 	int TrackType=0;
118 	int pvt_look_flag=0;
119 	int curr_c=-1;
120 	int a_c=-1;
121 	int v_c=-1;
122 	int t_c=-1;
123 	int value=0;
124 
125 	if(!buffer) return 0;
126 	memset(video,0,sizeof(video));
127 	memset(audio,0,sizeof(audio));
128 	memset(lang,0,sizeof(lang));
129 	memset(codec,0,sizeof(codec));
130 
131 	readed_bytes=fread(buffer,1,BUFFER_SIZE,file); /*todo buffer_size,1 ? */
132 	if(!readed_bytes){free(buffer);return 0;}
133 	p=0;
134 	while(buffer[p]!=MKVID_FILE_BEGIN){
135 		p++;
136 		if(p>=readed_bytes){
137 			free(buffer);
138 			return 0;
139 		}
140 	}; /*skip text while EBML begin*/
141 
142 /*main loop*/
143 	do{
144 		offs=ElementRead(buffer,p,readed_bytes,&eID,&eSize);
145         p+=offs;
146 		if(!offs||p>=readed_bytes) break;
147 		for(c=0;c<sizeof(MKV_Parse_list)/sizeof(*MKV_Parse_list);c++)
148 			if(MKV_Parse_list[c]==eID) {
149 				break;
150 		    }
151 		if(c<sizeof(MKV_Parse_list)/sizeof(*MKV_Parse_list)) continue;
152 		if(p+eSize>readed_bytes) break; /*TODO - add (if requied) suckup from file to buffer*/
153 		if(eSize==4||eSize==8||eSize==1||eSize==2)
154 			value=(int)GetInt(buffer,p,eSize);
155 		switch(eID){
156 			case MKVID_TrackType: /*detect a stream type (video/audio/text)*/
157 				TrackType=value;
158 				pvt_look_flag=0;
159 				switch(TrackType){
160 					case MKV_Track_video:
161 						v_c++;
162 						if(v_c>MAX_STREAMS) v_c=MAX_STREAMS;
163 						video[v_c][V_exist]=1;
164 						video[v_c][V_l]=(int)(Duration/1e+9*(float)timescale);
165 						curr_c=v_c;
166 						break;
167 					case MKV_Track_audio:
168 					    a_c++;
169 					    if(a_c>MAX_STREAMS) a_c=MAX_STREAMS;
170 					    audio[a_c][A_exist]=1;
171 						audio[a_c][A_l]=(int)(Duration/1e+9*(float)timescale);
172 						curr_c=a_c;
173 					    break;
174 					case MKV_Track_subtitle_orig:
175 					    t_c++;
176 					    TrackType=MKV_Track_subtitle; /*for normal use in lang array*/
177 					    if(t_c>MAX_STREAMS) t_c=MAX_STREAMS;
178 					    curr_c=t_c;
179 						break;
180 				}
181 				break;
182 			case MKVID_DefaultDuration: /*fps detection*/
183 				if(TrackType==MKV_Track_video&&v_c>=0){
184 						DefaultDuration=value;
185 						if(DefaultDuration>100){
186 							video[v_c][V_fpsH]=1000000000/DefaultDuration; /*DD in nano sec. fps=1s/DD*/
187 							video[v_c][V_fpsL]=1000000000/(DefaultDuration/100)-100*video[v_c][V_fpsH];
188 						}
189 				}
190 				break;
191 			case MKVID_Language: /*stream language*/
192 				if(curr_c>=0&&TrackType<4&&eSize<MAX_STRING_SIZE)
193 					if(lang[curr_c][TrackType]) free(lang[curr_c][TrackType]);
194 					lang[curr_c][TrackType]=mkstr(buffer,p,eSize);
195 				break;
196 			case MKVID_CodecName:/*passtrough*/
197 			case MKVID_CodecID: /*codec detection (if V_MS/VFW/FOURCC - set a fourcc code, else fill a vcodecs value)*/
198 				if(curr_c>=0&&TrackType<4&&eSize<MAX_STRING_SIZE){
199 					if(codec[curr_c][TrackType]) free(codec[curr_c][TrackType]);
200 					codec[curr_c][TrackType]=mkstr(buffer,p,eSize);
201 					if(!strcmp(codec[curr_c][TrackType],"V_MS/VFW/FOURCC")) pvt_look_flag=1;
202 				}
203 				break;
204 			case MKVID_CodecPrivate:
205 				if(pvt_look_flag&&v_c>=0&&eSize>=24){ /*CodecPrivate contains a BITMAPINFOHEADER structure due CodecID==V_MS/VFW/FOURCC*/
206 					pvt_look_flag=0;
207 					video[v_c][V_cc]=(buffer[p+16]<<24)+(buffer[p+17]<<16)+(buffer[p+18]<<8)+buffer[p+19];
208 					if(codec[v_c][MKV_Track_video]){
209 						free(codec[v_c][MKV_Track_video]);
210 						codec[v_c][MKV_Track_video]=NULL;
211 					}
212 				}
213 				break;
214 			case MKVID_PixelWidth: /*pasthough*/
215 			case MKVID_DisplayWidth:
216 				if(v_c>=0)video[v_c][V_x]=value;
217 				break;
218 			case MKVID_PixelHeight: /*pasthough*/
219 			case MKVID_DisplayHeight:
220 				if(v_c>=0)video[v_c][V_y]=value;
221 				break;
222 			case MKVID_TimeCodeScale:
223 				timescale=GetInt(buffer,p,eSize);
224 				break;
225 			case MKVID_Duration:
226 				Duration=GetFloat(buffer,p,eSize);
227 				break;
228 			case MKVID_Channels:
229 				if(a_c>=0)audio[a_c][A_ch]=value;
230 				break;
231 			case MKVID_BitDepth:
232 				if(a_c>=0)audio[a_c][A_bits]=value;
233 				break;
234 			case MKVID_OutputSamplingFrequency: /*pasthough*/
235 			case MKVID_SamplingFrequency:
236 				if(a_c>=0) audio[a_c][A_freq]=(int)GetFloat(buffer,p,eSize);
237 				break;
238 			case MKVID_Title:
239 				if(eSize>MAX_STRING_SIZE) break;
240 				title=mkstr(buffer,p,eSize);
241 				SetNumericVar("stream.d",1);
242 				SetNumericVar("d1.num",1);
243 				SetStringVar("d11.name","Title");
244 				SetStringVar("d11.value",title);
245 				free(title);
246 				break;
247 /*TODO			case MKVID_Tags:*/
248 		}
249 		p+=eSize;/*skip unknown or uninteresting*/
250 	}while(1);
251 	AddAudioVideo(video,audio);
252 	tstr1=dup("dn.lang");
253 	tstr2=dup("dn.cc");
254 	if(tstr1&&tstr2){
255 		for(c=0;c<MAX_STREAMS;c++){
256 			for(c2=0;c2<4;c2++){
257 				tstr1[0]=stream_type_letters[c2];
258 				tstr1[1]=c+'0'+1;
259 				tstr2[1]=tstr1[1];
260 				tstr2[0]=tstr1[0];
261 				if(lang[c][c2]){
262 					SetStringVar(tstr1,lang[c][c2]);
263 					free(lang[c][c2]);
264 				}
265 				if(codec[c][c2]){
266 					SetStringVar(tstr2,codec[c][c2]);
267 					free(codec[c][c2]);
268 				}
269 			}
270 		}
271 		SetNumericVar("stream.t",t_c+1);
272 		free(tstr1);
273 		free(tstr2);
274 	}
275 	if(buffer) free(buffer);
276 	return 1;
277 }
278