[server] / trunk / server / src / mmdevice.cxx Repository:
ViewVC logotype

Annotation of /trunk/server/src/mmdevice.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 596 - (view) (download)

1 : amartin 378 /*
2 :     * This file is part of Project DiaStar Server.
3 :     *
4 :     * More information about this project can be found at:
5 :     * http://www.projectdiastar.org.
6 :     *
7 :     * Copyright (C) 2009 Dialogic Corp.
8 :     *
9 :     * This program is free software; you can redistribute it and/or
10 :     * modify it under the terms of the GNU General Public License
11 :     * as published by the Free Software Foundation; either version 2
12 :     * of the License, or (at your option) any later version.
13 :     *
14 :     * This program is distributed in the hope that it will be useful,
15 :     * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 :     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 :     * GNU General Public License for more details.
18 :     *
19 :     * You should have received a copy of the GNU General Public License
20 :     * along with this program; if not, write to the Free Software
21 :     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 :     * 02110-1301, USA.
23 :     * Alternatively see <http://www.gnu.org/licenses/>.
24 :     * Or see the LICENSE file included within the source tree.
25 :     *
26 :     */
27 :    
28 :     /*!
29 :     * \file mmdevice.cxx
30 :     * \brief Dialogic Multimedia device
31 :     * \author Antony Martin <antony.martin@dialogic.com>
32 :     * \author John Tarlton <john.tarlton@dialogic.com>
33 :     * \version 19-AUG-2009
34 :     */
35 :    
36 :     /*------------------------------ Dependencies --------------------------------*/
37 :    
38 :     #include <srllib.h>
39 :     #include <dxxxlib.h>
40 :    
41 :     #include "logger.h"
42 :    
43 : jtarlton 536 #include "mediafinder.h"
44 : amartin 378 #include "dialogicchannelmanager.h"
45 : jtarlton 459 #include "ipmdevice.h"
46 : amartin 378 #include "mmdevice.h"
47 :    
48 :     /*----------------------------------------------------------------------------*/
49 :    
50 :     /*
51 :     * ctor
52 :     */
53 :     MmDevice::MmDevice( const std::string& name,
54 : jtarlton 536 DialogicChannelManager& channelMgr )
55 : amartin 378 : DialogicDevice(name, channelMgr),
56 : jtarlton 390 playActive_ (false),
57 : jtarlton 538 recordActive_ (false)
58 : amartin 378 {
59 : amartin 403 busy_ = false;
60 :     }
61 : amartin 378
62 : jtarlton 536
63 : amartin 403 /*
64 : jtarlton 536 * dtor
65 :     */
66 :     MmDevice::~MmDevice()
67 :     {
68 :     }
69 :    
70 :    
71 :     /*
72 : jtarlton 596 * Process a command that was queued because an async operation was in progress
73 : amartin 403 * when it was initially requested.
74 :     */
75 :     void MmDevice::processPendingCommand()
76 :     {
77 : jtarlton 418 while ( !busy_ && !pending_.empty() )
78 : amartin 403 {
79 : jtarlton 419 LOGDEBUG("MmDevice::processPendingCommand()");
80 : amartin 403 switch( pending_.front().cmd )
81 :     {
82 :     case MmCommand::CONNECT:
83 : jtarlton 548 connect(pending_.front().other.audio, pending_.front().other.video);
84 : amartin 403 break;
85 : jtarlton 536
86 : amartin 403 case MmCommand::DISCONNECT:
87 :     disconnect();
88 :     break;
89 : jtarlton 536
90 : amartin 403 case MmCommand::PLAY:
91 : jtarlton 557 if ( !play(pending_.front().audioFile,
92 :     pending_.front().audioCoder,
93 :     pending_.front().videoFile,
94 :     pending_.front().videoCoder,
95 :     pending_.front().lang) )
96 :     {
97 :     channelMgr_.onPlayCompleted(this, PLAY_ERROR);
98 :     }
99 : amartin 403 break;
100 : jtarlton 536
101 : amartin 403 case MmCommand::RECORD:
102 : jtarlton 557 if ( !record(pending_.front().audioFile,
103 :     pending_.front().audioCoder,
104 :     pending_.front().videoFile,
105 :     pending_.front().videoCoder,
106 :     pending_.front().lang) )
107 :     {
108 :     channelMgr_.onRecordCompleted(this, RECORD_ERROR);
109 :     }
110 : amartin 403 break;
111 : jtarlton 536
112 : amartin 403 case MmCommand::STOPPLAY:
113 :     stopPlay();
114 :     break;
115 : jtarlton 536
116 : amartin 403 case MmCommand::STOPRECORD:
117 :     stopRecord();
118 :     break;
119 : jtarlton 536
120 : amartin 403 default:
121 :     break;
122 :     }
123 :     pending_.pop();
124 :     }
125 : amartin 378 }
126 :    
127 : jtarlton 596
128 : amartin 378 /*
129 :     * Open the device.
130 :     */
131 :     bool MmDevice::open()
132 :     {
133 : jtarlton 455 LOGDEBUG("MmDevice::open() device: " << getDeviceName());
134 :     devHandle_ = mm_Open(getDeviceName().c_str(), 0, NULL);
135 : jtarlton 390 if ( devHandle_ == EMM_ERROR )
136 :     {
137 :     LOGERROR("mm_Open() failed");
138 :     return false;
139 :     }
140 : amartin 378 return true;
141 :     }
142 :    
143 :    
144 :     /*
145 :     * Close the device.
146 :     */
147 :     bool MmDevice::close()
148 :     {
149 : jtarlton 455 LOGDEBUG("MmDevice::close() device: " << getDeviceName());
150 : jtarlton 400 if ( devHandle_ > 0 )
151 : jtarlton 390 {
152 : jtarlton 400 if ( mm_Close(devHandle_, NULL) == EMM_ERROR )
153 :     {
154 :     LOGERROR("mm_Close() failed");
155 :     return false;
156 :     }
157 :     devHandle_ = 0;
158 : jtarlton 390 }
159 : amartin 378 return true;
160 :     }
161 :    
162 :    
163 :     /*
164 :     * Connect media.
165 :     */
166 : jtarlton 548 bool MmDevice::connect( DialogicDevice* other_audio,
167 :     DialogicDevice* other_video )
168 : amartin 378 {
169 : jtarlton 413 if ( busy_ )
170 :     {
171 : jtarlton 548 pending_.push(MmCommand(MmCommand::CONNECT, other_audio, other_video));
172 : jtarlton 413 return true;
173 :     }
174 :    
175 : jtarlton 596 /* XXX should be params */
176 :     bool transcode_audio = true;
177 :     bool transcode_video = true;
178 :    
179 : jtarlton 548 if ( !other_audio_ && !other_video_ )
180 : amartin 403 {
181 : jtarlton 455 LOGDEBUG("MmDevice::connect() device: " << getDeviceName() <<
182 : jtarlton 548 " other_audio: " << (other_audio ? other_audio->getDeviceName() : "n/a") <<
183 :     " other_video: " << (other_video ? other_video->getDeviceName() : "n/a"));
184 : jtarlton 390
185 : jtarlton 393 DM_PORT_CONNECT_INFO_LIST portConnectInfoList;
186 :     INIT_DM_PORT_CONNECT_INFO_LIST(&portConnectInfoList);
187 :    
188 :     int count = 0;
189 :    
190 : jtarlton 548 if ( other_audio )
191 :     {
192 :     INIT_DM_PORT_CONNECT_INFO(&portConnectInfoList.port_connect_info[count]);
193 : jtarlton 596 portConnectInfoList.port_connect_info[count].unFlags = transcode_audio ? DMFL_TRANSCODE_ON : DMFL_TRANSCODE_NATIVE;
194 : jtarlton 548 portConnectInfoList.port_connect_info[count].port_info_tx = getAudioTxPortInfo();
195 :     portConnectInfoList.port_connect_info[count].port_info_rx = other_audio->getAudioRxPortInfo();
196 :     count++;
197 :     }
198 :     if ( other_video )
199 :     {
200 :     INIT_DM_PORT_CONNECT_INFO(&portConnectInfoList.port_connect_info[count]);
201 : jtarlton 596 portConnectInfoList.port_connect_info[count].unFlags = transcode_video ? DMFL_TRANSCODE_ON : DMFL_TRANSCODE_NATIVE;
202 : jtarlton 548 portConnectInfoList.port_connect_info[count].port_info_tx = getVideoTxPortInfo();
203 :     portConnectInfoList.port_connect_info[count].port_info_rx = other_video->getVideoRxPortInfo();
204 :     count++;
205 :     }
206 : jtarlton 393
207 :     portConnectInfoList.unCount = count;;
208 :     if ( dev_PortConnect(devHandle_, &portConnectInfoList, NULL) != DEV_SUCCESS )
209 :     {
210 : jtarlton 455 LOGERROR("MmDevice::connect() dev_PortConnect() failed on device: " <<
211 :     getDeviceName() << " " << ATDV_ERRMSGP(devHandle_));
212 : jtarlton 393 return false;
213 :     }
214 :    
215 : jtarlton 548 other_audio_ = other_audio;
216 :     other_video_ = other_video;
217 : amartin 403 busy_ = true;
218 : jtarlton 393 }
219 : amartin 405 else
220 :     {
221 : jtarlton 548 if ( other_audio && other_audio_ && (other_audio != other_audio_) )
222 : jtarlton 417 {
223 : jtarlton 455 LOGERROR("MmDevice::connect() " << getDeviceName() <<
224 : jtarlton 548 " unable to connect audio to device: " << other_audio->getDeviceName() <<
225 :     " already connected to device: " << other_audio_->getDeviceName());
226 : jtarlton 417 }
227 : jtarlton 548 if ( other_video && other_video_ && (other_video != other_video_) )
228 :     {
229 :     LOGERROR("MmDevice::connect() " << getDeviceName() <<
230 :     " unable to connect video to device: " << other_video->getDeviceName() <<
231 :     " already connected to device: " << other_video_->getDeviceName());
232 :     }
233 : amartin 405 }
234 :     return true;
235 : amartin 378 }
236 :    
237 :    
238 :     /*
239 :     * Disconnect media.
240 :     */
241 : jtarlton 401 bool MmDevice::disconnect()
242 : amartin 378 {
243 : jtarlton 413 if ( busy_ )
244 : jtarlton 393 {
245 : jtarlton 413 pending_.push(MmCommand(MmCommand::DISCONNECT));
246 :     return true;
247 :     }
248 : amartin 403
249 : jtarlton 548 if ( other_audio_ || other_video_ )
250 : jtarlton 413 {
251 : jtarlton 548 LOGDEBUG("MmDevice::disconnect() device: " << getDeviceName() <<
252 :     " other_audio: " << (other_audio_ ? other_audio_->getDeviceName() : "n/a") <<
253 :     " other_video: " << (other_video_ ? other_video_->getDeviceName() : "n/a"));
254 : jtarlton 455
255 : jtarlton 393 DM_PORT_CONNECT_INFO_LIST portConnectInfoList;
256 :     INIT_DM_PORT_CONNECT_INFO_LIST(&portConnectInfoList);
257 :     int count = 0;
258 :    
259 : jtarlton 548 if ( other_audio_ )
260 :     {
261 :     INIT_DM_PORT_CONNECT_INFO(&portConnectInfoList.port_connect_info[count]);
262 :     portConnectInfoList.port_connect_info[count].port_info_tx = getAudioTxPortInfo();
263 :     portConnectInfoList.port_connect_info[count].port_info_rx = other_audio_->getAudioRxPortInfo();
264 :     count++;
265 :     }
266 :     if ( other_video_ )
267 :     {
268 :     INIT_DM_PORT_CONNECT_INFO(&portConnectInfoList.port_connect_info[count]);
269 :     portConnectInfoList.port_connect_info[count].port_info_tx = getVideoTxPortInfo();
270 :     portConnectInfoList.port_connect_info[count].port_info_rx = other_video_->getVideoRxPortInfo();
271 :     count++;
272 :     }
273 : jtarlton 393 portConnectInfoList.unCount = count;
274 :     if ( dev_PortDisconnect(devHandle_, &portConnectInfoList, NULL) != DEV_SUCCESS )
275 :     {
276 : jtarlton 455 LOGERROR("MmDevice::disconnect() dev_PortDisconnect() failed on device: " <<
277 :     getDeviceName() << " " << ATDV_ERRMSGP(devHandle_));
278 : jtarlton 393 return false;
279 :     }
280 :    
281 : jtarlton 548 other_audio_ = 0;
282 :     other_video_ = 0;
283 : amartin 403 busy_ = true;
284 : jtarlton 393 }
285 :     return true;
286 : amartin 378 }
287 :    
288 :    
289 :     /*
290 :     * handle events from Dialogic apiS
291 :     */
292 :     bool MmDevice::processEvent( METAEVENT& metaevent )
293 :     {
294 :     long evttype = metaevent.evttype;
295 : jtarlton 390
296 :     switch ( evttype )
297 :     {
298 :     case MMEV_OPEN:
299 : jtarlton 393 onOpen();
300 : jtarlton 390 break;
301 :    
302 : jtarlton 459 case MMEV_PLAY:
303 :     /* mm_Play() completed successfully */
304 :     onPlay((MM_PLAY_RECORD_CMPLT*)sr_getevtdatap());
305 : jtarlton 390 break;
306 :    
307 : jtarlton 459 case MMEV_PLAY_ACK:
308 :     /* mm_Play() started successfully */
309 : amartin 403 onPlayAck();
310 : jtarlton 393 break;
311 :    
312 : jtarlton 390 case MMEV_PLAY_FAIL:
313 :     onPlayFail();
314 :     break;
315 :    
316 :     case MMEV_PLAY_ACK_FAIL:
317 :     onPlayAckFail();
318 :     break;
319 :    
320 : jtarlton 399 case MMEV_RECORD:
321 : jtarlton 459 onRecord((MM_PLAY_RECORD_CMPLT*)sr_getevtdatap());
322 : jtarlton 399 break;
323 :    
324 : jtarlton 390 case MMEV_RECORD_ACK:
325 : jtarlton 455 LOGDEBUG("MmDevice::processEvent() MMEV_RECORD_ACK device: " << getDeviceName());
326 : jtarlton 390 break;
327 :    
328 : jtarlton 399 case MMEV_RECORD_FAIL:
329 :     onRecordFail();
330 :     break;
331 :    
332 :     case MMEV_RECORD_ACK_FAIL:
333 : jtarlton 455 LOGWARN("MmDevice::processEvent() MMEV_RECORD_ACK_FAIL device: " << getDeviceName());
334 : jtarlton 399 break;
335 :    
336 :     case MMEV_VIDEO_RECORD_STARTED:
337 : jtarlton 455 LOGDEBUG("MmDevice::processEvent() MMEV_VIDEO_RECORD_STARTED device: " << getDeviceName());
338 : jtarlton 399 break;
339 :    
340 :     case MMEV_VIDEO_RECORD_STARTED_FAIL:
341 :     onRecordFail();
342 :     break;
343 :    
344 : jtarlton 390 case MMEV_STOP_ACK:
345 :     onStopAck();
346 :     break;
347 :    
348 :     case MMEV_STOP_ACK_FAIL:
349 :     onStopAckFail();
350 :     break;
351 :    
352 :     case MMEV_RESET:
353 :     onReset();
354 :     break;
355 :    
356 :     case MMEV_ERROR:
357 :     onError();
358 :     break;
359 :    
360 :     case DMEV_PORT_CONNECT:
361 : amartin 403 onPortConnect();
362 : jtarlton 390 break;
363 :    
364 :     case DMEV_PORT_CONNECT_FAIL:
365 : amartin 403 onPortConnectFail();
366 : jtarlton 390 break;
367 :    
368 :     case DMEV_PORT_DISCONNECT:
369 : amartin 403 onPortDisconnect();
370 : jtarlton 390 break;
371 :    
372 :     case DMEV_PORT_DISCONNECT_FAIL:
373 : amartin 403 onPortDisconnectFail();
374 : jtarlton 390 break;
375 :    
376 : jtarlton 393 case DMEV_GET_TX_PORT_INFO:
377 :     onGetTxPortInfo((DM_PORT_INFO_LIST*)sr_getevtdatap());
378 :     break;
379 :    
380 :     case DMEV_GET_RX_PORT_INFO:
381 :     onGetRxPortInfo((DM_PORT_INFO_LIST*)sr_getevtdatap());
382 :     break;
383 :    
384 : jtarlton 390 default:
385 : jtarlton 393 LOGDEBUG("MmDevice::processEvent() Unhandled event: " <<
386 :     std::hex << evttype << " " << ATDV_NAMEP(sr_getevtdev()));
387 : jtarlton 390 break;
388 :     }
389 : amartin 378 return true;
390 :     }
391 :    
392 :    
393 : jtarlton 390 /*
394 : jtarlton 393 * Handle MMEV_OPEN event.
395 :     */
396 :     void MmDevice::onOpen()
397 :     {
398 : jtarlton 455 LOGDEBUG("MmDevice::processEvent() MMEV_OPEN device: " << getDeviceName());
399 : jtarlton 393
400 :     /* Request port info
401 :     */
402 :     if ( dev_GetTransmitPortInfo(devHandle_, this) == -1 )
403 :     {
404 :     LOGERROR("dev_GetTransmitPortInfo() failed");
405 :     }
406 :     if ( dev_GetReceivePortInfo(devHandle_, this) == -1 )
407 :     {
408 :     LOGERROR("dev_GetReceivePortInfo() failed");
409 :     }
410 :     }
411 :    
412 : jtarlton 455
413 : amartin 403 /*
414 :     * Handle DMEV_PORT_CONNECT event.
415 :     */
416 :     void MmDevice::onPortConnect()
417 :     {
418 : jtarlton 455 LOGINFO("MmDevice::onPortConnect() device: " << getDeviceName());
419 : amartin 403 busy_ = false;
420 :     processPendingCommand();
421 :     }
422 : jtarlton 393
423 : jtarlton 455
424 : jtarlton 393 /*
425 : amartin 403 * Handle DMEV_PORT_CONNECT_FAIL event.
426 :     */
427 :     void MmDevice::onPortConnectFail()
428 :     {
429 : jtarlton 455 LOGWARN("MmDevice::onPortConnectFail() device: " << getDeviceName());
430 : amartin 403 busy_ = false;
431 :     processPendingCommand();
432 :     }
433 :    
434 : jtarlton 455
435 : amartin 403 /*
436 :     * Handle DMEV_PORT_DISCONNECT event.
437 :     */
438 :     void MmDevice::onPortDisconnect()
439 :     {
440 : jtarlton 455 LOGINFO("MmDevice::onPortDisconnect() device: " << getDeviceName());
441 : amartin 403 busy_ = false;
442 :     processPendingCommand();
443 :     }
444 :    
445 : jtarlton 455
446 : amartin 403 /*
447 :     * Handle DMEV_PORT_DISCONNECT_FAIL event.
448 :     */
449 :     void MmDevice::onPortDisconnectFail()
450 :     {
451 : jtarlton 455 LOGWARN("MmDevice::onPortDisconnectFail() device: " << getDeviceName());
452 : amartin 403 busy_ = false;
453 :     processPendingCommand();
454 :     }
455 :    
456 : jtarlton 455
457 : amartin 403 /*
458 : jtarlton 390 * Handle MMEV_PLAY event.
459 :     */
460 : jtarlton 459 void MmDevice::onPlay( MM_PLAY_RECORD_CMPLT* cmplt )
461 : jtarlton 390 {
462 : jtarlton 455 LOGINFO("MmDevice::onPlay() device: " << getDeviceName());
463 : jtarlton 459 LOGDEBUG(std::endl << *cmplt);
464 : amartin 403 if (playActive_)
465 :     {
466 :     playActive_ = false;
467 :     channelMgr_.onPlayCompleted(this);
468 :     }
469 :     busy_ = false;
470 :     processPendingCommand();
471 : jtarlton 390 }
472 : amartin 378
473 : jtarlton 455
474 : amartin 403 /*
475 :     * Handle MMEV_PLAY_ACK event.
476 :     */
477 :     void MmDevice::onPlayAck()
478 :     {
479 : jtarlton 455 LOGINFO("MmDevice::onPlayAck() device: " << getDeviceName());
480 : jtarlton 413 /* Wait for the onPlay() before clearing the busy flag */
481 : amartin 403 }
482 : jtarlton 390
483 : amartin 403
484 : jtarlton 390 /*
485 :     * Handle MMEV_PLAY_ACK_FAIL event.
486 :     */
487 :     void MmDevice::onPlayAckFail()
488 :     {
489 : jtarlton 455 LOGWARN("MmDevice::onPlayAckFail() device: " << getDeviceName());
490 : jtarlton 399 playActive_ = false;
491 : amartin 403 busy_ = false;
492 :     processPendingCommand();
493 : jtarlton 390 }
494 :    
495 :    
496 :     /*
497 :     * Handle MMEV_PLAY_FAIL event.
498 :     */
499 :     void MmDevice::onPlayFail()
500 :     {
501 : jtarlton 455 LOGWARN("MmDevice::onPlayFail() device: " << getDeviceName());
502 : jtarlton 399 playActive_ = false;
503 : jtarlton 580 channelMgr_.onPlayCompleted(this, PLAY_ERROR);
504 : amartin 403 busy_ = false;
505 :     processPendingCommand();
506 : jtarlton 390 }
507 :    
508 :    
509 :     /*
510 : jtarlton 399 * Handle MMEV_RECORD event.
511 : jtarlton 390 */
512 : jtarlton 459 void MmDevice::onRecord( MM_PLAY_RECORD_CMPLT* cmplt )
513 : jtarlton 390 {
514 : jtarlton 455 LOGINFO("MmDevice::onRecord() device: " << getDeviceName());
515 : jtarlton 459 LOGDEBUG(std::endl << *cmplt);
516 : amartin 406 if (recordActive_)
517 :     {
518 :     recordActive_ = false;
519 :     channelMgr_.onRecordCompleted(this);
520 :     }
521 : amartin 403 busy_ = false;
522 :     processPendingCommand();
523 : jtarlton 390 }
524 :    
525 :    
526 :     /*
527 : jtarlton 399 * Handle MMEV_RECORD_FAIL event.
528 :     */
529 :     void MmDevice::onRecordFail()
530 :     {
531 : jtarlton 455 LOGWARN("MmDevice::onRecordFail() device: " << getDeviceName());
532 : jtarlton 399 recordActive_ = false;
533 : jtarlton 580 channelMgr_.onRecordCompleted(this, RECORD_ERROR);
534 : amartin 403 busy_ = false;
535 :     processPendingCommand();
536 : jtarlton 399 }
537 :    
538 :    
539 :     /*
540 : jtarlton 390 * Handle MMEV_STOP_ACK event.
541 :     */
542 :     void MmDevice::onStopAck()
543 :     {
544 : jtarlton 455 LOGINFO("MmDevice::onStopAck() device: " << getDeviceName());
545 : jtarlton 413 /* Wait for the onPlay() before clearing the busy flag */
546 : jtarlton 390 }
547 :    
548 :    
549 :     /*
550 :     * Handle MMEV_STOP_ACK_FAIL event.
551 :     */
552 :     void MmDevice::onStopAckFail()
553 :     {
554 : jtarlton 455 LOGWARN("MmDevice::onStopAckFail() device: " << getDeviceName());
555 : amartin 403 busy_ = false;
556 :     processPendingCommand();
557 : jtarlton 390 }
558 :    
559 :    
560 :     /*
561 :     * Handle MMEV_RESET event.
562 :     */
563 :     void MmDevice::onReset()
564 :     {
565 : jtarlton 455 LOGINFO("MmDevice::onReset() device: " << getDeviceName());
566 : jtarlton 390 }
567 :    
568 :    
569 :     /*
570 :     * Handle MMEV_ERROR event.
571 :     */
572 :     void MmDevice::onError()
573 :     {
574 : jtarlton 455 LOGINFO("MmDevice::onError() device: " << getDeviceName());
575 : jtarlton 390 }
576 :    
577 :    
578 : jtarlton 393 /*
579 :     * Handler for DMEV_GET_TX_PORT_INFO events.
580 :     */
581 : jtarlton 455 void MmDevice::onGetTxPortInfo( DM_PORT_INFO_LIST* portInfoList )
582 : jtarlton 393 {
583 : jtarlton 455 LOGINFO("MmDevice::onGetTxPortInfo() device: " << getDeviceName());
584 : jtarlton 390
585 : jtarlton 393 txPortInfoList_ = *portInfoList;
586 : jtarlton 459 LOGDEBUG(std::endl << txPortInfoList_);
587 : jtarlton 393
588 :     for ( unsigned int i = 0; i < txPortInfoList_.unCount; i++ )
589 :     {
590 :     switch ( txPortInfoList_.port_info[i].port_media_type )
591 :     {
592 :     case DM_PORT_MEDIA_TYPE_AUDIO:
593 : jtarlton 413 audioPortTxInfo_ = txPortInfoList_.port_info[i];
594 : jtarlton 393 break;
595 :    
596 :     case DM_PORT_MEDIA_TYPE_VIDEO:
597 : jtarlton 413 videoPortTxInfo_ = txPortInfoList_.port_info[i];
598 : jtarlton 393 break;
599 :    
600 :     default:
601 :     break;
602 :     }
603 :     }
604 :     }
605 :    
606 :    
607 : jtarlton 390 /*
608 : jtarlton 393 * Handler for DMEV_GET_RX_PORT_INFO events.
609 :     */
610 : jtarlton 455 void MmDevice::onGetRxPortInfo( DM_PORT_INFO_LIST* portInfoList )
611 : jtarlton 393 {
612 : jtarlton 455 LOGINFO("MmDevice::onGetRxPortInfo() device: " << getDeviceName());
613 : jtarlton 393
614 :     rxPortInfoList_ = *portInfoList;
615 : jtarlton 459 LOGDEBUG(std::endl << rxPortInfoList_);
616 : jtarlton 393
617 :     for ( unsigned int i = 0; i < rxPortInfoList_.unCount; i++ )
618 :     {
619 :     switch ( rxPortInfoList_.port_info[i].port_media_type )
620 :     {
621 :     case DM_PORT_MEDIA_TYPE_AUDIO:
622 : jtarlton 413 audioPortRxInfo_ = rxPortInfoList_.port_info[i];
623 : jtarlton 393 break;
624 :    
625 :     case DM_PORT_MEDIA_TYPE_VIDEO:
626 : jtarlton 413 videoPortRxInfo_ = rxPortInfoList_.port_info[i];
627 : jtarlton 393 break;
628 :    
629 :     default:
630 :     break;
631 :     }
632 :     }
633 :     }
634 :    
635 :    
636 :     /*
637 : jtarlton 390 * Start playing.
638 :     */
639 :     bool MmDevice::play( const std::string& audioFile,
640 : jtarlton 536 const AudioCoderInfo& audioCoder,
641 :     const std::string& videoFile,
642 :     const VideoCoderInfo& videoCoder,
643 :     const std::string& lang )
644 : jtarlton 390 {
645 : amartin 403 if ( busy_ )
646 :     {
647 : jtarlton 536 pending_.push(MmCommand(MmCommand::PLAY, audioFile, audioCoder, videoFile, videoCoder, lang));
648 : amartin 403 return true;
649 :     }
650 : jtarlton 390
651 : jtarlton 536 std::string foundAudioFile, foundVideoFile;
652 :    
653 :     int count = 0; /* index for playRecordList_ */
654 :    
655 :     /* init audio
656 :     */
657 :     if ( !audioFile.empty() )
658 : jtarlton 390 {
659 : jtarlton 536 std::string foundAudioEncoding;
660 : jtarlton 390
661 : jtarlton 538 if ( !channelMgr_.getMediaFinder()->find(audioFile,
662 :     audioCoder.encoding,
663 :     lang,
664 :     foundAudioFile,
665 :     foundAudioEncoding) )
666 : jtarlton 536 {
667 : jtarlton 548 LOGERROR("MmDevice::play() device: " << getDeviceName() <<
668 :     " audio file not found: \"" << audioFile << "\"");
669 : jtarlton 536 return false;
670 :     }
671 :    
672 : jtarlton 390 INIT_MM_MEDIA_ITEM_LIST(&audioMediaList_);
673 :     INIT_MM_MEDIA_AUDIO(&audioMediaList_.item.audio);
674 :     audioMediaList_.ItemChain = EMM_ITEM_EOT;
675 : jtarlton 536 audioMediaList_.item.audio.szFileName = foundAudioFile.c_str();
676 : jtarlton 390
677 : jtarlton 536 if ( foundAudioEncoding == "vox" )
678 :     {
679 :     audioMediaList_.item.audio.codec.unCoding = MM_DATA_FORMAT_PCM;
680 :     audioMediaList_.item.audio.codec.unSampleRate = MM_DRT_8KHZ;
681 :     audioMediaList_.item.audio.codec.unBitsPerSample = 16;
682 :     audioMediaList_.item.audio.eFileFormat = EMM_AUD_FILEFORMAT_VOX;
683 :     audioMediaList_.item.audio.unOffset = 0;
684 :     }
685 :     else if ( foundAudioEncoding == "pcma" )
686 :     {
687 :     audioMediaList_.item.audio.codec.unCoding = MM_DATA_FORMAT_ALAW;
688 :     audioMediaList_.item.audio.codec.unSampleRate = MM_DRT_8KHZ;
689 :     audioMediaList_.item.audio.codec.unBitsPerSample = 8;
690 :     audioMediaList_.item.audio.eFileFormat = EMM_AUD_FILEFORMAT_VOX;
691 :     audioMediaList_.item.audio.unOffset = 0;
692 :     }
693 :     else if ( foundAudioEncoding == "pcmu" )
694 :     {
695 :     audioMediaList_.item.audio.codec.unCoding = MM_DATA_FORMAT_MULAW;
696 :     audioMediaList_.item.audio.codec.unSampleRate = MM_DRT_8KHZ;
697 :     audioMediaList_.item.audio.codec.unBitsPerSample = 8;
698 :     audioMediaList_.item.audio.eFileFormat = EMM_AUD_FILEFORMAT_VOX;
699 :     audioMediaList_.item.audio.unOffset = 0;
700 :     }
701 :     else
702 :     {
703 :     return false;
704 :     }
705 : jtarlton 390
706 : jtarlton 548 LOGDEBUG("MmDevice::play() device: " << getDeviceName() <<
707 :     ", a=" << audioMediaList_.item.audio.szFileName);
708 :    
709 : jtarlton 536 INIT_MM_PLAY_RECORD_LIST(&playRecordList_[count]);
710 :     playRecordList_[count].ItemType = EMM_MEDIA_TYPE_AUDIO;
711 :     playRecordList_[count].list = &audioMediaList_;
712 :     playRecordList_[count].ItemChain = EMM_ITEM_EOT;
713 : jtarlton 552 count++;
714 : jtarlton 390 }
715 : jtarlton 536
716 :     /* init video
717 :     */
718 :     if ( !videoFile.empty() )
719 : jtarlton 390 {
720 : jtarlton 536 std::string foundVideoEncoding;
721 :     unsigned int foundSize;
722 : jtarlton 390
723 : jtarlton 538 if ( !channelMgr_.getMediaFinder()->find(videoFile,
724 : jtarlton 548 videoCoder.encoding,
725 :     videoCoder.active_fmt.imageWidth,
726 :     lang,
727 :     foundVideoFile,
728 :     foundVideoEncoding,
729 :     foundSize) )
730 : jtarlton 536 {
731 : jtarlton 548 LOGERROR("MmDevice::play() device: " << getDeviceName() <<
732 :     " video file not found: \"" << videoFile << "\"");
733 : jtarlton 536 return false;
734 :     }
735 : jtarlton 390
736 :     INIT_MM_MEDIA_ITEM_LIST(&videoMediaList_);
737 :     INIT_MM_MEDIA_VIDEO(&videoMediaList_.item.video);
738 :     videoMediaList_.ItemChain = EMM_ITEM_EOT;
739 : jtarlton 536 videoMediaList_.item.video.szFileName = foundVideoFile.c_str();
740 : jtarlton 390
741 : jtarlton 536 if ( foundVideoEncoding == "mpv4-es" )
742 : jtarlton 390 {
743 : jtarlton 536 if ( foundSize == VIDEO_IMAGE_WIDTH_352 )
744 : jtarlton 390 {
745 :     videoMediaList_.item.video.codec.Coding = VIDEO_CODING_MP4V_ES;
746 :     videoMediaList_.item.video.codec.Profile = VIDEO_PROFILE_LEVEL_SP3_MPEG4;
747 :     videoMediaList_.item.video.codec.ImageWidth = VIDEO_IMAGE_WIDTH_352;
748 :     videoMediaList_.item.video.codec.ImageHeight = VIDEO_IMAGE_HEIGHT_288;
749 :     videoMediaList_.item.video.codec.FramesPerSec = VIDEO_FRAMESPERSEC_15;
750 :     }
751 :     else
752 :     {
753 :     videoMediaList_.item.video.codec.Coding = VIDEO_CODING_MP4V_ES;
754 :     videoMediaList_.item.video.codec.Profile = VIDEO_PROFILE_LEVEL_SP1_MPEG4;
755 :     videoMediaList_.item.video.codec.ImageWidth = VIDEO_IMAGE_WIDTH_176;
756 :     videoMediaList_.item.video.codec.ImageHeight = VIDEO_IMAGE_HEIGHT_144;
757 :     videoMediaList_.item.video.codec.FramesPerSec = VIDEO_FRAMESPERSEC_15;
758 :     }
759 :     }
760 : jtarlton 536 else /* h263 */
761 : jtarlton 390 {
762 : jtarlton 536 if ( foundSize == VIDEO_IMAGE_WIDTH_352 )
763 : jtarlton 390 {
764 :     videoMediaList_.item.video.codec.Coding = VIDEO_CODING_H263;
765 :     videoMediaList_.item.video.codec.Profile = VIDEO_PROFILE_0_H263;
766 :     videoMediaList_.item.video.codec.ImageWidth = VIDEO_IMAGE_WIDTH_352;
767 :     videoMediaList_.item.video.codec.ImageHeight = VIDEO_IMAGE_HEIGHT_288;
768 :     videoMediaList_.item.video.codec.FramesPerSec = VIDEO_FRAMESPERSEC_15;
769 :     }
770 :     else
771 :     {
772 :     videoMediaList_.item.video.codec.Coding = VIDEO_CODING_H263;
773 :     videoMediaList_.item.video.codec.Profile = VIDEO_PROFILE_0_H263;
774 :     videoMediaList_.item.video.codec.ImageWidth = VIDEO_IMAGE_WIDTH_176;
775 :     videoMediaList_.item.video.codec.ImageHeight = VIDEO_IMAGE_HEIGHT_144;
776 :     videoMediaList_.item.video.codec.FramesPerSec = VIDEO_FRAMESPERSEC_10;
777 :     }
778 :     }
779 : jtarlton 548 LOGDEBUG("MmDevice::play() device: " << getDeviceName() <<
780 :     ", v=" << videoMediaList_.item.video.szFileName);
781 : jtarlton 390
782 : jtarlton 536 /* append */
783 : jtarlton 548 if ( count > 0 )
784 :     {
785 : jtarlton 552 playRecordList_[count-1].ItemChain = EMM_ITEM_CONT;
786 : jtarlton 548 }
787 : jtarlton 536 INIT_MM_PLAY_RECORD_LIST(&playRecordList_[count]);
788 :     playRecordList_[count].ItemType = EMM_MEDIA_TYPE_VIDEO;
789 :     playRecordList_[count].list = &videoMediaList_;
790 :     playRecordList_[count].ItemChain = EMM_ITEM_EOT;
791 : jtarlton 390 }
792 :    
793 : jtarlton 536 INIT_MM_PLAY_RECORD_INFO(&playInfo_);
794 :     playInfo_.eFileFormat = EMM_FILE_FORMAT_UNDEFINED;
795 :     playInfo_.list = playRecordList_;
796 : jtarlton 390
797 :     if ( mm_Play(devHandle_, &playInfo_, NULL, NULL) == EMM_ERROR )
798 :     {
799 :     LOGERROR("mm_Play() failed on device: " << getDeviceName());
800 :     return false;
801 :     }
802 :     playActive_ = true;
803 : amartin 403 busy_ = true;
804 : jtarlton 390 return true;
805 :     }
806 :    
807 :    
808 :     /*
809 :     * Start recording.
810 :     */
811 : jtarlton 455 bool MmDevice::record( const std::string& audioFile,
812 : jtarlton 538 const AudioCoderInfo& audioCoder,
813 :     const std::string& videoFile,
814 :     const VideoCoderInfo& videoCoder,
815 :     const std::string& lang )
816 : jtarlton 390 {
817 : amartin 403 if ( busy_ )
818 :     {
819 : jtarlton 538 pending_.push(MmCommand(MmCommand::RECORD, audioFile, audioCoder, videoFile, videoCoder, lang));
820 : amartin 403 return true;
821 :     }
822 :    
823 : jtarlton 538 std::string audioFilePath = channelMgr_.getMediaFinder()->getMediaDir();
824 :     std::string videoFilePath = channelMgr_.getMediaFinder()->getMediaDir();
825 : jtarlton 390
826 : jtarlton 538 int count = 0; /* index for playRecordList_ */
827 : jtarlton 390
828 : jtarlton 538 /* init audio
829 : jtarlton 390 */
830 : jtarlton 538 if ( !audioFile.empty() )
831 : jtarlton 390 {
832 : jtarlton 538 audioFilePath += '/';
833 :     if ( !lang.empty() )
834 : jtarlton 390 {
835 : jtarlton 538 audioFilePath += lang;
836 :     audioFilePath += '/';
837 : jtarlton 390 }
838 : jtarlton 538 audioFilePath += audioCoder.encoding;
839 :     audioFilePath += '/';
840 :     audioFilePath += audioFile;
841 :    
842 :     INIT_MM_MEDIA_ITEM_LIST(&audioMediaList_);
843 :     INIT_MM_MEDIA_AUDIO(&audioMediaList_.item.audio);
844 :     audioMediaList_.ItemChain = EMM_ITEM_EOT;
845 :    
846 :     if ( audioCoder.encoding == "vox" )
847 :     {
848 :     audioMediaList_.item.audio.codec.unCoding = MM_DATA_FORMAT_PCM;
849 :     audioMediaList_.item.audio.codec.unSampleRate = MM_DRT_8KHZ;
850 :     audioMediaList_.item.audio.codec.unBitsPerSample = 16;
851 :     audioMediaList_.item.audio.eFileFormat = EMM_AUD_FILEFORMAT_VOX;
852 :     audioMediaList_.item.audio.unOffset = 0;
853 :     }
854 :     else if ( audioCoder.encoding == "pcma" )
855 :     {
856 :     audioMediaList_.item.audio.codec.unCoding = MM_DATA_FORMAT_ALAW;
857 :     audioMediaList_.item.audio.codec.unSampleRate = MM_DRT_8KHZ;
858 :     audioMediaList_.item.audio.codec.unBitsPerSample = 8;
859 :     audioMediaList_.item.audio.eFileFormat = EMM_AUD_FILEFORMAT_VOX;
860 :     audioMediaList_.item.audio.unOffset = 0;
861 :     }
862 :     else if ( audioCoder.encoding == "pcmu" )
863 :     {
864 :     audioMediaList_.item.audio.codec.unCoding = MM_DATA_FORMAT_MULAW;
865 :     audioMediaList_.item.audio.codec.unSampleRate = MM_DRT_8KHZ;
866 :     audioMediaList_.item.audio.codec.unBitsPerSample = 8;
867 :     audioMediaList_.item.audio.eFileFormat = EMM_AUD_FILEFORMAT_VOX;
868 :     audioMediaList_.item.audio.unOffset = 0;
869 :     }
870 : jtarlton 390 else
871 :     {
872 : jtarlton 538 return false;
873 : jtarlton 390 }
874 : jtarlton 538
875 :     audioMediaList_.item.audio.szFileName = audioFilePath.c_str();
876 :    
877 :     INIT_MM_PLAY_RECORD_LIST(&playRecordList_[count]);
878 :     playRecordList_[count].ItemType = EMM_MEDIA_TYPE_AUDIO;
879 :     playRecordList_[count].list = &audioMediaList_;
880 :     playRecordList_[count].ItemChain = EMM_ITEM_EOT;
881 : jtarlton 390 }
882 : jtarlton 538
883 :     /* init video
884 :     */
885 :     if ( !videoFile.empty() )
886 : jtarlton 390 {
887 : jtarlton 538 videoFilePath += '/';
888 :     if ( !lang.empty() )
889 : jtarlton 390 {
890 : jtarlton 538 videoFilePath += lang;
891 :     videoFilePath += '/';
892 : jtarlton 390 }
893 : jtarlton 538 videoFilePath += videoCoder.encoding;
894 :     videoFilePath += '/';
895 :    
896 :     INIT_MM_MEDIA_ITEM_LIST(&videoMediaList_);
897 :     INIT_MM_MEDIA_VIDEO(&videoMediaList_.item.video);
898 :     videoMediaList_.ItemChain = EMM_ITEM_EOT;
899 :    
900 :     if ( videoCoder.encoding == "mpv4-es" )
901 : jtarlton 390 {
902 : jtarlton 538 if ( videoCoder.active_fmt.imageWidth == VIDEO_IMAGE_WIDTH_352 )
903 :     {
904 :     videoFilePath += "CIF/";
905 :     videoMediaList_.item.video.codec.Coding = VIDEO_CODING_MP4V_ES;
906 :     videoMediaList_.item.video.codec.Profile = VIDEO_PROFILE_LEVEL_SP3_MPEG4;
907 :     videoMediaList_.item.video.codec.ImageWidth = VIDEO_IMAGE_WIDTH_352;
908 :     videoMediaList_.item.video.codec.ImageHeight = VIDEO_IMAGE_HEIGHT_288;
909 :     videoMediaList_.item.video.codec.FramesPerSec = VIDEO_FRAMESPERSEC_15;
910 :     }
911 :     else
912 :     {
913 :     videoFilePath += "QCIF/";
914 :     videoMediaList_.item.video.codec.Coding = VIDEO_CODING_MP4V_ES;
915 :     videoMediaList_.item.video.codec.Profile = VIDEO_PROFILE_LEVEL_SP1_MPEG4;
916 :     videoMediaList_.item.video.codec.ImageWidth = VIDEO_IMAGE_WIDTH_176;
917 :     videoMediaList_.item.video.codec.ImageHeight = VIDEO_IMAGE_HEIGHT_144;
918 :     videoMediaList_.item.video.codec.FramesPerSec = VIDEO_FRAMESPERSEC_15;
919 :     }
920 : jtarlton 390 }
921 : jtarlton 538 else /* h263 */
922 :     {
923 :     if ( videoCoder.active_fmt.imageWidth == VIDEO_IMAGE_WIDTH_352 )
924 :     {
925 :     videoFilePath += "CIF/";
926 :     videoMediaList_.item.video.codec.Coding = VIDEO_CODING_H263;
927 :     videoMediaList_.item.video.codec.Profile = VIDEO_PROFILE_0_H263;
928 :     videoMediaList_.item.video.codec.ImageWidth = VIDEO_IMAGE_WIDTH_352;
929 :     videoMediaList_.item.video.codec.ImageHeight = VIDEO_IMAGE_HEIGHT_288;
930 :     videoMediaList_.item.video.codec.FramesPerSec = VIDEO_FRAMESPERSEC_15;
931 :     }
932 :     else
933 :     {
934 :     videoFilePath += "QCIF/";
935 :     videoMediaList_.item.video.codec.Coding = VIDEO_CODING_H263;
936 :     videoMediaList_.item.video.codec.Profile = VIDEO_PROFILE_0_H263;
937 :     videoMediaList_.item.video.codec.ImageWidth = VIDEO_IMAGE_WIDTH_176;
938 :     videoMediaList_.item.video.codec.ImageHeight = VIDEO_IMAGE_HEIGHT_144;
939 :     videoMediaList_.item.video.codec.FramesPerSec = VIDEO_FRAMESPERSEC_10;
940 :     }
941 :     }
942 : jtarlton 390
943 : jtarlton 538 videoFilePath += videoFile;
944 :     videoMediaList_.item.video.szFileName = videoFilePath.c_str();
945 : jtarlton 390
946 : jtarlton 538 /* append */
947 :     playRecordList_[count++].ItemChain = EMM_ITEM_CONT;
948 : jtarlton 390
949 : jtarlton 538 INIT_MM_PLAY_RECORD_LIST(&playRecordList_[count]);
950 :     playRecordList_[count].ItemType = EMM_MEDIA_TYPE_VIDEO;
951 :     playRecordList_[count].list = &videoMediaList_;
952 :     playRecordList_[count].ItemChain = EMM_ITEM_EOT;
953 :     }
954 : jtarlton 390
955 : jtarlton 393 INIT_MM_RECORD_INFO(&recordInfo_);
956 : jtarlton 390 recordInfo_.eFileFormat = EMM_FILE_FORMAT_UNDEFINED;
957 :     recordInfo_.list = playRecordList_;
958 :    
959 : jtarlton 455 LOGDEBUG("MmDevice::record() device: " << getDeviceName() <<
960 :     " a=" << audioMediaList_.item.audio.szFileName <<
961 : jtarlton 393 ", v=" << videoMediaList_.item.video.szFileName);
962 : jtarlton 538
963 :     // XXX sendIFrameRequest();
964 :    
965 : jtarlton 390 if ( mm_Record(devHandle_, &recordInfo_, NULL, 0) == EMM_ERROR )
966 :     {
967 :     LOGERROR("mm_Record() failed on device: " << getDeviceName());
968 :     return false;
969 :     }
970 :     recordActive_ = true;
971 : amartin 403 busy_ = true;
972 : jtarlton 390 return true;
973 :     }
974 :    
975 :    
976 :     /*
977 :     * Stop current play.
978 :     */
979 :     bool MmDevice::stopPlay()
980 :     {
981 :     if ( playActive_ )
982 :     {
983 : jtarlton 455 LOGINFO("MmDevice::stopPlay() device: " << getDeviceName());
984 :    
985 : jtarlton 390 MM_STOP mmStopInfo[2];
986 :     MM_STOP_DETAILS mmStopDetails;
987 :     memset(&mmStopDetails, 0, sizeof(MM_STOP_DETAILS));
988 :    
989 :     mmStopInfo[0].unVersion = 0;
990 :     mmStopInfo[0].ItemChain = EMM_ITEM_CONT;
991 :     mmStopInfo[0].ItemType = EMM_STOP_VIDEO_PLAY;
992 :     mmStopInfo[0].details = mmStopDetails;
993 :     mmStopInfo[0].next = &mmStopInfo[1];
994 :    
995 :     mmStopInfo[1].unVersion = 0;
996 :     mmStopInfo[1].ItemChain = EMM_ITEM_EOT;
997 :     mmStopInfo[1].ItemType = EMM_STOP_AUDIO_PLAY;
998 :     mmStopInfo[1].details = mmStopDetails;
999 :     mmStopInfo[1].next = NULL;
1000 :    
1001 :     if ( mm_Stop(devHandle_, mmStopInfo, NULL) == EMM_ERROR )
1002 :     {
1003 :     LOGERROR("mm_Stop() failed on device: " << getDeviceName());
1004 :     return false;
1005 :     }
1006 :     playActive_= false;
1007 : amartin 403 busy_ = true;
1008 : jtarlton 390 }
1009 :     return true;
1010 :     }
1011 :    
1012 :    
1013 :     /*
1014 :     * Stop current recording.
1015 :     */
1016 :     bool MmDevice::stopRecord()
1017 :     {
1018 :     if ( recordActive_ )
1019 :     {
1020 : jtarlton 455 LOGINFO("MmDevice::stopRecord() device: " << getDeviceName());
1021 :    
1022 : jtarlton 390 MM_STOP mmStopInfo[2];
1023 :     MM_STOP_DETAILS mmStopDetails;
1024 :     memset(&mmStopDetails, 0, sizeof(MM_STOP_DETAILS));
1025 :    
1026 :     mmStopInfo[0].unVersion = 0;
1027 :     mmStopInfo[0].ItemChain = EMM_ITEM_CONT;
1028 :     mmStopInfo[0].ItemType = EMM_STOP_VIDEO_RECORD;
1029 :     mmStopInfo[0].details = mmStopDetails;
1030 :     mmStopInfo[0].next = &mmStopInfo[1];
1031 :    
1032 :     mmStopInfo[1].unVersion = 0;
1033 :     mmStopInfo[1].ItemChain = EMM_ITEM_EOT;
1034 :     mmStopInfo[1].ItemType = EMM_STOP_AUDIO_RECORD;
1035 :     mmStopInfo[1].details = mmStopDetails;
1036 :     mmStopInfo[1].next = NULL;
1037 :    
1038 :     if ( mm_Stop(devHandle_, mmStopInfo, 0) == EMM_ERROR )
1039 :     {
1040 :     LOGERROR("mm_Stop() failed on device: " << getDeviceName());
1041 :     return false;
1042 :     }
1043 :     recordActive_ = false;
1044 : amartin 403 busy_ = true;
1045 : jtarlton 390 }
1046 :     return true;
1047 :     }
1048 :    
1049 :    
1050 : amartin 378 /* vim:ts=4:set nu:
1051 :     * EOF
1052 :     */

No admin address has been configured
ViewVC Help
Powered by ViewVC 1.0.8