ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

mjpeg.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // Based on LGPL MJPEG/AVI to JPEG/JFIF conversion code from libav
00024 // Copyright (c) 2010 Adrian Daerr and Nicolas George
00025 // That in turn was adapted from mjpeg2jpeg.c, with original copyright:
00026 // Paris 2010 Adrian Daerr, public domain
00027 
00028 #include "common/memstream.h"
00029 #include "common/system.h"
00030 #include "common/textconsole.h"
00031 #include "graphics/surface.h"
00032 #include "image/jpeg.h"
00033 
00034 #include "image/codecs/mjpeg.h"
00035 
00036 namespace Common {
00037 class SeekableReadStream;
00038 }
00039 
00040 namespace Image {
00041 
00042 MJPEGDecoder::MJPEGDecoder() : Codec() {
00043     _pixelFormat = g_system->getScreenFormat();
00044     _surface = 0;
00045 }
00046 
00047 MJPEGDecoder::~MJPEGDecoder() {
00048     if (_surface) {
00049         _surface->free();
00050         delete _surface;
00051     }
00052 }
00053 
00054 // Header to be inserted
00055 static const byte s_jpegHeader[] = {
00056     0xff, 0xd8,                     // SOI
00057     0xff, 0xe0,                     // APP0
00058     0x00, 0x10,                     // APP0 header size (including
00059                                     // this field, but excluding preceding)
00060     'J', 'F', 'I', 'F', 0x00,       // ID string 'JFIF\0'
00061     0x01, 0x01,                     // version
00062     0x00,                           // bits per type
00063     0x00, 0x00,                     // X density
00064     0x00, 0x00,                     // Y density
00065     0x00,                           // X thumbnail size
00066     0x00
00067 };
00068 
00069 enum {
00070     DHT_SEGMENT_SIZE = 420
00071 };
00072 
00073 static const byte s_dhtSegmentHead[] = { 0xFF, 0xC4, 0x01, 0xA2, 0x00 };
00074 static const byte s_dhtSegmentFrag[] = {
00075     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
00076     0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
00077     0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
00078 };
00079 
00080 // Set up the standard Huffman tables (cf. JPEG standard section K.3)
00081 // IMPORTANT: these are only valid for 8-bit data precision!
00082 static const byte s_mjpegBitsDCLuminance[17] = {
00083     /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
00084 };
00085 
00086 static const byte s_mjpegValDC[12] = {
00087     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
00088 };
00089 
00090 #if 0
00091 static const byte s_mjpegBitsDCChrominance[17] = {
00092     /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
00093 };
00094 #endif
00095 
00096 static const byte s_mjpegBitsACLuminance[17] = {
00097     /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
00098 };
00099 
00100 static const byte s_mjpegValACLuminance[] = {
00101     0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
00102     0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
00103     0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
00104     0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
00105     0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
00106     0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
00107     0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
00108     0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
00109     0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
00110     0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
00111     0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
00112     0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
00113     0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
00114     0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
00115     0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
00116     0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
00117     0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
00118     0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
00119     0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
00120     0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
00121     0xf9, 0xfa
00122 };
00123 
00124 static const byte s_mjpegBitsACChrominance[17] = {
00125     /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
00126 };
00127 
00128 static const byte s_mjpegValACChrominance[] = {
00129     0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
00130     0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
00131     0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
00132     0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
00133     0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
00134     0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
00135     0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
00136     0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
00137     0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
00138     0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
00139     0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
00140     0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
00141     0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
00142     0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
00143     0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
00144     0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
00145     0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
00146     0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
00147     0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
00148     0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
00149     0xf9, 0xfa
00150 };
00151 
00152 const Graphics::Surface *MJPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
00153     // We need to reconstruct an actual JPEG stream here, then feed it to the JPEG decoder
00154     // Yes, this is a pain.
00155 
00156     stream.readUint32BE(); // Skip nonsense JPEG header
00157     uint16 inputSkip = stream.readUint16BE() + 4;
00158     uint32 tag = stream.readUint32BE();
00159 
00160     if (tag != MKTAG('A', 'V', 'I', '1')) {
00161         warning("Invalid MJPEG tag found");
00162         return 0;
00163     }
00164 
00165     uint32 outputSize = stream.size() - inputSkip + sizeof(s_jpegHeader) + DHT_SEGMENT_SIZE;
00166     byte *data = (byte *)malloc(outputSize);
00167 
00168     if (!data) {
00169         warning("Failed to allocate data for MJPEG conversion");
00170         return 0;
00171     }
00172 
00173     // Copy the header
00174     memcpy(data, s_jpegHeader, sizeof(s_jpegHeader));
00175     uint32 dataOffset = sizeof(s_jpegHeader);
00176 
00177     // Write the fake DHT segment
00178     memcpy(data + dataOffset, s_dhtSegmentHead, sizeof(s_dhtSegmentHead));
00179     dataOffset += sizeof(s_dhtSegmentHead);
00180     memcpy(data + dataOffset, s_mjpegBitsDCLuminance + 1, 16);
00181     dataOffset += 16;
00182     memcpy(data + dataOffset, s_dhtSegmentFrag, sizeof(s_dhtSegmentFrag));
00183     dataOffset += sizeof(s_dhtSegmentFrag);
00184     memcpy(data + dataOffset, s_mjpegValDC, 12);
00185     dataOffset += 12;
00186     data[dataOffset++] = 0x10;
00187     memcpy(data + dataOffset, s_mjpegBitsACLuminance + 1, 16);
00188     dataOffset += 16;
00189     memcpy(data + dataOffset, s_mjpegValACLuminance, 162);
00190     dataOffset += 162;
00191     data[dataOffset++] = 0x11;
00192     memcpy(data + dataOffset, s_mjpegBitsACChrominance + 1, 16);
00193     dataOffset += 16;
00194     memcpy(data + dataOffset, s_mjpegValACChrominance, 162);
00195     dataOffset += 162;
00196 
00197     // Write the actual data
00198     stream.seek(inputSkip);
00199     stream.read(data + dataOffset, stream.size() - inputSkip);
00200 
00201     Common::MemoryReadStream convertedStream(data, outputSize, DisposeAfterUse::YES);
00202     JPEGDecoder jpeg;
00203 
00204     if (!jpeg.loadStream(convertedStream)) {
00205         warning("Failed to decode MJPEG frame");
00206         return 0;
00207     }
00208 
00209     if (_surface) {
00210         _surface->free();
00211         delete _surface;
00212     }
00213 
00214     _surface = jpeg.getSurface()->convertTo(_pixelFormat);
00215 
00216     return _surface;
00217 }
00218 
00219 } // End of namespace Image


Generated on Sat Feb 16 2019 05:00:58 for ResidualVM by doxygen 1.7.1
curved edge   curved edge