1ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu/*
2ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * Copyright (C) Texas Instruments - http://www.ti.com/
3ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu *
4ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * Licensed under the Apache License, Version 2.0 (the "License");
5ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * you may not use this file except in compliance with the License.
6ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * You may obtain a copy of the License at
7ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu *
8ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu *      http://www.apache.org/licenses/LICENSE-2.0
9ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu *
10ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * Unless required by applicable law or agreed to in writing, software
11ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * distributed under the License is distributed on an "AS IS" BASIS,
12ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * See the License for the specific language governing permissions and
14ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * limitations under the License.
15ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu */
16ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
17ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu/**
18ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu* @file Encoder_libjpeg.h
19ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu*
20ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu* This defines API for camerahal to encode YUV using libjpeg
21ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu*
22ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu*/
23ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
24ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu#ifndef ANDROID_CAMERA_HARDWARE_ENCODER_LIBJPEG_H
25ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu#define ANDROID_CAMERA_HARDWARE_ENCODER_LIBJPEG_H
26ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
27ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu#include <utils/threads.h>
28ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu#include <utils/RefBase.h>
29ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
3036e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luuextern "C" {
3136e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu#include "jhead.h"
3236e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu}
330c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu
340c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu#define CANCEL_TIMEOUT 3000000 // 3 seconds
350c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu
36ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luunamespace android {
37ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu/**
38ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu * libjpeg encoder class - uses libjpeg to encode yuv
39ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu */
40ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
4136e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu#define MAX_EXIF_TAGS_SUPPORTED 30
42c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luutypedef void (*encoder_libjpeg_callback_t) (void* main_jpeg,
43c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                                            void* thumb_jpeg,
44ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu                                            CameraFrame::FrameType type,
45ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu                                            void* cookie1,
46ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu                                            void* cookie2,
470c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu                                            void* cookie3,
480c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu                                            bool canceled);
49ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
50ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luu// these have to match strings defined in external/jhead/exif.c
5136e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_MODEL[] = "Model";
5236e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_MAKE[] = "Make";
5336e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_FOCALLENGTH[] = "FocalLength";
5436e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_DATETIME[] = "DateTime";
5536e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_IMAGE_WIDTH[] = "ImageWidth";
5636e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_IMAGE_LENGTH[] = "ImageLength";
5736e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_LAT[] = "GPSLatitude";
5836e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_LAT_REF[] = "GPSLatitudeRef";
5936e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_LONG[] = "GPSLongitude";
6036e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_LONG_REF[] = "GPSLongitudeRef";
6136e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_ALT[] = "GPSAltitude";
6236e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_ALT_REF[] = "GPSAltitudeRef";
6336e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_MAP_DATUM[] = "GPSMapDatum";
6436e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_PROCESSING_METHOD[] = "GPSProcessingMethod";
6536e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_VERSION_ID[] = "GPSVersionID";
6636e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_TIMESTAMP[] = "GPSTimeStamp";
6736e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_GPS_DATESTAMP[] = "GPSDateStamp";
6836e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luustatic const char TAG_ORIENTATION[] = "Orientation";
69ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_FLASH[] = "Flash";
70ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_DIGITALZOOMRATIO[] = "DigitalZoomRatio";
71ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_EXPOSURETIME[] = "ExposureTime";
72ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_APERTURE[] = "ApertureValue";
73ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_ISO_EQUIVALENT[] = "ISOSpeedRatings";
74ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_WHITEBALANCE[] = "WhiteBalance";
75ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_LIGHT_SOURCE[] = "LightSource";
76ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_METERING_MODE[] = "MeteringMode";
77ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_EXPOSURE_PROGRAM[] = "ExposureProgram";
78ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_COLOR_SPACE[] = "ColorSpace";
79ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_CPRS_BITS_PER_PIXEL[] = "CompressedBitsPerPixel";
80ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_FNUMBER[] = "FNumber";
81ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_SHUTTERSPEED[] = "ShutterSpeedValue";
82ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_SENSING_METHOD[] = "SensingMethod";
83ef73bec8a83a2fd7fbb072d009382c64eb2da0eeTyler Luustatic const char TAG_CUSTOM_RENDERED[] = "CustomRendered";
8436e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu
8536e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luuclass ExifElementsTable {
8636e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu    public:
8736e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        ExifElementsTable() :
8836e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu           gps_tag_count(0), exif_tag_count(0), position(0),
891e3a7ee3fced4b9723f65147ae856bba7d4e2342Angus Kong           jpeg_opened(false), has_datetime_tag(false) { }
9036e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        ~ExifElementsTable();
9136e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu
9236e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        status_t insertElement(const char* tag, const char* value);
9336e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        void insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size);
94c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu        status_t insertExifThumbnailImage(const char*, int);
9536e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        void saveJpeg(unsigned char* picture, size_t jpeg_size);
96c78626b15e9f29a5bcf85447ceafb17dcbf58b69Emilian Peev        static const char* degreesToExifOrientation(unsigned int);
97c11c07d676f130e6e28ab1611f4862a01a160389Tyler Luu        static void stringToRational(const char*, unsigned int*, unsigned int*);
98c11c07d676f130e6e28ab1611f4862a01a160389Tyler Luu        static bool isAsciiTag(const char* tag);
9936e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu    private:
10036e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        ExifElement_t table[MAX_EXIF_TAGS_SUPPORTED];
10136e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        unsigned int gps_tag_count;
10236e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        unsigned int exif_tag_count;
10336e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        unsigned int position;
10436e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu        bool jpeg_opened;
1051e3a7ee3fced4b9723f65147ae856bba7d4e2342Angus Kong        bool has_datetime_tag;
10636e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu};
10736e9bdd56757ff8048e08f6e52f234480c44f122Tyler Luu
108ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luuclass Encoder_libjpeg : public Thread {
109c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu    /* public member types and variables */
110c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu    public:
111c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu        struct params {
112c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            uint8_t* src;
113c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            int src_size;
114c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            uint8_t* dst;
115c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            int dst_size;
116c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            int quality;
117c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            int in_width;
118c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            int in_height;
119c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            int out_width;
120c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            int out_height;
121d9520b9de06f01b9411307040cf245e6fc7fe361Milen Mitkov            int right_crop;
122d9520b9de06f01b9411307040cf245e6fc7fe361Milen Mitkov            int start_offset;
123c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            const char* format;
124c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            size_t jpeg_size;
125c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu         };
126c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu    /* public member functions */
127ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu    public:
128c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu        Encoder_libjpeg(params* main_jpeg,
129c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                        params* tn_jpeg,
130c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                        encoder_libjpeg_callback_t cb,
131c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                        CameraFrame::FrameType type,
132c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                        void* cookie1,
133c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                        void* cookie2,
134c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                        void* cookie3)
135c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            : Thread(false), mMainInput(main_jpeg), mThumbnailInput(tn_jpeg), mCb(cb),
1368e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu              mCancelEncoding(false), mCookie1(cookie1), mCookie2(cookie2), mCookie3(cookie3),
1378e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu              mType(type), mThumb(NULL) {
138ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu            this->incStrong(this);
1390c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu            mCancelSem.Create(0);
140ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        }
141ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
142ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        ~Encoder_libjpeg() {
1438e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu            CAMHAL_LOGVB("~Encoder_libjpeg(%p)", this);
144ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        }
145ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
146ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        virtual bool threadLoop() {
147c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            size_t size = 0;
148c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            sp<Encoder_libjpeg> tn = NULL;
149c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            if (mThumbnailInput) {
150c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                // start thread to encode thumbnail
1518e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu                mThumb = new Encoder_libjpeg(mThumbnailInput, NULL, NULL, mType, NULL, NULL, NULL);
1528e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu                mThumb->run();
153c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            }
154c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu
155c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            // encode our main image
156c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            size = encode(mMainInput);
157c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu
1580c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu            // signal cancel semaphore incase somebody is waiting
1590c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu            mCancelSem.Signal();
1600c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu
161c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            // check if it is main jpeg thread
1628e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu            if(mThumb.get()) {
163c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu                // wait until tn jpeg thread exits.
1648e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu                mThumb->join();
1658e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu                mThumb.clear();
1668e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu                mThumb = NULL;
167c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu            }
168c11c07d676f130e6e28ab1611f4862a01a160389Tyler Luu
169c11c07d676f130e6e28ab1611f4862a01a160389Tyler Luu            if(mCb) {
1700c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu                mCb(mMainInput, mThumbnailInput, mType, mCookie1, mCookie2, mCookie3, mCancelEncoding);
171c11c07d676f130e6e28ab1611f4862a01a160389Tyler Luu            }
172c11c07d676f130e6e28ab1611f4862a01a160389Tyler Luu
173ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu            // encoder thread runs, self-destructs, and then exits
174ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu            this->decStrong(this);
175ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu            return false;
176ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        }
177ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
1788e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu        void cancel() {
1790c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu           mCancelEncoding = true;
1808e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu           if (mThumb.get()) {
1818e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu               mThumb->cancel();
1820c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu               mCancelSem.WaitTimeout(CANCEL_TIMEOUT);
1838e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu           }
1840c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu        }
1850c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu
1860c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu        void getCookies(void **cookie1, void **cookie2, void **cookie3) {
1870c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu            if (cookie1) *cookie1 = mCookie1;
1880c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu            if (cookie2) *cookie2 = mCookie2;
1890c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu            if (cookie3) *cookie3 = mCookie3;
1908e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu        }
1918e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu
192ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu    private:
193c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu        params* mMainInput;
194c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu        params* mThumbnailInput;
195ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        encoder_libjpeg_callback_t mCb;
1968e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu        bool mCancelEncoding;
197ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        void* mCookie1;
198ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        void* mCookie2;
199ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        void* mCookie3;
200ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu        CameraFrame::FrameType mType;
2018e52e3bcc31f65a699c25557cf3026d324e631b4Tyler Luu        sp<Encoder_libjpeg> mThumb;
2020c7ae887ee41852f275887fd543fae810668e2d1Tyler Luu        Semaphore mCancelSem;
203ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
204c160a1f85c70e49a7613d774e2d99035f3cb4851Tyler Luu        size_t encode(params*);
205ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu};
206ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
207ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu}
208ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu
209ee6bb64f60c228d711dc1d6875d8f4b0ed88b6cfTyler Luu#endif
210