~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Open Mash Cross Reference
mash/codec/video/transcoder-jpeg.cc

Component: ~ [ mash ] ~ [ apps ] ~ [ gsm ] ~ [ lib ] ~ [ otcl ] ~ [ srm ] ~ [ tcl8.3 ] ~ [ tclcl ] ~ [ tk8.3 ] ~ [ tutorials ] ~

  1 /*
  2  * transcoder-jpeg.cc --
  3  *
  4  *      Motion Jpeg Transcoder
  5  *
  6  * Copyright (c) 1993-2002 The Regents of the University of California.
  7  * All rights reserved.
  8  *
  9  * Redistribution and use in source and binary forms, with or without
 10  * modification, are permitted provided that the following conditions are met:
 11  *
 12  * A. Redistributions of source code must retain the above copyright notice,
 13  *    this list of conditions and the following disclaimer.
 14  * B. Redistributions in binary form must reproduce the above copyright notice,
 15  *    this list of conditions and the following disclaimer in the documentation
 16  *    and/or other materials provided with the distribution.
 17  * C. Neither the names of the copyright holders nor the names of its
 18  *    contributors may be used to endorse or promote products derived from this
 19  *    software without specific prior written permission.
 20  *
 21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
 22  * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 31  * POSSIBILITY OF SUCH DAMAGE.
 32  */
 33 
 34 static const char rcsid[] =
 35     "@(#) $Header: /usr/mash/src/repository/mash/mash-1/codec/video/transcoder-jpeg.cc,v 1.16 2003/11/19 19:20:24 aswan Exp $";
 36 
 37 #include <string.h>
 38 #include "rtp/inet.h"
 39 #include "codec/transcoder.h"
 40 #include "codec/jpeg/jpeg.h"
 41 #include "rtp/rtp.h"
 42 
 43 #ifdef INFOPAD
 44 #include "ipadchan.h"
 45 #endif /* INFOPAD */
 46 
 47 class JpegTranscoder : public VideoTranscoder {
 48 public:
 49         JpegTranscoder();
 50         ~JpegTranscoder();
 51 protected:
 52         virtual void configure();
 53         void process_hdr(const jpeghdr*);
 54         u_char* reassemble(const rtphdr* rh, const u_char* bp, int& len);
 55 
 56         int decimate_;
 57         int ndec_;
 58 
 59         /*
 60          * Reassembly buffer.  This should be a dma buffer in the
 61          * jvdriver case but it's not clear if jvdriver allows buffers
 62          * to be shared across sockets. FIXME ask Lance
 63          * If it does, then we can pass dma buffers betwee
 64          * the decoder and renderers.
 65          */
 66 #define JPEG_SLOTS 64
 67 #define JPEG_SLOTMASK (JPEG_SLOTS - 1)
 68         struct slot {
 69                 int seqno;
 70                 int eof;        /* nbytes in last pkt of frame, o.w. 0 */
 71                 u_int32_t off;
 72                 u_int32_t ts;
 73         } slots_[JPEG_SLOTS];
 74 
 75         /*
 76          * Reassembly buffers.  We do double-buffering, which allows
 77          * packets to arrive out of order across frame boundaries,
 78          * but not across an entire frame (i.e., we don't want to see
 79          * any packets from frame k+2 until we're done with frame k).
 80          * We use RTP timestamps to keep track of which frame is
 81          * allocated to which buffer.  (RTP guarantees that the
 82          * timestamps are constant across a frame, and increase
 83          * between succesive frames.  FIXME is latter really true?)
 84          */
 85         struct rbuf {
 86                 int drop;
 87                 u_int32_t ts;
 88                 u_char* bp;
 89         };
 90         rbuf rb0_;
 91         rbuf rb1_;
 92         int rbsize_;
 93 
 94         JpegDecoder* decoder_;
 95 
 96         int type_;
 97         int inq_;
 98 
 99         JpegDecoder::config config_;
100 };
101 
102 class JpegPixelTranscoder : public JpegTranscoder {
103 public:
104         virtual void configure();
105         virtual void recv_data(pktbuf* pb);
106 };
107 
108 class JpegDCTTranscoder : public JpegTranscoder {
109 public:
110         virtual void configure();
111         virtual void recv_data(pktbuf* pb);
112 };
113 
114 static class JpegDCTTranscoderClass : public TclClass {
115 public:
116         JpegDCTTranscoderClass() : TclClass("Transcoder/JPEG/DCT") {}
117         TclObject* create(int, const char*const*) {
118                 return(new JpegDCTTranscoder);
119         }
120 } jpeg_dct_transcoder_class;
121 
122 static class JpegPixelTranscoderClass : public TclClass {
123 public:
124         JpegPixelTranscoderClass() : TclClass("Transcoder/JPEG/Pixel") {}
125         TclObject* create(int, const char*const*) {
126                 return(new JpegPixelTranscoder);
127         }
128 } jpeg_pixel_transcoder_class;
129 
130 /*
131  * Initial size of each reassembly buffer.
132  */
133 #define JPEG_BUFSIZE (16*1024)
134 
135 JpegTranscoder::JpegTranscoder()
136                :VideoTranscoder(sizeof(jpeghdr)), decimate_(0), ndec_(0),
137                 decoder_(0), type_(-1), inq_(-1)
138 {
139         rbsize_ = JPEG_BUFSIZE;
140         rb0_.bp = new u_char[2 * JPEG_BUFSIZE];
141         rb0_.ts = ~0;
142         rb0_.drop = 0;
143         rb1_.bp = &rb0_.bp[JPEG_BUFSIZE];
144         rb1_.ts = ~0;
145         rb1_.drop = 0;
146         memset(slots_, 0, sizeof(slots_));
147 
148         JpegDecoder::defaults(config_);
149 }
150 
151 void JpegTranscoder::configure()
152 {
153         config_.comp[0].hsf = 2;
154         config_.comp[0].vsf = (csss_ == 420) ? 2 : 1;
155         config_.comp[1].hsf = 1;
156         config_.comp[1].vsf = 1;
157         config_.comp[2].hsf = 1;
158         config_.comp[2].vsf = 1;
159         config_.width = inw_;
160         config_.height = inh_;
161 
162         JpegDecoder::quantizer(config_, inq_);
163 
164 }
165 
166 void JpegDCTTranscoder::configure()
167 {
168         JpegTranscoder::configure();
169         crinit(outw_, outh_);
170 
171         delete decoder_;
172         decoder_ = JpegDCTDecoder::create(config_, outw_, outh_);
173         decoder_->thresh(90);
174 }
175 
176 void JpegPixelTranscoder::configure()
177 {
178         JpegTranscoder::configure();
179         pixel_crinit(outw_, outh_);
180 
181         delete decoder_;
182         decoder_ = JpegPixelDecoder::create(config_, outw_, outh_);
183         decoder_->thresh(6);
184 }
185 
186 JpegTranscoder::~JpegTranscoder()
187 {
188         delete decoder_;
189         delete rb0_.bp;
190 }
191 
192 /*
193  * Reassemble an RTP/JPEG stream.  Return a pointer to a buffer
194  * each time we encounter an entire frame.  Otherwise, return 0.
195  * Set len to the length of the jpeg data in the buffer.
196  */
197 u_char* JpegTranscoder::reassemble(const rtphdr* rh, const u_char* bp, int& len)
198 {
199         jpeghdr* p = (jpeghdr*)(rh + 1);
200         int off = (int)ntohl(p->off);
201         int cc = len;
202 
203         if (off + cc > rbsize_) {
204                 /*
205                  * Grow reassembly buffers.
206                  */
207                 int nsize = rbsize_;
208                 do {
209                         nsize <<= 1;
210                 } while (off + cc > nsize);
211                 u_char* p = new u_char[2 * nsize];
212                 memcpy(p, rb0_.bp, rbsize_);
213                 memcpy(p + nsize, rb1_.bp, rbsize_);
214                 delete rb0_.bp;
215                 rb0_.bp = p;
216                 rb1_.bp = p + nsize;
217                 rbsize_ = nsize;
218         }
219         /*
220          * Initialize the slot data structure.
221          */
222         int seqno = ntohs(rh->rh_seqno);
223         int s = seqno & JPEG_SLOTMASK;
224         u_int32_t ts = ntohl(rh->rh_ts);
225         slots_[s].seqno = seqno;
226         slots_[s].off = off;
227         slots_[s].ts = ts;
228         /*
229          * Figure out which reassembly-buffer to use.  If we're not
230          * already reassembling this frame, take over the older buffer.
231          */
232         rbuf* rb;
233         if (ts == rb0_.ts)
234                 rb = &rb0_;
235         else if (ts == rb1_.ts)
236                 rb = &rb1_;
237         else {
238                 rb = ((int)(rb0_.ts - rb1_.ts) < 0) ? &rb0_ : &rb1_;
239                 rb->ts = ts;
240                 rb->drop = 0;
241                 /*
242                  * If we're decimating frames (to save cycles),
243                  * remember that we might want to drop the rest
244                  * of the packets from this frame.
245                  */
246                 if (decimate_) {
247                         if (--ndec_ <= 0)
248                                 ndec_ = decimate_;
249                         else
250                                 rb->drop = 1;
251                 }
252         }
253         if (rb->drop)
254                 return (0);
255 
256         memcpy((char*)&rb->bp[off], (char*)bp, cc);
257 
258         /*
259          * Check if we're at end-of-frame.  If not, see if we're
260          * filling a hole.  If not, return.  Otherwise, drop out
261          * below and check for an entire frame.  We set cc to be
262          * the entire frame size in the if-else below.
263          */
264         if ((ntohs(rh->rh_flags) & RTP_M) != 0) {
265                 slots_[s].eof = cc;
266                 cc += off;
267         } else {
268                 slots_[s].eof = 0;
269                 int ns = s;
270                 do {
271                         ns = (ns + 1) & JPEG_SLOTMASK;
272                         if (slots_[ns].ts != ts || ns == s)
273                                 return (0);
274                 } while (slots_[ns].eof != 0);
275                 cc = int(slots_[ns].eof + slots_[ns].off);
276         }
277         /*
278          * At this point, we know we have an end-of-frame, and
279          * all packets from slot 's' up until the end-of-frame.
280          * Scan backward from slot 's' making sure we have all
281          * packets from the start-of-frame (off == 0) to 's'.
282          */
283         int ps = s;
284         do {
285                 ps = (ps - 1) & JPEG_SLOTMASK;
286                 if (slots_[ps].ts != ts || ps == s)
287                         return (0);
288         } while (slots_[ps].off != 0);
289 
290         len = cc;
291         return (rb->bp);
292 }
293 
294 void JpegTranscoder::process_hdr(const jpeghdr* p)
295 {
296         int reconfig = 0;
297 
298         if (p->type != type_) {
299                 type_ = p->type;
300                 csss_ = ((type_ == 1) || (type_ == 65)) ? 420 : 422;
301                 reconfig = 1;
302         }
303 
304         int q = p->q;
305         if (q != inq_) {
306                 JpegDecoder::quantizer(config_, q);
307                 inq_ = q;
308                 reconfig = 1;
309         }
310 
311         int inw = p->width << 3;
312         int inh = p->height << 3;
313         if (inw_ !=  inw || inh_ != inh) {
314                 inw_ = inw;
315                 inh_ = inh;
316                 Tcl& tcl = Tcl::instance();
317                 tcl.evalf("%s frame_width %d", encoder_->name(), inw);
318                 outw_ = atoi(tcl.result());
319                 tcl.evalf("%s frame_height %d", encoder_->name(), inh);
320                 outh_ = atoi(tcl.result());
321                 reconfig = 1;
322         }
323 
324         if (reconfig)
325                 configure();
326 }
327 
328 void JpegDCTTranscoder::recv_data(pktbuf *pb)
329 {
330         rtphdr* rh = (rtphdr*)pb->dp;
331         int cc = pb->len - sizeof(rtphdr);
332         const u_char* bp = (const u_char*)(rh + 1);
333 
334         jpeghdr* p = (jpeghdr*)(rh + 1);
335         process_hdr(p);
336 
337         bp += sizeof(jpeghdr);
338         cc -= sizeof(jpeghdr);
339 
340         bp = reassemble(rh, bp, cc);
341         pb->release();
342         if (bp == 0) {
343                 have_frame_ = 0;
344                 return;
345         }
346 
347         have_frame_ = 1;
348         double now;
349         if (!txonly_) {
350                 if (bps_ == 0)
351                         return;
352                 now = gettimeofday();
353         }
354 
355         if (fc_ <= now || txonly_) {
356                 /* If we have fallen behind (>200ms), re-sync. */
357                 if (now - fc_ > 200000.)
358                         fc_ = now;
359                 // XXX not necessarily FULL_FRAME, should read
360                 // from rtp jpeg header
361                 decoder_->decode(bp, cc, JPEG_FULL_FRAME, crvec_, mark_);
362                 DCTFrame df(ntohl(rh->rh_ts),
363                             ((JpegDCTDecoder*)decoder_)->frame(), crvec_,
364                               outw_, outh_, csss_);
365                 int nb = encoder_->nb();
366                 encoder_->recv(&df);
367                 obytes_ = encoder_->nb();
368                 nb = obytes_ - nb;
369                 double bits = 8 * nb;
370                 lastfc_ = fc_;
371                 fc_ += 1e6 * bits / bps_;
372                 ofrms_++;
373 
374                 mark_ = age_blocks() | CR_MOTION_BIT | CR_LQ;
375         }
376 }
377 
378 void JpegPixelTranscoder::recv_data(pktbuf *pb)
379 {
380         rtphdr* rh = (rtphdr*)pb->dp;
381         int cc = pb->len - sizeof(rtphdr);
382         const u_char* bp = (const u_char*)(rh + 1);
383 
384         jpeghdr* p = (jpeghdr*)(rh + 1);
385         process_hdr(p);
386 
387         bp += sizeof(jpeghdr);
388         cc -= sizeof(jpeghdr);
389 
390         bp = reassemble(rh, bp, cc);
391         if (bp == 0) {
392                 pb->release();
393                 have_frame_ = 0;
394                 return;
395         }
396 
397         have_frame_ = 1;
398         double now;
399         if (!txonly_) {
400                 if (bps_ == 0) {
401                         pb->release();
402                         return;
403                 }
404                 now = gettimeofday();
405         }
406 
407         if (fc_ <= now || txonly_) {
408                 /* If we have fallen behind (>200ms), re-sync. */
409                 if (now - fc_ > 200000.)
410                         fc_ = now;
411                 
412                 // XXX not necessarily FULL_FRAME, should read
413                 // from rtp jpeg header
414                 decoder_->decode(bp, cc, JPEG_FULL_FRAME, crvec_, mark_);
415                 blk_to_mb_cr();
416                 YuvFrame f(ntohl(rh->rh_ts),
417                            ((JpegPixelDecoder*)decoder_)->frame(), mb_crvec_,
418                               outw_, outh_, csss_);
419                 int nb = encoder_->nb();
420                 encoder_->recv(&f);
421                 obytes_ = encoder_->nb();
422                 nb = obytes_ - nb;
423                 double bits = 8 * nb;
424                 lastfc_ = fc_;
425                 fc_ += 1e6 * bits / bps_;
426                 ofrms_++;
427 
428                 mark_ = age_blocks() | CR_MOTION_BIT | CR_LQ;
429         }
430         pb->release();
431 }
432 
433 #ifdef INFOPAD
434 void JpegIpadVQTranscoder::configure(const u_char* p, Network* n)
435 {
436         JpegTranscoder::configure(p, n);
437 
438         IpadChannel* c = (IpadChannel*)n;
439         encoder_ = new IpadVQEncoder(c, outw_, outh_, csss_, 0);
440         make_decoder();
441 }
442 
443 void JpegIpadVQTranscoder::resize(int w, int h)
444 {
445         outw_ = inw_ = w;
446         outh_ = inh_ = h;
447         /* FIXME */
448         u_int bps = encoder_->bps();
449         delete encoder_;
450         encoder_ = new IpadVQEncoder((IpadChannel*)net_, outw_, outh_,
451                                      csss_, 0);
452         encoder_->bps(bps);
453 }
454 
455 void JpegIpadVQTranscoder::make_decoder(void)
456 {
457         delete decoder_;
458         crinit(outw_, outh_);
459 /*FIXME*/
460 #ifdef notdef
461         Tcl& tcl = Tcl::instance();
462         int q = atoi(tcl.attr("softJPEGthresh"));
463         if (q < 0)
464                 q = JpegDecoder::q_to_thresh(inq_);
465         decoder_->thresh(q);
466         decoder_->cthresh(atoi(tcl.attr("softJPEGcthresh")));
467 #endif
468 }
469 
470 int JpegIpadVQTranscoder::recv(const rtphdr* rh, const u_char* bp, int cc, int)
471 {
472         jpeghdr* p = (jpeghdr*)(rh + 1);
473 
474         int reconfig = process_hdr(p);
475         if (reconfig)
476                 reconfigure();
477 
478         bp += sizeof(jpeghdr);
479         cc -= sizeof(jpeghdr);
480 
481         bp = reassemble(rh, bp, cc);
482         if (bp == 0)
483                 return (0);
484 
485         if (bps() == 0)
486                 return (1);
487 
488         double now = gettimeofday();
489         if (fc_ <= now) {
490                 /* If we have fallen behind (>200ms), re-sync. */
491                 if (now - fc_ > 200000.)
492                         fc_ = now;
493                 JpegPixelDecoder* d = (JpegPixelDecoder*)decoder_;
494                 d->decode(bp, cc, crvec_, CR_SEND|CR_MOTION);
495                 int cc = encoder()->encode(d->frame(), outw_, outh_, crvec_);
496                 age_blocks();
497                 double bits = 8 * double(cc);
498                 obits_ += int(bits);
499                 ofrms_ += 1;
500                 opkts_ = encoder()->np();
501                 lastfc_ = fc_;
502                 fc_ += 1e6 * bits / double(bps());
503         }
504         return (1);
505 }
506 #endif /* INFOPAD */
507 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.