Mercurial > mplayer.hg
annotate libmpdemux/muxer_mpeg.c @ 9160:2a67f587c792
Here is updated manual (24.01.2003).
- by imoteph
author | gabucino |
---|---|
date | Wed, 29 Jan 2003 17:23:23 +0000 |
parents | 12fc55eb3373 |
children | b677b25a898d |
rev | line source |
---|---|
8585 | 1 |
2 #include <stdio.h> | |
3 #include <stdlib.h> | |
4 #include <string.h> | |
5 #include <sys/types.h> | |
6 | |
7 #include "config.h" | |
8 #include "../version.h" | |
9 | |
10 #include "wine/mmreg.h" | |
11 #include "wine/avifmt.h" | |
12 #include "wine/vfw.h" | |
13 #include "bswap.h" | |
14 | |
15 #include "muxer.h" | |
16 | |
17 // 18 bytes reserved for block headers and STD | |
18 #define MUXER_MPEG_DATASIZE (MUXER_MPEG_BLOCKSIZE-18) | |
19 | |
20 // ISO-11172 requirements | |
21 #define MPEG_MAX_PTS_DELAY 90000 /* 1s */ | |
22 #define MPEG_MAX_SCR_INTERVAL 63000 /* 0.7s */ | |
23 | |
24 // suggestions | |
25 #define MPEG_STARTPTS 45000 /* 0.5s */ | |
26 #define MPEG_MIN_PTS_DELAY 9000 /* 0.1s */ | |
27 #define MPEG_STARTSCR 9 /* 0.1ms */ | |
28 | |
29 //static unsigned int mpeg_min_delay; | |
30 //static unsigned int mpeg_max_delay; | |
31 | |
32 static muxer_stream_t* mpegfile_new_stream(muxer_t *muxer,int type){ | |
33 muxer_stream_t *s; | |
34 | |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
35 if (!muxer) return NULL; |
8585 | 36 if(muxer->avih.dwStreams>=MUXER_MAX_STREAMS){ |
37 printf("Too many streams! increase MUXER_MAX_STREAMS !\n"); | |
38 return NULL; | |
39 } | |
40 switch (type) { | |
41 case MUXER_TYPE_VIDEO: | |
42 if (muxer->num_videos >= 15) { | |
43 printf ("MPEG stream can't contain above of 15 video streams!\n"); | |
44 return NULL; | |
45 } | |
46 break; | |
47 case MUXER_TYPE_AUDIO: | |
48 if (muxer->avih.dwStreams - muxer->num_videos >= 31) { | |
49 printf ("MPEG stream can't contain above of 31 audio streams!\n"); | |
50 return NULL; | |
51 } | |
52 break; | |
53 default: | |
54 printf ("Unknown stream type!\n"); | |
55 return NULL; | |
56 } | |
57 s=malloc(sizeof(muxer_stream_t)); | |
58 memset(s,0,sizeof(muxer_stream_t)); | |
59 if(!s) return NULL; // no mem!? | |
60 if (!(s->b_buffer = malloc (MUXER_MPEG_BLOCKSIZE))) { | |
61 free (s); | |
62 return NULL; // no mem?! | |
63 } else if (type == MUXER_TYPE_VIDEO) { | |
64 s->ckid = be2me_32 (0x1e0 + muxer->num_videos); | |
65 muxer->num_videos++; | |
66 s->h.fccType=streamtypeVIDEO; | |
67 if(!muxer->def_v) muxer->def_v=s; | |
68 // printf ("Added video stream %d\n", muxer->num_videos); | |
69 } else { // MUXER_TYPE_AUDIO | |
70 s->ckid = be2me_32 (0x1c0 + s->id - muxer->num_videos); | |
71 s->h.fccType=streamtypeAUDIO; | |
72 // printf ("Added audio stream %d\n", s->id - muxer->num_videos + 1); | |
73 } | |
74 muxer->streams[muxer->avih.dwStreams]=s; | |
75 s->type=type; | |
76 s->id=muxer->avih.dwStreams; | |
77 s->timer=0.0; | |
78 s->size=0; | |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
79 s->muxer=muxer; |
8585 | 80 muxer->avih.dwStreams++; |
81 return s; | |
82 } | |
83 | |
84 static void write_mpeg_ts(unsigned char *b, unsigned int ts, char mod) { | |
85 b[0] = ((ts >> 29) & 0xf) | 1 | mod; | |
86 b[1] = (ts >> 22) & 0xff; | |
87 b[2] = ((ts >> 14) & 0xff) | 1; | |
88 b[3] = (ts >> 7) & 0xff; | |
89 b[4] = ((ts << 1) & 0xff) | 1; | |
90 } | |
91 | |
92 static void write_mpeg_rate(unsigned char *b, unsigned int rate) { | |
93 if (rate) | |
94 rate--; // for round upward | |
95 rate /= 50; | |
96 rate++; // round upward | |
97 b[0] = ((rate >> 15) & 0x7f) | 0x80; | |
98 b[1] = (rate >> 7) & 0xff; | |
99 b[2] = ((rate << 1) & 0xff) | 1; | |
100 } | |
101 | |
102 static void write_mpeg_std(unsigned char *b, unsigned int size, char mod) { | |
103 if (size) | |
104 size--; // for round upward | |
105 if (size < (128 << 8)) | |
106 size >>= 7; // by 128 bytes | |
107 else { | |
108 size >>= 10; | |
109 size |= 0x2000; // by 1kbyte | |
110 } | |
111 size++; // round upward | |
112 b[0] = ((size >> 8) & 0x3f) | 0x40 | mod; | |
113 b[1] = size & 0xff; | |
114 } | |
115 | |
116 static int write_mpeg_block(muxer_t *muxer, muxer_stream_t *s, FILE *f, char *bl, size_t len, int isoend){ | |
117 size_t sz; // rest in block buffer | |
118 unsigned char buff[12]; // 0x1ba header | |
119 unsigned int mints=0; | |
120 uint16_t l1; | |
121 | |
122 if (s->b_buffer_ptr == 0) { // 00001111 if no PTS | |
123 s->b_buffer[0] = 0xf; | |
124 s->b_buffer_ptr = 1; | |
125 sz = MUXER_MPEG_DATASIZE-1; | |
126 } else if (s->b_buffer_ptr > MUXER_MPEG_DATASIZE) { | |
127 printf ("Unknown error in write_mpeg_block()!\n"); | |
128 return 0; | |
129 } else { | |
130 sz = MUXER_MPEG_DATASIZE - s->b_buffer_ptr; | |
131 if (s->b_buffer[7] == 0xff) // PTS not set yet | |
132 s->b_buffer[11] = 0xf; // terminate stuFFing bytes | |
133 } | |
134 if (len > sz) | |
135 len = sz; | |
136 *(uint32_t *)buff = be2me_32 (0x1ba); | |
137 write_mpeg_ts (buff+4, muxer->file_end, 0x20); // 0010 and SCR | |
138 write_mpeg_rate (buff+9, muxer->sysrate); | |
139 fwrite (buff, 12, 1, f); | |
140 fwrite (&s->ckid, 4, 1, f); // stream_id | |
141 memset (buff, 0xff, 12); // stuFFing bytes | |
142 sz -= len; | |
143 // calculate padding bytes in buffer | |
144 while (mints < s->b_buffer_ptr && s->b_buffer[mints] == 0xff) mints++; | |
145 if (mints+sz < 12) { // cannot write padding block so write up to 12 stuFFing bytes | |
146 l1 = be2me_16 (MUXER_MPEG_DATASIZE); | |
147 fwrite (&l1, 2, 1, f); | |
148 mints = 0; // so stuFFed bytes will be written all | |
149 if (sz) | |
150 fwrite (buff, sz, 1, f); | |
151 sz = 0; // no padding block anyway | |
152 } else { // use padding block | |
153 if (sz > 6) // sufficient for PAD header so don't shorter data | |
154 mints = 0; | |
155 else | |
156 sz += mints; // skip stuFFing bytes (sz>8 here) | |
157 l1 = be2me_16 (s->b_buffer_ptr+len-mints); | |
158 fwrite (&l1, 2, 1, f); | |
159 } | |
160 if (s->b_buffer_ptr) | |
161 fwrite (s->b_buffer+mints, s->b_buffer_ptr-mints, 1, f); | |
162 if (len) | |
163 fwrite (bl, len, 1, f); | |
164 if (sz > 6) { // padding block (0x1be) | |
165 uint32_t l0; | |
166 | |
167 if (isoend) | |
168 l0 = be2me_32 (0x1b9); | |
169 else | |
170 l0 = be2me_32 (0x1be); | |
171 sz -= 6; | |
172 l1 = be2me_16 (sz); | |
173 fwrite (&l0, 4, 1, f); | |
174 fwrite (&l1, 2, 1, f); | |
175 memset (s->b_buffer, 0xff, sz); // stuFFing bytes | |
176 fwrite (s->b_buffer, sz, 1, f); | |
177 } | |
178 s->b_buffer_ptr = 0; | |
179 muxer->movi_end += MUXER_MPEG_BLOCKSIZE; | |
180 // prepare timestamps for next pack | |
181 mints = (MUXER_MPEG_BLOCKSIZE*90000/muxer->sysrate)+1; // min ts delta | |
182 sz = (int)(s->timer*90000) + MPEG_STARTPTS; // new PTS | |
183 if (sz > muxer->file_end) | |
184 sz -= muxer->file_end; // suggested ts delta | |
185 else | |
186 { | |
187 sz = 0; | |
188 printf ("Error in stream: PTS earlier than SCR!\n"); | |
189 } | |
190 if (sz > MPEG_MAX_PTS_DELAY) { | |
191 // printf ("Warning: attempt to set PTS to SCR delay to %u \n", sz); | |
192 mints = sz-MPEG_MAX_PTS_DELAY; // try to compensate | |
193 if (mints > MPEG_MAX_SCR_INTERVAL) { | |
194 printf ("Error in stream: SCR interval %u is too big!\n", mints); | |
195 } | |
196 } else if (sz > 54000) // assume 0.3...0.7s is optimal | |
197 mints += (sz-45000)>>2; // reach 0.5s in 4 blocks ? | |
198 else if (sz < 27000) { | |
199 unsigned int newsysrate = 0; | |
200 | |
201 if (s->timer > 0.5) // too early to calculate??? | |
202 newsysrate = muxer->movi_end/(s->timer*0.4); // pike-factor 2.5 (8dB) | |
203 if (sz < MPEG_MIN_PTS_DELAY) | |
204 printf ("Error in stream: PTS to SCR delay %u is too little!\n", sz); | |
205 if (muxer->sysrate < newsysrate) | |
206 muxer->sysrate = newsysrate; // increase next rate to current rate | |
207 else if (!newsysrate) | |
208 muxer->sysrate += muxer->sysrate>>3; // increase next rate by 25% | |
209 } | |
210 muxer->file_end += mints; // update the system timestamp | |
211 return len; | |
212 } | |
213 | |
214 static void set_mpeg_pts(muxer_t *muxer, muxer_stream_t *s, unsigned int pts) { | |
215 unsigned int dts, nts; | |
216 | |
217 if (s->b_buffer_ptr != 0 && s->b_buffer[7] != 0xff) | |
218 return; // already set | |
219 if (s->b_buffer_ptr == 0) { | |
220 memset (s->b_buffer, 0xff, 7); // reserved for PTS or STD, stuFFing for now | |
221 s->b_buffer_ptr = 12; | |
222 } | |
223 dts = (int)(s->timer*90000) + MPEG_STARTPTS; // PTS | |
224 if (pts) { | |
225 write_mpeg_ts (s->b_buffer+2, pts, 0x30); // 0011 and both PTS/DTS | |
226 } else { | |
227 write_mpeg_ts (s->b_buffer+7, dts, 0x20); // 0010 and PTS only | |
228 return; | |
229 } | |
230 nts = dts - muxer->file_end; | |
231 // if (nts < mpeg_min_delay) mpeg_min_delay = nts; | |
232 // if (nts > mpeg_max_delay) mpeg_max_delay = nts; | |
233 nts = 180000*s->h.dwScale/s->h.dwRate; // two frames | |
234 if (dts-nts < muxer->file_end) { | |
235 dts += muxer->file_end; | |
236 dts /= 2; // calculate average time | |
237 printf ("Warning: DTS to SCR delay is too small\n"); | |
238 } | |
239 else | |
240 dts -= nts/2; // one frame :) | |
241 write_mpeg_ts (s->b_buffer+7, dts, 0x10); | |
242 } | |
243 | |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
244 static void mpegfile_write_chunk(muxer_stream_t *s,size_t len,unsigned int flags){ |
8585 | 245 size_t ptr=0, sz; |
246 unsigned int pts=0; | |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
247 muxer_t *muxer = s->muxer; |
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
248 FILE *f; |
8585 | 249 |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
250 f = muxer->file; |
8585 | 251 if (s->type == MUXER_TYPE_VIDEO) { // try to recognize frame type... |
252 if (s->buffer[0] != 0 || s->buffer[1] != 0 || s->buffer[2] != 1 || len<6) { | |
253 printf ("Unknown block type, possibly non-MPEG stream!\n"); | |
254 sz = len; | |
255 // return; | |
256 } else if (s->buffer[3] == 0 || s->buffer[3] == 0xb3 || | |
257 s->buffer[3] == 0xb8) { // Picture or GOP | |
258 int temp_ref; | |
259 int pt; | |
260 | |
261 if (s->buffer[3]) { // GOP -- scan for Picture | |
262 s->gop_start = s->h.dwLength; | |
263 while (ptr < len-5 && (s->buffer[ptr] != 0 || s->buffer[ptr+1] != 0 || | |
264 s->buffer[ptr+2] != 1 || s->buffer[ptr+3] != 0)) ptr++; | |
265 if (s->b_buffer_ptr > MUXER_MPEG_DATASIZE-39-12) { // 39 bytes for Gop+Pic+Slice headers | |
266 write_mpeg_block (muxer, s, f, NULL, 0, 0); | |
267 } | |
268 } | |
269 if (ptr >= len-5) { | |
270 pt = 0; // Picture not found?! | |
271 temp_ref = 0; | |
272 printf ("Warning: picture not found in GOP!\n"); | |
273 } else { | |
274 pt = (s->buffer[ptr+5]>>3) & 7; | |
275 temp_ref = (s->buffer[ptr+4]<<2)+(s->buffer[ptr+5]>>6); | |
276 } | |
277 ptr = 0; | |
278 temp_ref += s->gop_start; | |
279 switch (pt) { | |
280 case 2: // predictive | |
281 if (s->ipb[0]) { | |
282 sz = len + s->ipb[0]; | |
283 if (s->ipb[0] < s->ipb[2]) | |
284 s->ipb[0] = s->ipb[2]; | |
285 s->ipb[2] = 0; | |
286 } else if (s->ipb[2]) { | |
287 sz = len + s->ipb[2]; | |
288 s->ipb[0] = s->ipb[2]; | |
289 s->ipb[2] = 0; | |
290 } else | |
291 sz = 4 * len; // no bidirectional frames yet? | |
292 s->ipb[1] = len; | |
293 // pictires may be not in frame sequence so recalculate timer | |
294 pts = (int)(90000*((double)temp_ref*s->h.dwScale/s->h.dwRate)) + MPEG_STARTPTS; | |
295 break; | |
296 case 3: // bidirectional | |
297 s->ipb[2] += len; | |
298 sz = s->ipb[1] + s->ipb[2]; | |
299 // pictires may be not in frame sequence so recalculate timer | |
300 s->timer = (double)temp_ref*s->h.dwScale/s->h.dwRate; | |
301 break; | |
302 default: // intra-coded | |
303 // pictires may be not in frame sequence so recalculate timer | |
304 pts = (int)(90000*((double)temp_ref*s->h.dwScale/s->h.dwRate)) + MPEG_STARTPTS; | |
305 sz = len; // no extra buffer for it... | |
306 } | |
307 } else { | |
308 printf ("Unknown block type, possibly non-MPEG stream!\n"); | |
309 sz = len; | |
310 // return; | |
311 } | |
312 sz <<= 1; | |
313 } else { // MUXER_TYPE_AUDIO | |
314 if (len < 2*MUXER_MPEG_DATASIZE) | |
315 sz = 2*MUXER_MPEG_DATASIZE; // min requirement | |
316 else | |
317 sz = len; | |
318 } | |
319 set_mpeg_pts (muxer, s, pts); | |
320 // alter counters: | |
321 if (s->h.dwSampleSize) { | |
322 // CBR | |
323 s->h.dwLength += len/s->h.dwSampleSize; | |
324 if (len%s->h.dwSampleSize) printf("Warning! len isn't divisable by samplesize!\n"); | |
325 } else { | |
326 // VBR | |
327 s->h.dwLength++; | |
328 } | |
329 if (!muxer->sysrate) { | |
330 muxer->sysrate = 2108000/8; // constrained stream parameter | |
331 muxer->file_end = MUXER_MPEG_BLOCKSIZE*90000/muxer->sysrate + MPEG_STARTSCR+1; | |
332 } | |
333 if (sz > s->h.dwSuggestedBufferSize) { // increase and set STD | |
334 s->h.dwSuggestedBufferSize = sz; | |
335 if (s->b_buffer[2] != 0xff) // has both PTS and DTS | |
336 write_mpeg_std (s->b_buffer, s->h.dwSuggestedBufferSize, 0x40); // 01 | |
337 else // has only PTS | |
338 write_mpeg_std (s->b_buffer+5, s->h.dwSuggestedBufferSize, 0x40); // 01 | |
339 } | |
340 s->size += len; | |
341 // write out block(s) if it's ready | |
342 while (s->b_buffer_ptr+len >= MUXER_MPEG_DATASIZE-12) { // reserved for std and pts | |
343 // write out the block | |
344 sz = write_mpeg_block (muxer, s, f, &s->buffer[ptr], len, 0); | |
345 // recalculate the rest of chunk | |
346 ptr += sz; | |
347 len -= sz; | |
348 } | |
349 s->timer = (double)s->h.dwLength*s->h.dwScale/s->h.dwRate; | |
350 if (len) { // save rest in buffer | |
351 if (s->b_buffer_ptr == 0) { | |
352 memset (s->b_buffer, 0xff, 12); // stuFFing bytes for now | |
353 if (s->type == MUXER_TYPE_AUDIO && s->h.dwSampleSize) { // CBR audio | |
354 sz = s->h.dwLength - len/s->h.dwSampleSize; // first sample number | |
355 write_mpeg_ts (s->b_buffer+7, | |
356 (int)(90000*((double)sz*s->h.dwScale/s->h.dwRate)) + MPEG_STARTPTS, | |
357 0x20); // 0010 and PTS only | |
358 } | |
359 s->b_buffer_ptr = 12; | |
360 } | |
361 memcpy (s->b_buffer+s->b_buffer_ptr, s->buffer+ptr, len); | |
362 s->b_buffer_ptr += len; | |
363 } | |
364 } | |
365 | |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
366 static void mpegfile_write_header(muxer_t *muxer){ |
8585 | 367 unsigned int i; |
368 size_t sz = MUXER_MPEG_BLOCKSIZE-24; | |
369 unsigned char buff[12]; | |
370 muxer_stream_t *s = muxer->streams[0]; | |
371 uint32_t l1; | |
372 uint16_t l2; | |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
373 FILE *f = muxer->file; |
8585 | 374 |
375 if (s == NULL) | |
376 return; // no streams!? | |
377 // packet header (0x1ba) -- rewrite first stream buffer | |
378 *(uint32_t *)buff = be2me_32 (0x1ba); | |
379 write_mpeg_ts (buff+4, MPEG_STARTSCR, 0x20); // 0010 -- pack | |
380 write_mpeg_rate (buff+9, muxer->sysrate); | |
381 fwrite (buff, 12, 1, f); | |
382 // start system stream (in own block): Sys (0x1bb) | |
383 l1 = be2me_32 (0x1bb); | |
384 l2 = be2me_16 (6 + 3*muxer->avih.dwStreams); // header_length | |
385 fwrite (&l1, 4, 1, f); | |
386 fwrite (&l2, 2, 1, f); | |
387 write_mpeg_rate (buff, muxer->sysrate); // rate_bound | |
388 // set number of audio/video, fixed_flag=CSPS_flag=system_*_lock_flag=0 | |
389 buff[3] = (muxer->avih.dwStreams - muxer->num_videos) << 2; // audio_bound | |
390 buff[4] = muxer->num_videos | 0x20; | |
391 buff[5] = 0xff; // reserved_byte | |
392 fwrite (buff, 6, 1, f); | |
393 for (i = 0; i < muxer->avih.dwStreams; i++) { | |
394 buff[0] = ((char *)&muxer->streams[i]->ckid)[3]; // last char in big endian | |
395 //fprintf (stderr, "... stream 0x1%02x; bufsize %u", (int)buff[0], muxer->streams[i]->h.dwSuggestedBufferSize); | |
396 write_mpeg_std (buff+1, muxer->streams[i]->h.dwSuggestedBufferSize, 0xc0); // 11 | |
397 fwrite (buff, 3, 1, f); | |
398 sz -= 3; | |
399 } | |
400 if (sz >= 6) { // padding block | |
401 l1 = be2me_32 (0x1be); | |
402 sz -= 6; | |
403 l2 = be2me_16 (sz); | |
404 fwrite (&l1, 4, 1, f); | |
405 fwrite (&l2, 2, 1, f); | |
406 } | |
407 s->b_buffer[0] = 0x0f; // end of list - next bit has to be 0 | |
408 // stuFFing bytes -- rewrite first stream buffer | |
409 if (sz > 1) | |
410 memset (s->b_buffer+1, 0xff, sz-1); | |
411 fwrite (s->b_buffer, sz, 1, f); | |
412 muxer->movi_start = 0; | |
413 muxer->movi_end = MUXER_MPEG_BLOCKSIZE; | |
414 } | |
415 | |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
416 static void mpegfile_write_index(muxer_t *muxer){ |
8585 | 417 unsigned int i; |
418 unsigned int rsr; | |
419 | |
420 if (!muxer->avih.dwStreams) return; // no streams?! | |
421 // finish all but one video and audio streams | |
422 rsr = muxer->sysrate; // reserve it since it's silly change it at that point | |
423 for (i = 0; i < muxer->avih.dwStreams-1; i++) | |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
424 write_mpeg_block (muxer, muxer->streams[i], muxer->file, NULL, 0, 0); |
8585 | 425 // end sequence: ISO-11172-End (0x1b9) and finish very last block |
9007
12fc55eb3373
Cleanup of the muxer API, func parameters muxer & muxer_f eliminated.
arpi
parents:
8585
diff
changeset
|
426 write_mpeg_block (muxer, muxer->streams[i], muxer->file, NULL, 0, 1); |
8585 | 427 //fprintf (stderr, "PTS to SCR delay: min %u.%03u, max %u.%03u\n", |
428 // mpeg_min_delay/90000, (mpeg_min_delay/90)%1000, | |
429 // mpeg_max_delay/90000, (mpeg_max_delay/90)%1000); | |
430 muxer->sysrate = rsr; | |
431 } | |
432 | |
433 void muxer_init_muxer_mpeg(muxer_t *muxer){ | |
434 muxer->cont_new_stream = &mpegfile_new_stream; | |
435 muxer->cont_write_chunk = &mpegfile_write_chunk; | |
436 muxer->cont_write_header = &mpegfile_write_header; | |
437 muxer->cont_write_index = &mpegfile_write_index; | |
438 // mpeg_min_delay = mpeg_max_delay = MPEG_STARTPTS-MPEG_STARTSCR; | |
439 } | |
440 |