Annotation of multiplexer/splitps.c, revision 1.1

1.1     ! oskar       1: /*
        !             2:  * ISO 13818 stream multiplexer
        !             3:  * Copyright (C) 2001 Convergence Integrated Media GmbH Berlin
        !             4:  * Author: Oskar Schirmer (oskar@convergence.de)
        !             5:  */
        !             6: 
        !             7: /*
        !             8:  * Module:  Split PS
        !             9:  * Purpose: Split a program stream.
        !            10:  *
        !            11:  * This module examines a program stream and copies the packets to
        !            12:  * the input stream buffers. PSI data is extracted, descriptors are
        !            13:  * copied to the mapstream (stream 0).
        !            14:  */
        !            15: 
        !            16: #include "global.h"
        !            17: #include "error.h"
        !            18: #include "pes.h"
        !            19: #include "ps.h"
        !            20: #include "input.h"
        !            21: #include "descref.h"
        !            22: #include "splitpes.h"
        !            23: #include "splitps.h"
        !            24: 
        !            25: static int ps_program_end_code (file_descr *f)
        !            26: {
        !            27:   warn (LIMP,"Program End Code",EPST,3,0,0);
        !            28:   /* close file, do ... */
        !            29:   return (PES_HDCODE_SIZE);
        !            30: }
        !            31: 
        !            32: static int ps_pack_header (file_descr *f,
        !            33:     int l)
        !            34: {
        !            35:   int i, s;
        !            36:   long x;
        !            37:   byte a, b;
        !            38:   clockref oldscr; /* used for correcting bad DVB scr values */
        !            39:   if (l < PS_PACKHD_SIZE) {
        !            40:     warn (LDEB,"Pack header (incomplete)",EPST,2,1,l);
        !            41:     return (0);
        !            42:   }
        !            43:   i = f->data.out;
        !            44:   list_incr (i,f->data,PS_PACKHD_STUFLN);
        !            45:   s = (f->data.ptr[i] & 0x07) + PS_PACKHD_SIZE;
        !            46:   if (l < s) {
        !            47:     warn (LDEB,"Pack header (incomplete)",EPST,2,2,l);
        !            48:     return (0);
        !            49:   }
        !            50:   oldscr = f->u.ps.ph.scr;
        !            51:   warn (LINF,"Pack header",EPST,2,0,s);
        !            52:   i = f->data.out;
        !            53:   list_incr (i,f->data,PS_PACKHD_SCR);
        !            54:   a = f->data.ptr[i];
        !            55:   marker_check (a,0x44,0xC4);
        !            56:   f->u.ps.ph.scr.ba33 = (a >> 5) & 1;
        !            57:   x = ((a & 0x18) | ((a & 0x03) << 1)) << 7;
        !            58:   list_incr (i,f->data,1);
        !            59:   x = (x | f->data.ptr[i]) << 8;
        !            60:   list_incr (i,f->data,1);
        !            61:   a = f->data.ptr[i];
        !            62:   marker_bit (a,2);
        !            63:   x = (x | ((a & 0xF8) | ((a & 0x03) << 1))) << 7;
        !            64:   list_incr (i,f->data,1);
        !            65:   x = (x | f->data.ptr[i]);
        !            66:   list_incr (i,f->data,1);
        !            67:   a = f->data.ptr[i];
        !            68:   marker_bit (a,2);
        !            69:   f->u.ps.ph.scr.base = (x << 5) | (a >> 3);
        !            70:   warn (LSEC,"SCR base",EPST,2,3,f->u.ps.ph.scr.base);
        !            71:   if (accept_weird_scr
        !            72:    && ((oldscr.base - (90*40+1)) == f->u.ps.ph.scr.base)) {
        !            73:     /* the DVB card produces weird scr-s, every second scr is less than
        !            74:        previous one, indicating an odd value decrease of 40ms. weird! */
        !            75:     f->u.ps.ph.scr.base = oldscr.base + (90*40);
        !            76:   }
        !            77:   list_incr (i,f->data,1);
        !            78:   b = f->data.ptr[i];
        !            79:   marker_bit (b,0);
        !            80:   f->u.ps.ph.scr.ext = ((a & 0x03) << 7) | (b >> 1);
        !            81:   warn (LSEC,"SCR ext",EPST,2,4,f->u.ps.ph.scr.ext);
        !            82:   f->u.ps.time.read = clockref2msec (f->u.ps.ph.scr);
        !            83:   warn (LDEB,"(time read)",EPST,2,5,f->u.ps.time.read);
        !            84:   list_incr (i,f->data,1);
        !            85:   x = f->data.ptr[i] << 8;
        !            86:   list_incr (i,f->data,1);
        !            87:   x = (x | f->data.ptr[i]) << 6;
        !            88:   list_incr (i,f->data,1);
        !            89:   a = f->data.ptr[i];
        !            90:   marker_check (a,0x03,0x03);
        !            91:   f->u.ps.ph.muxrate = x | (a >> 2);
        !            92:   warn (LSEC,"muxrate",EPST,2,6,f->u.ps.ph.muxrate);
        !            93:   return (s);
        !            94: }
        !            95: 
        !            96: static boolean ps_system_header (file_descr *f,
        !            97:     int size)
        !            98: {
        !            99:   int i;
        !           100:   byte a, sid;
        !           101:   long x;
        !           102:   boolean bbs;
        !           103:   int bsb;
        !           104:   warn (LINF,"System header",EPST,1,0,size);
        !           105:   if (size < PS_SYSTHD_STREAM) {
        !           106:     warn (LWAR,"System header too short",EPST,1,1,size);
        !           107:     return (FALSE);
        !           108:   }
        !           109:   i = f->data.out;
        !           110:   list_incr (i,f->data,PES_HEADER_SIZE);
        !           111:   a = f->data.ptr[i];
        !           112:   marker_bit (a,7);
        !           113:   x = (a & 0x7F) << 8;
        !           114:   list_incr (i,f->data,1);
        !           115:   x = (x | f->data.ptr[i]) << 7;
        !           116:   list_incr (i,f->data,1);
        !           117:   a = f->data.ptr[i];
        !           118:   marker_bit (a,0);
        !           119:   f->u.ps.sh.ratebound = x | (a >> 1);
        !           120:   warn (LSEC,"rate_bound",EPST,1,5,f->u.ps.sh.ratebound);
        !           121:   list_incr (i,f->data,1);
        !           122:   a = f->data.ptr[i];
        !           123:   f->u.ps.sh.csps_flag = a & 0x01;
        !           124:   warn (LSEC,"csps_flag",EPST,1,6,f->u.ps.sh.csps_flag);
        !           125:   f->u.ps.sh.fixed_flag = (a >>= 1) & 0x01;
        !           126:   warn (LSEC,"fixed_flag",EPST,1,7,f->u.ps.sh.fixed_flag);
        !           127:   f->u.ps.sh.audiobound = a >> 1;
        !           128:   warn (LSEC,"audiobound",EPST,1,8,f->u.ps.sh.audiobound);
        !           129:   list_incr (i,f->data,1);
        !           130:   a = f->data.ptr[i];
        !           131:   marker_bit (a,5);
        !           132:   f->u.ps.sh.videobound = a & 0x1F;
        !           133:   warn (LSEC,"videobound",EPST,1,9,f->u.ps.sh.videobound);
        !           134:   f->u.ps.sh.system_video_lock_flag = (a >>= 6) & 0x01;
        !           135:   warn (LSEC,
        !           136:     "system_video_lock_flag",EPST,1,10,f->u.ps.sh.system_video_lock_flag);
        !           137:   f->u.ps.sh.system_audio_lock_flag = a >> 1;
        !           138:   warn (LSEC,
        !           139:     "system_audio_lock_flag",EPST,1,11,f->u.ps.sh.system_audio_lock_flag);
        !           140:   list_incr (i,f->data,1);
        !           141:   f->u.ps.sh.packet_rate_restriction_flag = f->data.ptr[i] >> 7;
        !           142:   warn (LSEC,"packet_rate_restriction_flag",
        !           143:     EPST,1,12,f->u.ps.sh.packet_rate_restriction_flag);
        !           144:   memset (f->u.ps.sh.buffer_bound,0,sizeof(f->u.ps.sh.buffer_bound));
        !           145:   size -= PS_SYSTHD_STREAM;
        !           146:   while ((size -= PS_SYSTHD_STRLEN) >= 0) {
        !           147:     list_incr (i,f->data,1);
        !           148:     a = f->data.ptr[i];
        !           149:     if (a & 0x80) {
        !           150:       sid = a;
        !           151:       warn (LSEC,"stream_id",EPST,1,13,sid);
        !           152:       list_incr (i,f->data,1);
        !           153:       a = f->data.ptr[i];
        !           154:       if (marker_check (a,0xC0,0xC0)) {
        !           155:         warn (LWAR,"Missing 11",EPST,1,3,a);
        !           156:         return (FALSE);
        !           157:       }
        !           158:       bbs = (a >> 5) & 0x01;
        !           159:       warn (LSEC,"buffer bound scale",EPST,1,14,bbs);
        !           160:       list_incr (i,f->data,1);
        !           161:       bsb = ((a & 0x1F) << 8) | f->data.ptr[i];
        !           162:       warn (LSEC,"buffer size bound",EPST,1,15,bsb);
        !           163:       /* register stream here, if auto */
        !           164:       if (a == PES_JOKER_AUDIO) {
        !           165:         a = PES_CODE_AUDIO;
        !           166:         x = PES_NUMB_AUDIO;
        !           167:       } else if (a == PES_JOKER_VIDEO) {
        !           168:         a = PES_CODE_VIDEO;
        !           169:         x = PES_NUMB_VIDEO;
        !           170:       } else if (a >= PES_LOWEST_SID) {
        !           171:         x = 1;
        !           172:       } else {
        !           173:         x = 0;
        !           174:       }
        !           175:       if (bbs) {
        !           176:         bsb = -bsb;
        !           177:       }
        !           178:       while (--x >= 0) {
        !           179:         f->u.ps.sh.buffer_bound[a-PES_LOWEST_SID] = bsb;
        !           180:         a += 1;
        !           181:       }
        !           182:     } else {
        !           183:       warn (LWAR,"Next bit 0",EPST,1,2,a);
        !           184:       return (FALSE);
        !           185:     }
        !           186:   }
        !           187:   if (size != -PS_SYSTHD_STRLEN) {
        !           188:     warn (LWAR,"System header length",EPST,1,4,size);
        !           189:     return (FALSE);
        !           190:   }
        !           191:   return (TRUE);
        !           192: }
        !           193: 
        !           194: static boolean ps_stream_map (file_descr *f,
        !           195:     int size)
        !           196: {
        !           197:   int i, psmv, psil, esil, esml;
        !           198:   byte a, styp, esid;
        !           199:   boolean cni;
        !           200:   warn (LINF,"Stream Map",EPST,4,0,size);
        !           201:   if ((size > (1018 + PES_HEADER_SIZE))
        !           202:    || (size < PS_STRMAP_SIZE)) {
        !           203:     warn (LWAR,"Map size bad",EPST,4,12,size);
        !           204:     return (FALSE);
        !           205:   }
        !           206:   i = f->data.out;
        !           207:   list_incr (i,f->data,PES_HEADER_SIZE);
        !           208:   a = f->data.ptr[i];
        !           209:   psmv = a & 0x1F;
        !           210:   warn (LSEC,"Map Version",EPST,4,1,psmv);
        !           211:   alloc_descriptor (f->u.ps.stream[0],0,0,psmv);
        !           212:   cni = a >> 7;
        !           213:   warn (LSEC,"Current Next",EPST,4,2,cni);
        !           214:   list_incr (i,f->data,2);
        !           215:   psil = f->data.ptr[i] << 8;
        !           216:   list_incr (i,f->data,1);
        !           217:   psil = psil | f->data.ptr[i];
        !           218:   list_incr (i,f->data,1);
        !           219:   warn (LSEC,"PS Info Length",EPST,4,3,psil);
        !           220:   size -= PS_STRMAP_SIZE;
        !           221:   if (size < psil) {
        !           222:     warn (LWAR,"Invalid Size",EPST,4,7,size);
        !           223:     return (FALSE);
        !           224:   }
        !           225:   size -= psil;
        !           226:   while (psil > 0) {
        !           227:     i = put_descriptor (f,f->u.ps.stream[0],i,&psil);
        !           228:   }
        !           229:   if (psil < 0) {
        !           230:     warn (LWAR,"PS Info Broken",EPST,4,4,psil);
        !           231:     return (FALSE);
        !           232:   }
        !           233:   if (cni) {
        !           234:     finish_descriptor (f->u.ps.stream[0]);
        !           235:   }
        !           236:   esml = f->data.ptr[i] << 8;
        !           237:   list_incr (i,f->data,1);
        !           238:   esml = esml | f->data.ptr[i];
        !           239:   list_incr (i,f->data,1);
        !           240:   warn (LSEC,"ES Map Length",EPST,4,5,esml);
        !           241:   if (size != esml) {
        !           242:     warn (LWAR,"Invalid Size",EPST,4,8,size);
        !           243:     return (FALSE);
        !           244:   }
        !           245:   while (esml > 0) {
        !           246:     styp = f->data.ptr[i];
        !           247:     list_incr (i,f->data,1);
        !           248:     esid = f->data.ptr[i];
        !           249:     list_incr (i,f->data,1);
        !           250:     esil = f->data.ptr[i] << 8;
        !           251:     list_incr (i,f->data,1);
        !           252:     esil = esil | f->data.ptr[i];
        !           253:     list_incr (i,f->data,1);
        !           254:     warn (LSEC,"Stream Type",EPST,4,9,styp);
        !           255:     warn (LSEC,"E Stream Id",EPST,4,10,esid);
        !           256:     warn (LSEC,"ES Info Length",EPST,4,11,esil);
        !           257:     if (esid >= PES_LOWEST_SID) {
        !           258:       if (f->automatic) {
        !           259:         if (f->u.ps.stream[esid] == NULL) {
        !           260:           f->u.ps.stream[esid] =
        !           261:             connect_streamprog (
        !           262:                 f,f->auto_programnb,esid,-esid,styp,
        !           263:                 NULL,f->u.ps.stream[0],FALSE);
        !           264:         }
        !           265:       }
        !           266:     }
        !           267:     esml -= (esil + 4);
        !           268:     if (esml >= 0) {
        !           269:       alloc_descriptor (f->u.ps.stream[0],esid,0,psmv);
        !           270:       while (esil > 0) {
        !           271:         i = put_descriptor (f,f->u.ps.stream[0],i,&esil);
        !           272:       }
        !           273:       if (esil < 0) {
        !           274:         warn (LWAR,"ES Map Broken",EPST,4,13,esil);
        !           275:         return (FALSE);
        !           276:       }
        !           277:       if (cni) {
        !           278:         finish_descriptor (f->u.ps.stream[0]);
        !           279:       }
        !           280:     }
        !           281:   }
        !           282:   if (esml < 0) {
        !           283:     warn (LWAR,"ES Map Broken",EPST,4,6,esml);
        !           284:     return (FALSE);
        !           285:   }
        !           286:   return (TRUE);
        !           287: }
        !           288: 
        !           289: static int ps_stream_dir_get45 (file_descr *f,
        !           290:     int i,
        !           291:     long long *r)
        !           292: {
        !           293:   byte a;
        !           294:   int b, n;
        !           295:   n = 2;
        !           296:   *r = 0;
        !           297:   do {
        !           298:     list_incr (i,f->data,1);
        !           299:     b = f->data.ptr[i];
        !           300:     list_incr (i,f->data,1);
        !           301:     a = f->data.ptr[i];
        !           302:     marker_bit (a,0);
        !           303:     b = (b << 7) | (a >> 1);
        !           304:     *r = (*r << 15) | b;
        !           305:   } while (--n >= 0);
        !           306:   return (i);
        !           307: }
        !           308: 
        !           309: static boolean ps_stream_directory (file_descr *f,
        !           310:     int size)
        !           311: {
        !           312:   int i, n;
        !           313:   long x;
        !           314:   byte a;
        !           315:   int numoau;
        !           316:   long long prevdo, nextdo;
        !           317:   warn (LINF,"Stream Dir",EPST,5,0,0);
        !           318:   i = f->data.out;
        !           319:   list_incr (i,f->data,PES_HEADER_SIZE);
        !           320:   x = f->data.ptr[i];
        !           321:   list_incr (i,f->data,1);
        !           322:   a = f->data.ptr[i];
        !           323:   marker_bit (a,0);
        !           324:   numoau = (x << 7) | (a >> 1);
        !           325:   warn (LSEC,"Num Acces Units",EPST,5,1,numoau);
        !           326:   if (size != (PS_STRDIR_SIZE + numoau * PS_STRDIR_SIZEAU)) {
        !           327:     warn (LWAR,"Invalid Size",EPST,5,2,size);
        !           328:     return (FALSE);
        !           329:   }
        !           330:   i = ps_stream_dir_get45 (f,i,&prevdo);
        !           331:   warn (LSEC,"Prev Dir Offset",EPST,5,3,((long)prevdo));
        !           332:   i = ps_stream_dir_get45 (f,i,&nextdo);
        !           333:   warn (LSEC,"Next Dir Offset",EPST,5,4,((long)nextdo));
        !           334:   n = 0;
        !           335:   while (n < numoau) {
        !           336:     byte psid;
        !           337:     boolean idi;
        !           338:     int refo, btr, cpi;
        !           339:     long long headpo;
        !           340:     clockref pts; /* and process all this ... */
        !           341:     list_incr (i,f->data,1);
        !           342:     psid = f->data.ptr[i];
        !           343:     warn (LSEC,"Packet Str Id",EPST,5,5,psid);
        !           344:     i = ps_stream_dir_get45 (f,i,&headpo);
        !           345:     if (headpo & (1LL << 44)) {
        !           346:       headpo = (1LL << 44) - headpo;
        !           347:     }
        !           348:     warn (LSEC,"Head Pos Offset",EPST,5,6,((long)headpo));
        !           349:     list_incr (i,f->data,1);
        !           350:     refo = f->data.ptr[i];
        !           351:     list_incr (i,f->data,1);
        !           352:     refo = (refo << 8) | f->data.ptr[i];
        !           353:     warn (LSEC,"Reference Offset",EPST,5,7,refo);
        !           354:     list_incr (i,f->data,1);
        !           355:     a = f->data.ptr[i];
        !           356:     marker_check (a,0x81,0x81);
        !           357:     pts.ba33 = (a >> 3) & 1;
        !           358:     x = a & 0x06;
        !           359:     list_incr (i,f->data,1);
        !           360:     x = (x << 7) | f->data.ptr[i];
        !           361:     list_incr (i,f->data,1);
        !           362:     a = f->data.ptr[i];
        !           363:     marker_bit (a,0);
        !           364:     x = (x << 8) | (a & 0xFE);
        !           365:     list_incr (i,f->data,1);
        !           366:     x = (x << 7) | f->data.ptr[i];
        !           367:     list_incr (i,f->data,1);
        !           368:     a = f->data.ptr[i];
        !           369:     marker_bit (a,0);
        !           370:     pts.base = (x << 7) | (a >> 1);
        !           371:     pts.ext = 0;
        !           372:     warn (LSEC,"PTS base",EPST,5,8,pts.base);
        !           373:     list_incr (i,f->data,1);
        !           374:     btr = f->data.ptr[i];
        !           375:     list_incr (i,f->data,1);
        !           376:     a = f->data.ptr[i];
        !           377:     marker_bit (a,0);
        !           378:     btr = (btr << 8) | (a & 0xFE);
        !           379:     list_incr (i,f->data,1);
        !           380:     btr = (btr << 7) | f->data.ptr[i];
        !           381:     list_incr (i,f->data,1);
        !           382:     a = f->data.ptr[i];
        !           383:     marker_bit (a,7);
        !           384:     cpi = (a >> 4) & 0x03;
        !           385:     idi = (a >> 6) & 0x01;
        !           386:     n += 1;
        !           387:   }
        !           388:   return (TRUE);
        !           389: }
        !           390: 
        !           391: static boolean ps_data_stream (file_descr *f,
        !           392:     int size,
        !           393:     byte sourceid)
        !           394: {
        !           395:   stream_descr *s;
        !           396:   ctrl_buffer *c;
        !           397:   warn (LINF,"Data Stream",EPST,6,0,size);
        !           398:   if ((f->u.ps.stream[sourceid] == NULL)
        !           399:    && (f->automatic)) {
        !           400:     f->u.ps.stream[sourceid] =
        !           401:       connect_streamprog (f,f->auto_programnb,sourceid,-sourceid,
        !           402:           guess_streamtype(sourceid),NULL,f->u.ps.stream[0],FALSE);
        !           403:   }
        !           404:   s = f->u.ps.stream[sourceid];
        !           405:   if (s != NULL) {
        !           406:     if ((!list_full (s->ctrl))
        !           407:      && (list_free (s->data) >= 2*size-1)) {
        !           408:       c = &s->ctrl.ptr[s->ctrl.in];
        !           409:       c->length = size;
        !           410:       f->payload += size;
        !           411:       f->total += size;
        !           412:       c->index = pes_transfer (&f->data,&s->data,size);
        !           413:       warn (LDEB,"Sequence",EPST,6,1,f->sequence);
        !           414:       c->sequence = f->sequence++;
        !           415:       c->scramble = 0;
        !           416:       c->time.read = msec_now ();
        !           417:       c->time.push = f->u.ps.time.read; /* good if multi packages per pack ? */
        !           418:       c->pcr.valid = FALSE;
        !           419:       c->opcr.valid = FALSE;
        !           420:       list_incr (s->ctrl.in,s->ctrl,1);
        !           421:       return (TRUE);
        !           422:     }
        !           423:     return (FALSE);
        !           424:   }
        !           425:   f->total += size;
        !           426:   list_incr (f->data.out,f->data,size);
        !           427:   return (TRUE);
        !           428: }
        !           429: 
        !           430: boolean split_ps (file_descr *f)
        !           431: {
        !           432:   int l, p;
        !           433:   byte a;
        !           434:   warn (LDEB,"Split PS",EPST,0,0,f);
        !           435:   if (pes_skip_to_prefix (f)) {
        !           436:     l = list_size (f->data);
        !           437:     if (l >= PES_HDCODE_SIZE) {
        !           438:       a = pes_stream_id (&f->data);
        !           439:       if (a >= PS_CODE_SYST_HDR) {
        !           440:         if (l >= PES_HEADER_SIZE) {
        !           441:           p = pes_packet_length (&f->data);
        !           442:           p += PES_HEADER_SIZE;
        !           443:           if (l >= p) {
        !           444:             switch (a) {
        !           445:               case PS_CODE_SYST_HDR:
        !           446:                 if (!ps_system_header (f,p)) {
        !           447:                   p = PES_SYNC_SIZE;
        !           448:                 }
        !           449:                 break;
        !           450:               case PES_CODE_STR_MAP:
        !           451:                 if (!ps_stream_map (f,p)) {
        !           452:                   p = PES_SYNC_SIZE;
        !           453:                 }
        !           454:                 break;
        !           455:               case PES_CODE_PADDING:
        !           456:                 break;
        !           457:               case PES_CODE_PRIVATE2:
        !           458: /*                p = PES_SYNC_SIZE; */
        !           459:                 break;
        !           460:               case PES_CODE_ECM:
        !           461: /*                p = PES_SYNC_SIZE; */
        !           462:                 break;
        !           463:               case PES_CODE_EMM:
        !           464: /*                p = PES_SYNC_SIZE; */
        !           465:                 break;
        !           466:               case PES_CODE_DSMCC:
        !           467: /*                p = PES_SYNC_SIZE; */
        !           468:                 break;
        !           469:               case PES_CODE_ITU222E:
        !           470: /*                p = PES_SYNC_SIZE; */
        !           471:                 break;
        !           472:               case PES_CODE_STR_DIR:
        !           473:                 if (!ps_stream_directory (f,p)) {
        !           474:                   p = PES_SYNC_SIZE;
        !           475:                 }
        !           476:                 break;
        !           477:               default:
        !           478:                 return (ps_data_stream (f,p,a));
        !           479:                 break;
        !           480:             }
        !           481:           } else {
        !           482:             p = 0;
        !           483:           }
        !           484:         } else {
        !           485:           p = 0;
        !           486:         }
        !           487:       } else if (a == PS_CODE_END) {
        !           488:         p = ps_program_end_code (f);
        !           489:       } else if (a == PS_CODE_PACK_HDR) {
        !           490:         p = ps_pack_header (f,l);
        !           491:       } else {
        !           492:         warn (LWAR,"Unknown Stream Id",EPST,0,1,a);
        !           493:         p = PES_SYNC_SIZE;
        !           494:       }
        !           495:       if (p > 0) {
        !           496:         f->total += p;
        !           497:         list_incr (f->data.out,f->data,p);
        !           498:         return (TRUE);
        !           499:       }
        !           500:     }
        !           501:   }
        !           502:   return (FALSE);
        !           503: }
        !           504: 

LinuxTV legacy CVS <linuxtv.org/cvs>