[Jde-dev] mencoderRecorder
Sara Marugan
smarugan en gsyc.es
Mar Abr 27 22:29:55 CEST 2010
Me parece bien que tratemos de dejarlo lo más genérico posible.
Mañana le echo un ojo y subo mis cambios ok?
Saludo!
Roberto Calvo escribió:
> Al final dio tiempo! :-) Te cuento como ha quedado:
>
> - RecorderConfig ha cambiado. Ahora tiene protocol y device, donde se
> le puede pasar tanto un dispositivo v4l como un dispositivo cameraServer
> (como cualquier otro protocolo y device que nos inventemos en un
> futuro).
>
> - ffmpegRecorder ha cambiado a deviceRecorder.
>
> - El componente Recorder, tiene en su fichero de configuración con que
> "provider" va a grabar (ffmpeg, mencoder ...). Es lo único que conoce,
> todo lo demás le llega a través de RecorderConfig. Quizás tenga sentido
> que el provider también le llegue por el RecorderConfig, aunque mejor
> vamos poco a poco.
>
> - Si miras el método startRecording de recorder.cpp, sobre la línea 90,
> te he dejado ya el esqueleto para que cargues tu clase que sabe grabar
> "RECORDING_PROTOCOL_CAMERASERVER".
>
> - Echale un ojo al deviceRecorder, para hacer tu cameraServerRecorder
> (ponle el nombre que tu quieras) porque tienes que controlar con que
> provider se graba (ffmpeg, mencoder). No tienes que implementar todos,
> pero si al menos controlar los que no soportes.
>
> Y creo que nada más. Cuando te pongas con ello, dime si ves algo raro o
> se te ocurre otra manera para hacerlo.
>
> un saludete!
>
> El mar, 27-04-2010 a las 18:24 +0200, Roberto Calvo escribió:
>
>> Si lees esto a tiempo, no subas aún el código. Si no ha llegado a
>> tiempo, tampoco pasa nada :-)
>>
>> Al estar cambiando el diseño que hemos comentado, me he dado cosa de una
>> cuenta. El recorder en principio no tenía nada de configuración para
>> saber donde grabar, el llega todo a través del RecorderConfig, y
>> deberíamos seguir haciendolo así. Estoy cambiando el ICE para que no
>> tenga v4ldevice y v4lversion, sino protocol y device. De esa forma
>> podríamos tener estas configuraciones:
>>
>> protocol=v4l - device=/dev/video1
>>
>> protocol=cameraServer - device= proxy correspondiente.
>>
>> ¿Que te parece?
>>
>> El mar, 27-04-2010 a las 18:07 +0200, Sara Marugan escribió:
>>
>>> Jeje, la verdad es que sí había pensado lo de uno fuera de tipo "device"
>>> y otro "camera" (o image como dices tú) recorder. Así que si tú también
>>> lo has pensado cambiemoslo.
>>>
>>> Vale, pues lo subo al svn ya con el nombre cambiado y sólo quedaría por
>>> retocar lo de los procesos zombies, que no influye en la funcionalidad,
>>> pero está mejor liberar los recursos bien.
>>>
>>>
>>> Roberto Calvo wrote:
>>>
>>>> Genial!
>>>>
>>>> Los cambios que comentas no les veo problema. De hecho creo que tienes
>>>> permisos para subir al subversion no? Por mi no hay problema que lo
>>>> subas mientras compruebes que sigue todo funcionando, que la entrega del
>>>> TFM se acerca :-)
>>>>
>>>> Por cierto, echando un ojo al diseño, quizás tengo más sentido que
>>>> tengamos recorders dependiendo de como graban, y no dependiendo del
>>>> programa que usen. Por ejemplo podemos tener:
>>>>
>>>> - DeviceRecorder: Graban directamente del dispositivo físico
>>>> - ImageRecorder: Graban utilizando imágenes
>>>>
>>>> ¿Que te parece esta separación?
>>>> La veo mucho más útil y si en el futuro tenemos 4 programas diferentes
>>>> para generar ImageRecorder no hace falta tener 4 clases distintas,
>>>> simplemente configurar a esta clase de una manera determinada. En el
>>>> fichero de config podríamos indicar que programa se quiere utilizar para
>>>> grabar.
>>>>
>>>> De todas formas sube esto que has hecho al subversion para tenerlo. Yo
>>>> voy a pasar el ffmpegRecorder a DeviceRecorder para que soporte ffmpeg y
>>>> mencoder.
>>>>
>>>> un saludo!
>>>>
>>>> El mar, 27-04-2010 a las 13:27 +0200, Sara Marugan escribió:
>>>>
>>>>
>>>>> Hola,
>>>>>
>>>>> mencoderRecorder graba vídeo desde una cámara de jderobot. Sólo hay que
>>>>> indicarle el proxy de la cámara.
>>>>>
>>>>> Adjunto el parche que cambia alguna cosilla en recoder y en
>>>>> recorderconfig le he cambiado el tipo a duration porque al ser int daba
>>>>> problemas para llegar bien pasando por la red no sé por qué. Además
>>>>> width y height también son string.
>>>>>
>>>>> Adjunto también los nuevos ficheros de código.
>>>>>
>>>>> Un saludo!
>>>>> código fuente en C++ adjunto (parche_mencoderRecorder.cpp)
>>>>> Index: src/interfaces/slice/jderobot/recorder.ice
>>>>> ===================================================================
>>>>> --- src/interfaces/slice/jderobot/recorder.ice (revisión: 463)
>>>>> +++ src/interfaces/slice/jderobot/recorder.ice (copia de trabajo)
>>>>> @@ -64,7 +64,7 @@
>>>>> string frameRate;
>>>>>
>>>>> //! Seconds of recording
>>>>> - int duration;
>>>>> + string duration;
>>>>>
>>>>> //! Status
>>>>> // 0: In progress
>>>>> Index: src/components/recorder/ffmpegRecorder.h
>>>>> ===================================================================
>>>>> --- src/components/recorder/ffmpegRecorder.h (revisión: 463)
>>>>> +++ src/components/recorder/ffmpegRecorder.h (copia de trabajo)
>>>>> @@ -31,7 +31,7 @@
>>>>>
>>>>> public:
>>>>> /// \brief Recorder
>>>>> - ffmpegRecorder(const jderobotice::Context& context);
>>>>> + ffmpegRecorder(const jderobotice::Context& context,const jderobot::RecorderConfigPtr& recConfig);
>>>>>
>>>>>
>>>>> private:
>>>>> Index: src/components/recorder/recorder.cpp
>>>>> ===================================================================
>>>>> --- src/components/recorder/recorder.cpp (revisión: 463)
>>>>> +++ src/components/recorder/recorder.cpp (copia de trabajo)
>>>>> @@ -26,6 +26,7 @@
>>>>> #include <list>
>>>>>
>>>>> #include "ffmpegRecorder.h"
>>>>> +#include "mencoderRecorder.h"
>>>>>
>>>>> namespace RecorderProcess {
>>>>>
>>>>> @@ -44,8 +45,13 @@
>>>>> const Ice::Current& c)
>>>>> {
>>>>>
>>>>> - ffmpegRecorder* myRecorder = new ffmpegRecorder(context);
>>>>> - myRecorder->setConfig(recConfig);
>>>>> + GenericRecorder *myRecorder;
>>>>> + if(recConfig->cameraProxy.length()==0){
>>>>> + myRecorder =(ffmpegRecorder*) new ffmpegRecorder(context,recConfig);
>>>>> + }
>>>>> + else{
>>>>> + myRecorder = (mencoderRecorder*) new mencoderRecorder(context,recConfig);
>>>>> + }
>>>>>
>>>>> myRecorder->startRecording();
>>>>>
>>>>> Index: src/components/recorder/GenericRecorder.h
>>>>> ===================================================================
>>>>> --- src/components/recorder/GenericRecorder.h (revisión: 463)
>>>>> +++ src/components/recorder/GenericRecorder.h (copia de trabajo)
>>>>> @@ -37,7 +37,7 @@
>>>>>
>>>>> public:
>>>>> /// \brief Constructor
>>>>> - GenericRecorder(const jderobotice::Context& context) : mContext(context),mRecConfig(NULL), mId(), mStatus() {};
>>>>> + GenericRecorder(const jderobotice::Context& context,const jderobot::RecorderConfigPtr& recConfig) : mContext(context),mRecConfig(recConfig), mId(), mStatus() {};
>>>>>
>>>>>
>>>>> void setId (int id) {mId = id;};
>>>>> Index: src/components/recorder/Makefile.am
>>>>> ===================================================================
>>>>> --- src/components/recorder/Makefile.am (revisión: 463)
>>>>> +++ src/components/recorder/Makefile.am (copia de trabajo)
>>>>> @@ -2,19 +2,20 @@
>>>>> bin_PROGRAMS = recorder recorder_client
>>>>> endif
>>>>>
>>>>> -recorder_SOURCES = recorder.cpp ffmpegRecorder.cpp ffmpegRecorder.h GenericRecorder.h GenericRecorder.cpp
>>>>> +recorder_SOURCES = recorder.cpp ffmpegRecorder.cpp ffmpegRecorder.h mencoderRecorder.cpp mencoderRecorder.h GenericRecorder.h GenericRecorder.cpp
>>>>>
>>>>> -recorder_CXXFLAGS = -I$(top_srcdir)/src/interfaces/cpp $(LIBJDEROBOTICE_CPPFLAGS)
>>>>> +recorder_CPPFLAGS = -I$(top_srcdir)/src/interfaces/cpp $(LIBJDEROBOTICE_CPPFLAGS) $(LIBCOLORSPACESMM_LDFLAGS)
>>>>>
>>>>> -recorder_LDFLAGS = $(LIBICE) $(LIBICEUTIL) $(RECORDER_LIBS)
>>>>> +recorder_LDFLAGS = $(LIBICE) $(LIBICEUTIL) $(RECORDER_LIBS) $(LIBICEBOX) $(LIBICESTORM) $(LIBICESTORMSERVICE) $(LIBCOLORSPACESMM_LDFLAGS)
>>>>>
>>>>> recorder_LDADD = $(top_srcdir)/src/interfaces/cpp/jderobot/libJderobotInterfaces.la \
>>>>> - $(top_srcdir)/src/libs/jderobotice/libJderobotIce.la
>>>>> + $(top_srcdir)/src/libs/jderobotice/libJderobotIce.la \
>>>>> + $(top_srcdir)/src/libs/colorspaces/libcolorspacesmm.la
>>>>>
>>>>>
>>>>> recorder_client_SOURCES = recorder_client.cpp
>>>>>
>>>>> -recorder_client_CXXFLAGS = -I$(top_srcdir)/src/interfaces/cpp $(LIBJDEROBOTICE_CPPFLAGS)
>>>>> +recorder_client_CPPFLAGS = -I$(top_srcdir)/src/interfaces/cpp $(LIBJDEROBOTICE_CPPFLAGS)
>>>>>
>>>>> recorder_client_LDFLAGS = $(LIBICE) $(LIBICEUTIL) $(RECORDER_LIBS) -pthread
>>>>>
>>>>> Index: src/components/recorder/ffmpegRecorder.cpp
>>>>> ===================================================================
>>>>> --- src/components/recorder/ffmpegRecorder.cpp (revisión: 463)
>>>>> +++ src/components/recorder/ffmpegRecorder.cpp (copia de trabajo)
>>>>> @@ -27,7 +27,7 @@
>>>>>
>>>>> void *record_function( void *ptr );
>>>>>
>>>>> -ffmpegRecorder::ffmpegRecorder(const jderobotice::Context& context) : GenericRecorder(context)
>>>>> +ffmpegRecorder::ffmpegRecorder(const jderobotice::Context& context,const jderobot::RecorderConfigPtr& recConfig) : GenericRecorder(context,recConfig)
>>>>> {
>>>>>
>>>>> }
>>>>> Index: src/components/surveillance/surveillance.cpp
>>>>> ===================================================================
>>>>> --- src/components/surveillance/surveillance.cpp (revisión: 463)
>>>>> +++ src/components/surveillance/surveillance.cpp (copia de trabajo)
>>>>> @@ -89,7 +89,7 @@
>>>>>
>>>>> std::cout << " [*] New Recording launched, with ID = " << recId << " - " + timeRecording << " min." << std::endl;
>>>>>
>>>>> - sleep( recConfig->duration );
>>>>> + sleep( atoi(recConfig->duration.c_str()) );
>>>>>
>>>>> recManagerPrx->stopRecording(recId);
>>>>> }
>>>>> cabecera de código fuente en C adjunto (mencoderRecorder.h)
>>>>> /*
>>>>> *
>>>>> * Copyright (C) 1997-2010 JDE Developers Team
>>>>> *
>>>>> * This program is free software: you can redistribute it and/or modify
>>>>> * it under the terms of the GNU General Public License as published by
>>>>> * the Free Software Foundation, either version 3 of the License, or
>>>>> * (at your option) any later version.
>>>>> *
>>>>> * This program is distributed in the hope that it will be useful,
>>>>> * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>>> * GNU General Public License for more details.
>>>>> *
>>>>> * You should have received a copy of the GNU General Public License
>>>>> * along with this program. If not, see http://www.gnu.org/licenses/.
>>>>> *
>>>>> * Author : Sara Marugán Alonso <smarugan en gsyc.es>
>>>>> *
>>>>> */
>>>>>
>>>>> #ifndef MECODER_RECORDER_H
>>>>> #define MECODER_RECORDER_H
>>>>>
>>>>> #include "GenericRecorder.h"
>>>>> #include <jderobotice/component.h>
>>>>> #include <jderobotice/application.h>
>>>>> #include <jderobot/camera.h>
>>>>>
>>>>> class mencoderRecorder : public GenericRecorder
>>>>> {
>>>>>
>>>>> public:
>>>>> /// \brief Recorder
>>>>> mencoderRecorder(const jderobotice::Context& context,const jderobot::RecorderConfigPtr& recConfig);
>>>>>
>>>>> private:
>>>>>
>>>>> int doRecording();
>>>>> };
>>>>>
>>>>> #endif MECODER_RECORDER_H
>>>>> código fuente en C++ adjunto (mencoderRecorder.cpp)
>>>>> /*
>>>>> *
>>>>> * Copyright (C) 1997-2010 JDE Developers Team
>>>>> *
>>>>> * This program is free software: you can redistribute it and/or modify
>>>>> * it under the terms of the GNU General Public License as published by
>>>>> * the Free Software Foundation, either version 3 of the License, or
>>>>> * (at your option) any later version.
>>>>> *
>>>>> * This program is distributed in the hope that it will be useful,
>>>>> * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>>> * GNU General Public License for more details.
>>>>> *
>>>>> * You should have received a copy of the GNU General Public License
>>>>> * along with this program. If not, see http://www.gnu.org/licenses/.
>>>>> *
>>>>> * Author : Sara Marugán Alonso <smarugan en gsyc.es>
>>>>> *
>>>>> */
>>>>>
>>>>> #include "mencoderRecorder.h"
>>>>> #include <unistd.h>
>>>>> #include <signal.h>
>>>>> #include <sys/wait.h>
>>>>> #include <sys/types.h>
>>>>> #include <string.h>
>>>>> #include <strings.h>
>>>>> #include <jderobot/image.h>
>>>>> #include <stdio.h>
>>>>> #include <stdlib.h>
>>>>> #include <fcntl.h>
>>>>> #include <Ice/Ice.h>
>>>>> #include <IceUtil/Thread.h>
>>>>> #include <IceUtil/Time.h>
>>>>> #include <IceUtil/IceUtil.h>
>>>>> #include <IceUtil/Handle.h>
>>>>> #include <IceStorm/IceStorm.h>
>>>>> #include <iostream>
>>>>> #include <jderobot/camera.h>
>>>>> #include <colorspaces/colorspacesmm.h>
>>>>> #include <ctime>
>>>>>
>>>>>
>>>>> //////////////////////////////////////////////////////// IMAGE CONSUMER INTERFACE IMPLEMENTATION
>>>>> class ImageConsumerI: virtual public jderobot::ImageConsumer {
>>>>> private:
>>>>> jderobot::Time timeStampOld;
>>>>> jderobot::Time timeStampNew;
>>>>> int size;
>>>>> int fifo_fd;
>>>>> char *imageData;
>>>>> public:
>>>>> ImageConsumerI(int width,int height){
>>>>> size=width*height*3;
>>>>> imageData = (char*) malloc(size);
>>>>> timeStampOld.seconds=timeStampNew.seconds=0;
>>>>> timeStampOld.useconds=timeStampNew.useconds=0;
>>>>>
>>>>> // open fifo
>>>>> if ((fifo_fd=open("fifovid", O_WRONLY))<0){
>>>>> fprintf (stderr, "mencoderRecorder error: could not open fifo\n");
>>>>> }
>>>>> }
>>>>>
>>>>> ~ImageConsumerI(){
>>>>> // close fifo
>>>>> close (fifo_fd);
>>>>> unlink("fifovid");
>>>>>
>>>>> free(imageData);
>>>>> }
>>>>>
>>>>>
>>>>> virtual void report(const ::jderobot::ImageDataPtr& data,
>>>>> const Ice::Current&) {
>>>>>
>>>>> colorspaces::Image::FormatPtr fmt = colorspaces::Image::Format::searchFormat(data->description->format);
>>>>> if (!fmt)
>>>>> throw "Format not supported";
>>>>>
>>>>> colorspaces::Image img(data->description->width,
>>>>> data->description->height,
>>>>> fmt,
>>>>> &(data->pixelData[0]));
>>>>>
>>>>> timeStampOld=timeStampNew;
>>>>> timeStampNew=data->timeStamp;
>>>>>
>>>>> if(timeStampOld!=timeStampNew){
>>>>> int s=size/3;
>>>>> for (int j=0; j<s; j++) {
>>>>> imageData[j*3] = img.data[(s-1-j)*3];
>>>>> imageData[j*3+1] = img.data[(s-1-j)*3+1];
>>>>> imageData[j*3+2] = img.data[(s-1-j)*3+2];
>>>>> }
>>>>>
>>>>> if (write (fifo_fd, imageData, size)>size){
>>>>> fprintf (stderr, "mencoderRecorder error: could not write on fifo\n");
>>>>> }
>>>>> }
>>>>> }
>>>>> };
>>>>>
>>>>>
>>>>> //////////////////////////////////////////////////////////// THREAD
>>>>>
>>>>> void* callback(void* obj);
>>>>>
>>>>> class Thread {
>>>>> private:
>>>>> Ice::CommunicatorPtr communicator;
>>>>> jderobot::RecorderConfigPtr configuration;
>>>>>
>>>>> public:
>>>>> int main()
>>>>> {
>>>>> int imgwidth= atoi((char*)configuration->width.c_str());
>>>>> int imgheight= atoi((char*)configuration->height.c_str());
>>>>> int duration= atoi((char*)configuration->duration.c_str());
>>>>> IceStorm::TopicPrx topic;
>>>>>
>>>>> //IMAGE CONSUMERS INIT
>>>>>
>>>>> // generate camera adapter endpoint randomly
>>>>> srand((unsigned)time(0));
>>>>> char adapter_endpoint [256];
>>>>>
>>>>> int adapter_port = 10010 + (rand()%500)+1;
>>>>> sprintf(adapter_endpoint,"default -t 5000 -p %d",adapter_port);
>>>>>
>>>>> Ice::ObjectAdapterPtr adapter= communicator->createObjectAdapterWithEndpoints(adapter_endpoint,adapter_endpoint);
>>>>>
>>>>> if (adapter==0){
>>>>> fprintf(stderr,"mencoderRecorder error: could not create adapter for camera");
>>>>> return -1;
>>>>> }
>>>>>
>>>>> Ice::ObjectPrx obj = communicator->stringToProxy(configuration->cameraProxy);
>>>>>
>>>>> if (obj==0){
>>>>> fprintf(stderr,"mencoderRecorder error: could not create proxy\n");
>>>>> return(-1);
>>>>> }
>>>>>
>>>>> std::string topicName = configuration->name;
>>>>>
>>>>> IceStorm::TopicManagerPrx topicManager=IceStorm::TopicManagerPrx::checkedCast(obj);
>>>>>
>>>>> ImageConsumerI *imageConsumer = new ImageConsumerI(imgwidth,imgheight);
>>>>> Ice::ObjectPrx proxy = adapter->addWithUUID(imageConsumer)->ice_oneway();
>>>>>
>>>>> try {
>>>>> topic = topicManager->retrieve(topicName);
>>>>> IceStorm::QoS qos;
>>>>> topic->subscribeAndGetPublisher(qos, proxy);
>>>>> }
>>>>> catch (const IceStorm::NoSuchTopic& ex) {
>>>>> std::cerr << ex << std::endl;
>>>>> return -1;
>>>>> }
>>>>>
>>>>> adapter->activate();
>>>>>
>>>>> //communicator->waitForShutdown();
>>>>> sleep(duration);
>>>>>
>>>>> topic->unsubscribe(proxy);
>>>>>
>>>>> adapter->deactivate();
>>>>>
>>>>> delete imageConsumer;
>>>>> adapter=NULL;
>>>>>
>>>>> pthread_exit(0);
>>>>> }
>>>>>
>>>>> void run(Ice::CommunicatorPtr comm,jderobot::RecorderConfigPtr config)
>>>>> {
>>>>> communicator=comm;
>>>>> configuration=config;
>>>>> pthread_create(&thread, 0, &callback, this);
>>>>> sleep(1);
>>>>> }
>>>>>
>>>>> void stop(){
>>>>> }
>>>>>
>>>>> int join()
>>>>> {
>>>>> return pthread_join(thread, ret);
>>>>> }
>>>>>
>>>>> pthread_t thread;
>>>>> void** ret;
>>>>> }; // class Thread
>>>>>
>>>>>
>>>>> void* callback(void* obj)
>>>>> {
>>>>> static_cast<Thread*>(obj)->main();
>>>>> return(0);
>>>>> } // callback
>>>>>
>>>>>
>>>>> ///////////////////////////////////////// MENCODER_RECORDER FUCNTIONS
>>>>>
>>>>> mencoderRecorder::mencoderRecorder(const jderobotice::Context& context,
>>>>> const jderobot::RecorderConfigPtr& recConfig) : GenericRecorder(context,recConfig)
>>>>> {
>>>>> // create fifo
>>>>> unlink ("fifovid");
>>>>> if ( (mkfifo ("fifovid", 0600) != 0) ){
>>>>> fprintf (stderr, "mencoderRecorder error: could not create fifo\n");
>>>>> }
>>>>>
>>>>> // create thread for getting images
>>>>> Thread thread;
>>>>> thread.run(getContext().communicator(),recConfig);
>>>>> }
>>>>>
>>>>> int mencoderRecorder::doRecording()
>>>>> {
>>>>>
>>>>> int imgwidth= atoi((char*)getConfig()->width.c_str());
>>>>> int imgheight= atoi((char*)getConfig()->height.c_str());
>>>>>
>>>>>
>>>>> getContext().tracer().info ( "starting recording: Path = " +
>>>>> getConfig()->path + " - FrameRate = " +
>>>>> getConfig()->frameRate + " fps - ");
>>>>>
>>>>> char str[50];
>>>>> int file;
>>>>> file = open("/dev/null",O_RDWR);
>>>>> close(0); dup(file);
>>>>> close(1); dup(file);
>>>>> close(2); dup(file);
>>>>>
>>>>> // execute mencoder
>>>>> sprintf(str,"fps=%.1f:w=%d:h=%d:format=%s",atof((char*)getConfig()->frameRate.c_str()),imgwidth,imgheight, "rgb24");
>>>>> execlp("mencoder","mencoder","fifovid","-demuxer","rawvideo", "-rawvideo",
>>>>> str, "-o", (char*) (getConfig()->path).c_str(), "-ovc", "lavc" ,NULL);
>>>>>
>>>>> printf("mencoderRecorder error: cannot execute mencoder\n");
>>>>> return -1;
>>>>> }
>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Jde-developers mailing list
>>>>> Jde-developers en gsyc.es
>>>>> http://gsyc.escet.urjc.es/cgi-bin/mailman/listinfo/jde-developers
>>>>>
>>>>>
>>>>
>>>>
>>> _______________________________________________
>>> Jde-developers mailing list
>>> Jde-developers en gsyc.es
>>> http://gsyc.escet.urjc.es/cgi-bin/mailman/listinfo/jde-developers
>>>
>> _______________________________________________
>> Jde-developers mailing list
>> Jde-developers en gsyc.es
>> http://gsyc.escet.urjc.es/cgi-bin/mailman/listinfo/jde-developers
>>
>
>
More information about the Jde-developers
mailing list