xref: /haiku/src/add-ons/media/plugins/dvd_streamer/DVDMediaIO.cpp (revision a127b88ecbfab58f64944c98aa47722a18e363b2)
1 /*
2  * Copyright 2019, Dario Casalinuovo. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "DVDMediaIO.h"
8 
9 #include <stdio.h>
10 #include <unistd.h>
11 #include <inttypes.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 
16 #include "MediaDebug.h"
17 
18 
19 #define DVD_READ_CACHE 1
20 
21 #define DEFAULT_LANGUAGE "en"
22 
23 
24 DVDMediaIO::DVDMediaIO(const char* path)
25 	:
26 	BAdapterIO(
27 		B_MEDIA_STREAMING | B_MEDIA_SEEKABLE,
28 		B_INFINITE_TIMEOUT),
29 	fPath(path),
30 	fLoopThread(-1),
31 	fExit(false)
32 {
33 	fBuffer = (uint8_t*) malloc(DVD_VIDEO_LB_LEN);
34 }
35 
36 
37 DVDMediaIO::~DVDMediaIO()
38 {
39 	fExit = true;
40 
41 	status_t status;
42 	if (fLoopThread != -1)
43 		wait_for_thread(fLoopThread, &status);
44 
45 	free(fBuffer);
46 }
47 
48 
49 ssize_t
50 DVDMediaIO::WriteAt(off_t position, const void* buffer, size_t size)
51 {
52 	return B_NOT_SUPPORTED;
53 }
54 
55 
56 status_t
57 DVDMediaIO::SetSize(off_t size)
58 {
59 	return B_NOT_SUPPORTED;
60 }
61 
62 
63 status_t
64 DVDMediaIO::Open()
65 {
66 	fInputAdapter = BuildInputAdapter();
67 
68 	if (dvdnav_open(&fDvdNav, fPath) != DVDNAV_STATUS_OK) {
69 		TRACE("DVDMediaIO::Open() dvdnav_open error\n");
70 		return B_ERROR;
71 	}
72 
73 	// read ahead cache usage
74 	if (dvdnav_set_readahead_flag(fDvdNav, DVD_READ_CACHE) != DVDNAV_STATUS_OK) {
75 		TRACE("DVDMediaIO::Open() dvdnav_set_readahead_flag error: %s\n",
76 			dvdnav_err_to_string(fDvdNav));
77 		return B_ERROR;
78 	}
79 
80 	// set language
81 	if (dvdnav_menu_language_select(fDvdNav, DEFAULT_LANGUAGE) != DVDNAV_STATUS_OK ||
82 			dvdnav_audio_language_select(fDvdNav, DEFAULT_LANGUAGE) != DVDNAV_STATUS_OK ||
83 		dvdnav_spu_language_select(fDvdNav, DEFAULT_LANGUAGE) != DVDNAV_STATUS_OK) {
84 		TRACE("DVDMediaIO::Open() setting languages error: %s\n",
85 			dvdnav_err_to_string(fDvdNav));
86 		return B_ERROR;
87 	}
88 
89 	// set the PGC positioning flag
90 	if (dvdnav_set_PGC_positioning_flag(fDvdNav, 1) != DVDNAV_STATUS_OK) {
91 		TRACE("DVDMediaIO::Open() dvdnav_set_PGC_positioning_flag error: %s\n",
92 			dvdnav_err_to_string(fDvdNav));
93 		return 2;
94 	}
95 
96 	fLoopThread = spawn_thread(_LoopThread, "two and two are five",
97 		B_NORMAL_PRIORITY, this);
98 
99 	if (fLoopThread <= 0 || resume_thread(fLoopThread) != B_OK)
100 		return B_ERROR;
101 
102 	return B_OK;
103 }
104 
105 
106 int32
107 DVDMediaIO::_LoopThread(void* data)
108 {
109 	static_cast<DVDMediaIO *>(data)->LoopThread();
110 	return 0;
111 }
112 
113 
114 void
115 DVDMediaIO::LoopThread()
116 {
117 	while (!fExit) {
118 		int err;
119 		int event;
120 		int len;
121 
122 #if DVD_READ_CACHE
123 		err = dvdnav_get_next_cache_block(fDvdNav, &fBuffer, &event, &len);
124 #else
125 		err = dvdnav_get_next_block(fDvdNav, fBuffer, &event, &len);
126 #endif
127 
128 		if (err == DVDNAV_STATUS_ERR) {
129 			TRACE("DVDMediaIO::LoopThread(): Error getting next block: %s\n",
130 				dvdnav_err_to_string(fDvdNav));
131 			continue;
132 		}
133 
134 		HandleDVDEvent(event, len);
135 
136 #if DVD_READ_CACHE
137 		dvdnav_free_cache_block(fDvdNav, fBuffer);
138 #endif
139 	}
140 
141 	if (dvdnav_close(fDvdNav) != DVDNAV_STATUS_OK) {
142 		TRACE("DVDMediaIO::LoopThread() dvdnav_close error: %s\n",
143 			dvdnav_err_to_string(fDvdNav));
144 	}
145 	fLoopThread = -1;
146 }
147 
148 
149 void
150 DVDMediaIO::HandleDVDEvent(int event, int len)
151 {
152 	switch (event) {
153 		case DVDNAV_BLOCK_OK:
154 			fInputAdapter->Write(fBuffer, len);
155 			break;
156 
157 		case DVDNAV_NOP:
158 			break;
159 
160 		case DVDNAV_STILL_FRAME:
161 		{
162 			dvdnav_still_event_t* still_event
163 				= (dvdnav_still_event_t*) fBuffer;
164 			if (still_event->length < 0xff) {
165 				TRACE("DVDMediaIO::HandleDVDEvent: Skipped %d "
166 						"seconds of still frame\n",
167 					still_event->length);
168 			} else {
169 				TRACE("DVDMediaIO::HandleDVDEvent: Skipped "
170 					"indefinite length still frame\n");
171 			}
172 			dvdnav_still_skip(fDvdNav);
173 			break;
174 		}
175 
176 		case DVDNAV_WAIT:
177 			TRACE("DVDMediaIO::HandleDVDEvent: Skipping wait condition\n");
178 			dvdnav_wait_skip(fDvdNav);
179 			break;
180 
181 		case DVDNAV_SPU_CLUT_CHANGE:
182 			break;
183 
184 		case DVDNAV_SPU_STREAM_CHANGE:
185 			break;
186 
187 		case DVDNAV_AUDIO_STREAM_CHANGE:
188 			break;
189 
190 		case DVDNAV_HIGHLIGHT:
191 		{
192 			dvdnav_highlight_event_t* highlight_event
193 				= (dvdnav_highlight_event_t*) fBuffer;
194 			TRACE("DVDMediaIO::HandleDVDEvent: Button: %d\n",
195 				highlight_event->buttonN);
196 			break;
197 		}
198 
199 		case DVDNAV_VTS_CHANGE:
200 			break;
201 
202 		case DVDNAV_CELL_CHANGE:
203 		{
204 			int32_t title = 0, chapter = 0;
205 			uint32_t pos, len;
206 
207 			dvdnav_current_title_info(fDvdNav, &title, &chapter);
208 			dvdnav_get_position(fDvdNav, &pos, &len);
209 			TRACE("DVDMediaIO::HandleDVDEvent: Cell: Title %d, Chapter %d\n",
210 				tt, ptt);
211 			TRACE("DVDMediaIO::HandleDVDEvent: At position %.0f%% inside "
212 				"the feature\n", 100 * (double)pos / (double)len);
213 			break;
214 		}
215 
216 		case DVDNAV_NAV_PACKET:
217 			break;
218 
219 		case DVDNAV_HOP_CHANNEL:
220 			break;
221 
222 		case DVDNAV_STOP:
223 			fExit = true;
224 			break;
225 
226 		default:
227 			TRACE("DVDMediaIO::HandleDVDEvent: unknown event (%i)\n",
228 				event);
229 			fExit = true;
230 			break;
231 	}
232 }
233 
234 
235 status_t
236 DVDMediaIO::SeekRequested(off_t position)
237 {
238 	dvdnav_sector_search(fDvdNav, position, SEEK_SET);
239 	return B_OK;
240 }
241 
242 
243 void
244 DVDMediaIO::MouseMoved(uint32 x, uint32 y)
245 {
246 	pci_t* pci = dvdnav_get_current_nav_pci(fDvdNav);
247 	dvdnav_mouse_select(fDvdNav, pci, x, y);
248 }
249 
250 
251 void
252 DVDMediaIO::MouseDown(uint32 x, uint32 y)
253 {
254 	pci_t* pci = dvdnav_get_current_nav_pci(fDvdNav);
255 	dvdnav_mouse_activate(fDvdNav, pci, x, y);
256 }
257