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