1 /*
2 * This file is part of MPlayer.
3 *
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifndef MPLAYER_VCD_READ_FBSD_H
20 #define MPLAYER_VCD_READ_FBSD_H
21
22 #define _XOPEN_SOURCE 500
23
24 #include <sys/types.h>
25 #include <inttypes.h>
26 #include <unistd.h>
27 #include "libavutil/intreadwrite.h"
28 #include <sys/cdio.h>
29 #include <sys/ioctl.h>
30 #if defined(__NetBSD__) || defined(__OpenBSD__)
31 #define VCD_NETBSD 1
32 #endif
33 #ifdef VCD_NETBSD
34 #include <sys/scsiio.h>
35 #define TOCADDR(te) ((te).data->addr)
36 #define READ_TOC CDIOREADTOCENTRYS
37 #else
38 #include <sys/cdrio.h>
39 #define TOCADDR(te) ((te).entry.addr)
40 #define READ_TOC CDIOREADTOCENTRY
41 #endif
42 #include "mp_msg.h"
43
44 //=================== VideoCD ==========================
45 #define CDROM_LEADOUT 0xAA
46
47 typedef struct {
48 uint8_t sync [12];
49 uint8_t header [4];
50 uint8_t subheader [8];
51 uint8_t data [2324];
52 uint8_t spare [4];
53 } cdsector_t;
54
55 #ifdef VCD_NETBSD
56 typedef struct ioc_read_toc_entry vcd_tocentry;
57 #else
58 typedef struct ioc_read_toc_single_entry vcd_tocentry;
59 #endif
60
61 typedef struct mp_vcd_priv_st {
62 int fd;
63 vcd_tocentry entry;
64 #ifdef VCD_NETBSD
65 struct cd_toc_entry entry_data;
66 #else
67 cdsector_t buf;
68 #endif
69 struct ioc_toc_header tochdr;
70 unsigned int track;
71 } mp_vcd_priv_t;
72
73 static inline void
vcd_set_msf(mp_vcd_priv_t * vcd,unsigned int sect)74 vcd_set_msf(mp_vcd_priv_t* vcd, unsigned int sect)
75 {
76 #ifdef VCD_NETBSD
77 vcd->entry.data = &vcd->entry_data;
78 #endif
79 sect += 150;
80 TOCADDR(vcd->entry).msf.frame = sect % 75;
81 sect = sect / 75;
82 TOCADDR(vcd->entry).msf.second = sect % 60;
83 sect = sect / 60;
84 TOCADDR(vcd->entry).msf.minute = sect;
85 }
86
87 static inline void
vcd_inc_msf(mp_vcd_priv_t * vcd)88 vcd_inc_msf(mp_vcd_priv_t* vcd)
89 {
90 #ifdef VCD_NETBSD
91 vcd->entry.data = &vcd->entry_data;
92 #endif
93 TOCADDR(vcd->entry).msf.frame++;
94 if (TOCADDR(vcd->entry).msf.frame==75){
95 TOCADDR(vcd->entry).msf.frame=0;
96 TOCADDR(vcd->entry).msf.second++;
97 if (TOCADDR(vcd->entry).msf.second==60){
98 TOCADDR(vcd->entry).msf.second=0;
99 TOCADDR(vcd->entry).msf.minute++;
100 }
101 }
102 }
103
104 static inline unsigned int
vcd_get_msf(mp_vcd_priv_t * vcd)105 vcd_get_msf(mp_vcd_priv_t* vcd)
106 {
107 #ifdef VCD_NETBSD
108 vcd->entry.data = &vcd->entry_data;
109 #endif
110 return TOCADDR(vcd->entry).msf.frame +
111 (TOCADDR(vcd->entry).msf.second +
112 TOCADDR(vcd->entry).msf.minute * 60) * 75 - 150;
113 }
114
115 /**
116 * \brief read a TOC entry
117 * \param fd device to read from
118 * \param dst buffer to read data into
119 * \param nr track number to read info for
120 * \return 1 on success, 0 on failure
121 */
122 static int
read_toc_entry(mp_vcd_priv_t * vcd,int nr)123 read_toc_entry(mp_vcd_priv_t *vcd, int nr)
124 {
125 vcd->entry.address_format = CD_MSF_FORMAT;
126 #ifdef VCD_NETBSD
127 vcd->entry.data_len = sizeof(struct cd_toc_entry);
128 vcd->entry.data = &vcd->entry_data;
129 vcd->entry.starting_track = nr;
130 #else
131 vcd->entry.track = nr;
132 #endif
133 if (ioctl(vcd->fd, READ_TOC, &vcd->entry) == -1) {
134 mp_msg(MSGT_OPEN,MSGL_ERR,"read CDROM toc entry: %s\n",strerror(errno));
135 return 0;
136 }
137 return 1;
138 }
139
140 static int
vcd_seek_to_track(mp_vcd_priv_t * vcd,int track)141 vcd_seek_to_track(mp_vcd_priv_t* vcd, int track)
142 {
143 if (!read_toc_entry(vcd, track))
144 return -1;
145 return VCD_SECTOR_DATA * vcd_get_msf(vcd);
146 }
147
148 static int
vcd_get_track_end(mp_vcd_priv_t * vcd,int track)149 vcd_get_track_end(mp_vcd_priv_t* vcd, int track)
150 {
151 if (!read_toc_entry(vcd,
152 track < vcd->tochdr.ending_track ? track + 1 : CDROM_LEADOUT))
153 return -1;
154 return VCD_SECTOR_DATA * vcd_get_msf(vcd);
155 }
156
157 static mp_vcd_priv_t*
vcd_read_toc(int fd)158 vcd_read_toc(int fd)
159 {
160 struct ioc_toc_header tochdr;
161 mp_vcd_priv_t* vcd;
162 int i, last_startsect;
163 if (ioctl(fd, CDIOREADTOCHEADER, &tochdr) == -1) {
164 mp_msg(MSGT_OPEN,MSGL_ERR,"read CDROM toc header: %s\n",strerror(errno));
165 return NULL;
166 }
167 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VCD_START_TRACK=%d\n", tochdr.starting_track);
168 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VCD_END_TRACK=%d\n", tochdr.ending_track);
169 vcd = malloc(sizeof(mp_vcd_priv_t));
170 vcd->fd = fd;
171 vcd->tochdr = tochdr;
172 for (i = tochdr.starting_track; i <= tochdr.ending_track + 1; i++) {
173 if (!read_toc_entry(vcd,
174 i <= tochdr.ending_track ? i : CDROM_LEADOUT)) {
175 free(vcd);
176 return NULL;
177 }
178
179 if (i <= tochdr.ending_track)
180 mp_msg(MSGT_OPEN,MSGL_INFO,"track %02d: adr=%d ctrl=%d format=%d %02d:%02d:%02d\n",
181 #ifdef VCD_NETBSD
182 (int)vcd->entry.starting_track,
183 (int)vcd->entry.data->addr_type,
184 (int)vcd->entry.data->control,
185 #else
186 (int)vcd->entry.track,
187 (int)vcd->entry.entry.addr_type,
188 (int)vcd->entry.entry.control,
189 #endif
190 (int)vcd->entry.address_format,
191 (int)TOCADDR(vcd->entry).msf.minute,
192 (int)TOCADDR(vcd->entry).msf.second,
193 (int)TOCADDR(vcd->entry).msf.frame
194 );
195
196 if (mp_msg_test(MSGT_IDENTIFY, MSGL_INFO))
197 {
198 int startsect = vcd_get_msf(vcd);
199 if (i > tochdr.starting_track)
200 {
201 // convert duraion to MSF
202 vcd_set_msf(vcd, startsect - last_startsect);
203 mp_msg(MSGT_IDENTIFY, MSGL_INFO,
204 "ID_VCD_TRACK_%d_MSF=%02d:%02d:%02d\n",
205 i - 1,
206 TOCADDR(vcd->entry).msf.minute,
207 TOCADDR(vcd->entry).msf.second,
208 TOCADDR(vcd->entry).msf.frame);
209 }
210 last_startsect = startsect;
211 }
212 }
213 return vcd;
214 }
215
vcd_end_track(mp_vcd_priv_t * vcd)216 static int vcd_end_track(mp_vcd_priv_t* vcd)
217 {
218 return vcd->tochdr.ending_track;
219 }
220
221 static int
vcd_read(mp_vcd_priv_t * vcd,char * mem)222 vcd_read(mp_vcd_priv_t* vcd, char *mem)
223 {
224 #ifdef VCD_NETBSD
225 struct scsireq sc;
226 int lba = vcd_get_msf(vcd);
227 int blocks;
228 int rc;
229
230 blocks = 1;
231
232 memset(&sc, 0, sizeof(sc));
233 sc.cmd[0] = 0xBE;
234 sc.cmd[1] = 5 << 2; // mode2/form2
235 AV_WB32(&sc.cmd[2], lba);
236 AV_WB24(&sc.cmd[6], blocks);
237 sc.cmd[9] = 1 << 4; // user data only
238 sc.cmd[10] = 0; // no subchannel
239 sc.cmdlen = 12;
240 sc.databuf = (caddr_t) mem;
241 sc.datalen = VCD_SECTOR_DATA;
242 sc.senselen = sizeof(sc.sense);
243 sc.flags = SCCMD_READ;
244 sc.timeout = 10000;
245 rc = ioctl(vcd->fd, SCIOCCOMMAND, &sc);
246 if (rc == -1) {
247 mp_msg(MSGT_STREAM,MSGL_ERR,"SCIOCCOMMAND: %s\n",strerror(errno));
248 return -1;
249 }
250 if (sc.retsts || sc.error) {
251 mp_msg(MSGT_STREAM,MSGL_ERR,"scsi command failed: status %d error %d\n",
252 sc.retsts,sc.error);
253 return -1;
254 }
255 #else
256 if (pread(vcd->fd,&vcd->buf,VCD_SECTOR_SIZE,vcd_get_msf(vcd)*VCD_SECTOR_SIZE)
257 != VCD_SECTOR_SIZE) return 0; // EOF?
258
259 memcpy(mem,vcd->buf.data,VCD_SECTOR_DATA);
260 #endif
261 vcd_inc_msf(vcd);
262 return VCD_SECTOR_DATA;
263 }
264
265 #endif /* MPLAYER_VCD_READ_FBSD_H */
266