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
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.