Skip to content

Commit 09d0d93

Browse files
committed
tegra: Add Tegra JPEG decoder
1 parent bdc2984 commit 09d0d93

2 files changed

Lines changed: 322 additions & 0 deletions

File tree

include/internal/libfreenect2/rgb_packet_processor.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,20 @@ class VaapiRgbPacketProcessor : public RgbPacketProcessor
123123
};
124124
#endif //LIBFREENECT2_WITH_VAAPI_SUPPORT
125125

126+
#ifdef LIBFREENECT2_WITH_TEGRAJPEG_SUPPORT
127+
class TegraJpegRgbPacketProcessorImpl;
128+
129+
class TegraJpegRgbPacketProcessor : public RgbPacketProcessor
130+
{
131+
public:
132+
TegraJpegRgbPacketProcessor();
133+
virtual ~TegraJpegRgbPacketProcessor();
134+
virtual bool good();
135+
virtual void process(const libfreenect2::RgbPacket &packet);
136+
private:
137+
TegraJpegRgbPacketProcessorImpl *impl_;
138+
};
139+
#endif //LIBFREENECT2_WITH_TEGRAJPEG_SUPPORT
140+
126141
} /* namespace libfreenect2 */
127142
#endif /* RGB_PACKET_PROCESSOR_H_ */
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
/*
2+
* This file is part of the OpenKinect Project. http://www.openkinect.org
3+
*
4+
* Copyright (c) 2014 individual OpenKinect contributors. See the CONTRIB file
5+
* for details.
6+
*
7+
* This code is licensed to you under the terms of the Apache License, version
8+
* 2.0, or, at your option, the terms of the GNU General Public License,
9+
* version 2.0. See the APACHE20 and GPL2 files for the text of the licenses,
10+
* or the following URLs:
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
* http://www.gnu.org/licenses/gpl-2.0.txt
13+
*
14+
* If you redistribute this file in source form, modified or unmodified, you
15+
* may:
16+
* 1) Leave this header intact and distribute it under the same terms,
17+
* accompanying it with the APACHE20 and GPL20 files, or
18+
* 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or
19+
* 3) Delete the GPL v2 clause and accompany it with the APACHE20 file
20+
* In all cases you must keep the copyright notice intact and include a copy
21+
* of the CONTRIB file.
22+
*
23+
* Binary distributions must follow the binary distribution requirements of
24+
* either License.
25+
*/
26+
27+
#include <libfreenect2/rgb_packet_processor.h>
28+
#include "libfreenect2/logging.h"
29+
30+
namespace nv_headers {
31+
#define DONT_USE_EXTERN_C
32+
#include "nv_headers/jpeglib.h"
33+
}
34+
35+
#include <dlfcn.h>
36+
37+
#ifdef LIBFREENECT2_WITH_CXX11_SUPPORT
38+
#define TYPEOF(expr) decltype(expr)
39+
#else
40+
#define TYPEOF(expr) __typeof__(expr)
41+
#endif
42+
43+
#define FOR_ALL(MACRO) \
44+
MACRO(jpeg_std_error) \
45+
MACRO(jpeg_CreateDecompress) \
46+
MACRO(jpeg_mem_src) \
47+
MACRO(jpeg_read_header) \
48+
MACRO(jpeg_start_decompress) \
49+
MACRO(jpeg_read_scanlines) \
50+
MACRO(jpeg_finish_decompress) \
51+
MACRO(jpeg_abort_decompress) \
52+
MACRO(jpeg_destroy_decompress)
53+
54+
class libjpeg_handle
55+
{
56+
private:
57+
void *handle;
58+
public:
59+
#define DECLARE(func) TYPEOF(&nv_headers::func) func;
60+
FOR_ALL(DECLARE)
61+
bool good;
62+
63+
#define INIT(func) func(0),
64+
libjpeg_handle(): FOR_ALL(INIT) good(false)
65+
{
66+
handle = dlopen(LIBFREENECT2_TEGRAJPEG_LIBRARY, RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);
67+
const char *err;
68+
err = dlerror();
69+
if (handle == NULL) {
70+
LOG_ERROR << "dlopen: " << err;
71+
return;
72+
}
73+
#define IMPORT(func) func = reinterpret_cast<TYPEOF(&nv_headers::func)>(dlsym(handle, #func)); if ((err = dlerror())) { LOG_ERROR << "dlsym: " << err; dlclose(handle); handle = NULL; return; }
74+
FOR_ALL(IMPORT)
75+
good = true;
76+
}
77+
78+
~libjpeg_handle()
79+
{
80+
if (handle)
81+
dlclose(handle);
82+
}
83+
};
84+
85+
using namespace nv_headers;
86+
87+
namespace libfreenect2
88+
{
89+
90+
class TegraImage: public Buffer
91+
{
92+
public:
93+
struct jpeg_decompress_struct dinfo;
94+
};
95+
96+
class TegraImageAllocator: public Allocator
97+
{
98+
public:
99+
void *owner;
100+
struct jpeg_error_mgr *jerr;
101+
libjpeg_handle &libjpeg;
102+
103+
TegraImageAllocator(void *owner, struct jpeg_error_mgr *jerr, libjpeg_handle &libjpeg):
104+
owner(owner), jerr(jerr), libjpeg(libjpeg) {}
105+
106+
virtual Buffer *allocate(size_t size)
107+
{
108+
TegraImage *ti = new TegraImage();
109+
ti->allocator = this;
110+
ti->dinfo.client_data = owner;
111+
ti->dinfo.err = jerr;
112+
libjpeg.jpeg_create_decompress(&ti->dinfo);
113+
return ti;
114+
}
115+
116+
virtual void free(Buffer *b)
117+
{
118+
if (b == NULL)
119+
return;
120+
TegraImage *ti = static_cast<TegraImage *>(b);
121+
libjpeg.jpeg_destroy_decompress(&ti->dinfo);
122+
delete ti;
123+
}
124+
};
125+
126+
class TegraFrame: public Frame
127+
{
128+
public:
129+
TegraFrame(size_t width, size_t height, size_t bpp, TegraImage *ti):
130+
Frame(width, height, bpp, (unsigned char*)-1)
131+
{
132+
data = NULL;
133+
rawdata = reinterpret_cast<unsigned char*>(ti);
134+
}
135+
136+
TegraImage *image()
137+
{
138+
return reinterpret_cast<TegraImage *>(rawdata);
139+
}
140+
141+
virtual ~TegraFrame()
142+
{
143+
image()->allocator->free(image());
144+
rawdata = NULL;
145+
}
146+
147+
void fill()
148+
{
149+
data = image()->dinfo.jpegTegraMgr->buff[0];
150+
}
151+
};
152+
153+
class TegraJpegRgbPacketProcessorImpl: public WithPerfLogging
154+
{
155+
public:
156+
libjpeg_handle libjpeg;
157+
struct jpeg_error_mgr jerr;
158+
size_t real_bib;
159+
160+
static const size_t WIDTH = 1920;
161+
static const size_t HEIGHT = 1080;
162+
static const size_t BPP = 4;
163+
164+
bool good;
165+
166+
TegraFrame *frame;
167+
168+
Allocator *image_allocator;
169+
170+
TegraJpegRgbPacketProcessorImpl():
171+
libjpeg(),
172+
good(true),
173+
frame(NULL),
174+
image_allocator(NULL)
175+
{
176+
if (!libjpeg.good) {
177+
good = false;
178+
return;
179+
}
180+
libjpeg.jpeg_std_error(&jerr);
181+
jerr.error_exit = TegraJpegRgbPacketProcessorImpl::my_error_exit;
182+
183+
image_allocator = new PoolAllocator(new TegraImageAllocator(reinterpret_cast<void*>(this), &jerr, libjpeg));
184+
185+
newFrame();
186+
}
187+
188+
~TegraJpegRgbPacketProcessorImpl()
189+
{
190+
delete frame;
191+
delete image_allocator;
192+
}
193+
194+
void newFrame()
195+
{
196+
frame = new TegraFrame(WIDTH, HEIGHT, BPP, static_cast<TegraImage *>(image_allocator->allocate(0)));
197+
}
198+
199+
static inline TegraJpegRgbPacketProcessorImpl *owner(j_decompress_ptr dinfo)
200+
{
201+
return static_cast<TegraJpegRgbPacketProcessorImpl*>(dinfo->client_data);
202+
}
203+
204+
static int fill_input_buffer(j_decompress_ptr dinfo)
205+
{
206+
dinfo->src->bytes_in_buffer = owner(dinfo)->real_bib;
207+
return 1;
208+
}
209+
210+
static void abort_jpeg_error(j_decompress_ptr dinfo, const char *msg)
211+
{
212+
owner(dinfo)->libjpeg.jpeg_abort_decompress(dinfo);
213+
LOG_ERROR << msg;
214+
owner(dinfo)->good = false;
215+
}
216+
217+
static void my_error_exit(j_common_ptr info)
218+
{
219+
char buffer[JMSG_LENGTH_MAX];
220+
info->err->format_message(info, buffer);
221+
abort_jpeg_error((j_decompress_ptr)info, buffer);
222+
}
223+
224+
void decompress(unsigned char *buf, size_t len)
225+
{
226+
j_decompress_ptr dinfo = &frame->image()->dinfo;
227+
libjpeg.jpeg_mem_src(dinfo, buf, len);
228+
229+
// This hack prevents an extra memcpy in jpeg_read_header
230+
real_bib = len;
231+
dinfo->src->bytes_in_buffer = 0;
232+
dinfo->src->fill_input_buffer = TegraJpegRgbPacketProcessorImpl::fill_input_buffer;
233+
libjpeg.jpeg_read_header(dinfo, true);
234+
235+
if (dinfo->progressive_mode)
236+
abort_jpeg_error(dinfo, "Tegra HW doesn't support progressive JPEG; use TurboJPEG");
237+
238+
if (!dinfo->tegra_acceleration)
239+
abort_jpeg_error(dinfo, "Tegra HW acceleration is disabled unexpectedly");
240+
241+
if (dinfo->image_width != WIDTH || dinfo->image_height != HEIGHT)
242+
abort_jpeg_error(dinfo, "image dimensions does not match preset");
243+
244+
dinfo->out_color_space = JCS_RGBA_8888;
245+
246+
libjpeg.jpeg_start_decompress(dinfo);
247+
248+
// Hardware acceleration returns the entire surface in one go.
249+
// The normal way with software decoding uses jpeg_read_scanlines with loop.
250+
if (libjpeg.jpeg_read_scanlines(dinfo, NULL, 0) != dinfo->output_height || dinfo->output_height != HEIGHT)
251+
abort_jpeg_error(dinfo, "Incomplete decoding result");
252+
253+
/* Empirically: 1 surface for RGBA; 3 surfaces for YUV */
254+
//size_t pitch = dinfo->jpegTegraMgr->pitch[0];
255+
//unsigned char *surface = dinfo->jpegTegraMgr->buff[0];
256+
//if (pitch == 0 || surface == NULL)
257+
// abort_jpeg_error(dinfo, "Empty result buffer");
258+
259+
frame->fill();
260+
261+
libjpeg.jpeg_finish_decompress(dinfo);
262+
}
263+
};
264+
265+
TegraJpegRgbPacketProcessor::TegraJpegRgbPacketProcessor() :
266+
impl_(new TegraJpegRgbPacketProcessorImpl())
267+
{
268+
}
269+
270+
TegraJpegRgbPacketProcessor::~TegraJpegRgbPacketProcessor()
271+
{
272+
delete impl_;
273+
}
274+
275+
bool TegraJpegRgbPacketProcessor::good()
276+
{
277+
return impl_->good;
278+
}
279+
280+
void TegraJpegRgbPacketProcessor::process(const RgbPacket &packet)
281+
{
282+
if (listener_ == NULL)
283+
return;
284+
285+
if (!impl_->good) {
286+
LOG_ERROR << "Tegra jpeg is in error state";
287+
return;
288+
}
289+
290+
impl_->startTiming();
291+
292+
impl_->frame->timestamp = packet.timestamp;
293+
impl_->frame->sequence = packet.sequence;
294+
impl_->frame->exposure = packet.exposure;
295+
impl_->frame->gain = packet.gain;
296+
impl_->frame->gamma = packet.gamma;
297+
298+
impl_->decompress(packet.jpeg_buffer, packet.jpeg_buffer_length);
299+
300+
impl_->stopTiming(LOG_INFO);
301+
302+
if (impl_->good) {
303+
if (listener_->onNewFrame(Frame::Color, impl_->frame))
304+
impl_->newFrame();
305+
}
306+
}
307+
} /* namespace libfreenect2 */

0 commit comments

Comments
 (0)