1 /*MT*
2 
3     MediaTomb - http://www.mediatomb.cc/
4 
5     jpeg_resolution.cc - this file is part of MediaTomb.
6 
7     Copyright (C) 2005 Gena Batyan <bgeradz@mediatomb.cc>,
8                        Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
9 
10     Copyright (C) 2006-2010 Gena Batyan <bgeradz@mediatomb.cc>,
11                             Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>,
12                             Leonhard Wimmer <leo@mediatomb.cc>
13 
14     MediaTomb is free software; you can redistribute it and/or modify
15     it under the terms of the GNU General Public License version 2
16     as published by the Free Software Foundation.
17 
18     MediaTomb is distributed in the hope that it will be useful,
19     but WITHOUT ANY WARRANTY; without even the implied warranty of
20     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21     GNU General Public License for more details.
22 
23     You should have received a copy of the GNU General Public License
24     version 2 along with MediaTomb; if not, write to the Free Software
25     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
26 
27     $Id$
28 */
29 
30 /// \file jpeg_resolution.cc
31 
32 #include "tools.h" // API
33 
34 #include <cerrno>
35 #include <cstdio>
36 #include <cstdlib>
37 #include <cstring>
38 
39 #include "iohandler/io_handler.h"
40 
41 using uchar = unsigned char;
42 
43 #ifndef TRUE
44 #define TRUE 1
45 #define FALSE 0
46 #endif
47 
48 #define M_SOF0 0xC0 // Start Of Frame N
49 #define M_SOF1 0xC1 // N indicates which compression process
50 #define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use
51 #define M_SOF3 0xC3
52 #define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers
53 #define M_SOF6 0xC6
54 #define M_SOF7 0xC7
55 #define M_SOF9 0xC9
56 #define M_SOF10 0xCA
57 #define M_SOF11 0xCB
58 #define M_SOF13 0xCD
59 #define M_SOF14 0xCE
60 #define M_SOF15 0xCF
61 #define M_SOI 0xD8 // Start Of Image (beginning of datastream)
62 #define M_EOI 0xD9 // End Of Image (end of datastream)
63 #define M_SOS 0xDA // Start Of Scan (begins compressed data)
64 #define M_JFIF 0xE0 // Jfif marker
65 #define M_EXIF 0xE1 // Exif marker
66 #define M_COM 0xFE // COMment
67 #define M_DQT 0xDB
68 #define M_DHT 0xC4
69 #define M_DRI 0xDD
70 
71 #define ITEM_BUF_SIZE 16
Get16m(const void * Short)72 static int Get16m(const void* Short)
73 {
74     return (static_cast<uchar*>(const_cast<void*>(Short))[0] << 8) | static_cast<uchar*>(const_cast<void*>(Short))[1];
75 }
76 
ioh_fgetc(const std::unique_ptr<IOHandler> & ioh)77 static int ioh_fgetc(const std::unique_ptr<IOHandler>& ioh)
78 {
79     uchar c[1] = { 0 };
80     int ret = ioh->read(reinterpret_cast<char*>(c), sizeof(char));
81     if (ret < 0)
82         return ret;
83     return int(c[0]);
84 }
85 
get_jpeg_resolution(const std::unique_ptr<IOHandler> & ioh,int * w,int * h)86 static void get_jpeg_resolution(const std::unique_ptr<IOHandler>& ioh, int* w, int* h)
87 {
88     int a = ioh_fgetc(ioh);
89 
90     if (a != 0xff || ioh_fgetc(ioh) != M_SOI)
91         throw_std_runtime_error("get_jpeg_resolution: could not read jpeg specs");
92 
93     for (;;) {
94         int itemlen;
95         off_t skip;
96         int marker = 0;
97         int ll, lh, got;
98         uchar Data[ITEM_BUF_SIZE];
99 
100         for (a = 0; a < 7; a++) {
101             marker = ioh_fgetc(ioh);
102             if (marker != 0xff)
103                 break;
104 
105             if (a >= 6)
106                 throw_std_runtime_error("get_jpeg_resolution: too many padding bytes");
107         }
108 
109         // 0xff is legal padding, but if we get that many, something's wrong.
110         if (marker == 0xff)
111             throw_std_runtime_error("get_jpeg_resolution: too many padding bytes");
112 
113         // Read the length of the section.
114         lh = ioh_fgetc(ioh);
115         ll = ioh_fgetc(ioh);
116 
117         itemlen = (lh << 8) | ll;
118 
119         if (itemlen < 2)
120             throw_std_runtime_error("get_jpeg_resolution: invalid marker");
121 
122         skip = 0;
123         if (itemlen > ITEM_BUF_SIZE) {
124             skip = itemlen - ITEM_BUF_SIZE;
125             itemlen = ITEM_BUF_SIZE;
126         }
127 
128         // Store first two pre-read bytes.
129         Data[0] = uchar(lh);
130         Data[1] = uchar(ll);
131 
132         got = ioh->read(reinterpret_cast<char*>(Data + 2), itemlen - 2);
133         if (got != itemlen - 2)
134             throw_std_runtime_error("get_jpeg_resolution: Premature end of file?");
135 
136         ioh->seek(skip, SEEK_CUR);
137 
138         switch (marker) {
139         case M_EOI: // in case it's a tables-only JPEG stream
140             throw_std_runtime_error("get_jpeg_resolution: No image in jpeg");
141         case M_SOF0:
142         case M_SOF1:
143         case M_SOF2:
144         case M_SOF3:
145         case M_SOF5:
146         case M_SOF6:
147         case M_SOF7:
148         case M_SOF9:
149         case M_SOF10:
150         case M_SOF11:
151         case M_SOF13:
152         case M_SOF14:
153         case M_SOF15:
154             *w = Get16m(Data + 5);
155             *h = Get16m(Data + 3);
156             return;
157         }
158     }
159     throw_std_runtime_error("get_jpeg_resolution: resolution not found");
160 }
161 
162 // IOHandler must be opened
get_jpeg_resolution(std::unique_ptr<IOHandler> && ioh)163 std::string get_jpeg_resolution(std::unique_ptr<IOHandler>&& ioh)
164 {
165     int w, h;
166     try {
167         get_jpeg_resolution(ioh, &w, &h);
168     } catch (const std::runtime_error& e) {
169         ioh->close();
170         throw e;
171     }
172     ioh->close();
173 
174     return fmt::format("{}x{}", w, h);
175 }
176