hi.  this is a patchish thingy that adds a stream requst servlet to fred,
despite GJ's advice to not do that.  I'd send a full patch, but it
seems that cvs.freenet.sourceforge.net is dead, and since I have to go
home from work,i'm just going to add the required changes to make this
thing go.

To Main.java, you need to add the following lines at line #1032 (on my
copy, which is a week old) in Main.java:

        setSetValue(params,"mainport.params.defaultServlet.uri", "/default");
        setSetValue(params,"mainport.params.defaultServlet.method", "GET");
        setSetValue(params,"mainport.params.defaultServlet.class", 
"freenet.client.http.RedirectServlet");
        setSetValue(params,"mainport.params.defaultServlet.name", "Web 
Interface Redirect");
        setSetValue(params,"mainport.params.defaultServlet.params.targetURL", 
"/servlet/nodeinfo/");

Also, in order to get the metadata out of
freenet/client/GetRequestProcess.java, I had to change the definition
of metadataBucket on line #24 to be public.  There is probably a
better way to achieve this, and I would be appriciated being
enligtened on this.

anyhow, the extra file is attached.  apologised for failure to be able
to cvs diff :(.  i ahte sorceforge :-p

Anyhow, to the functionality:  This can stream static files of any
type, sort of... I've only tested it with ogg, mp3, and asf files.  It
takes two http parameters, ?buffer=number for a buffer time in minutes
when chunkTime is known, ?htlStep=number for the increment to increase
HTL on the block requests, and ?htl=number for the htl of the main request,
you know, the usual shit.  I also attached the readme.txt of the insertion
stuff, which might interst people - i put the programs to insert stuff at
http://artificial-stupidity.net/~fish/.

        - fish
-------------- next part --------------
/* 
 * Streaming Audio/Video request servelet
 *
 * @author <a href="mailto:fish at artificial-stupidity.net">Jaymz Julian</a>
 * 
 */

package freenet.client.http;

import java.io.*;
import java.net.*;
import java.util.*;
import java.text.DateFormat;
import javax.servlet.*;
import javax.servlet.http.*;

import freenet.*;
import freenet.node.*;
import freenet.support.*;
import freenet.support.io.*;
import freenet.client.*;
import freenet.client.events.*;
import freenet.client.listeners.*;
import freenet.client.metadata.*;
import freenet.thread.*;

// FEC stuff
import com.onionnetworks.fec.*;
import com.onionnetworks.util.*;

public class StreamServlet extends FproxyServlet
{
        private boolean firstAccess = true;
        private Node node;
        protected static ClientFactory clientFactory;
        private ServletContext context;
        int maxHtl=50;

        public void init()
        {
                ServletContext context = getServletContext();
                node = (Node)context.getAttribute("freenet.node.Node");
                context = getServletContext();
                clientFactory = (ClientFactory) 
context.getAttribute("freenet.client.ClientFactory");
        }

        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException
        {
                if(firstAccess)
                {
                        init();
                        firstAccess=false;
                }
                int myHtl=15;
                String requestString;

                try {

                        requestString= 
freenet.support.URLDecoder.decode(req.getRequestURI());
                        // cut off the '/servlet/stream/' part ;)
                        requestString=requestString.substring(16);
                        //System.out.println(requestString);
                } 
                catch (Exception e)
                {
                        PrintWriter pw = resp.getWriter();
                        resp.setContentType("text/plain");
                        pw.println("freenet.support.URLDecoder.decode threw.  
shite.");
                        return;
                }

                // okay, get the main bit o metadata
                FreenetURI requestUri;
                try {
                        requestUri=new FreenetURI(requestString);
                }
                catch (MalformedURLException e)
                {
                         //writeErrorMessage(e, resp, null, key, null, htlUsed, 
null, null, tryNum+1)
                         writeErrorMessage(e, resp, null, requestString, null, 
myHtl, null, null, 0);
                         return;
                }

                // Get the user specified queue length
                int queueLength=3;
                String requestQueueLength=req.getParameter("buffer");
                if(requestQueueLength!=null)
                {
                        try {
                                
queueLength=Integer.parseInt(requestQueueLength);
                        }
                        catch (Exception e) {}
                }
                //System.out.println(queueLength);

                int htlStep=5;
                String requestHtlStep=req.getParameter("htlstep");
                if(requestHtlStep!=null)
                {
                        try {
                                htlStep=Integer.parseInt(requestHtlStep);
                        }
                        catch (Exception e) {}
                }
                String requestHtl=req.getParameter("htl");
                if(requestHtl!=null)
                {
                        try {
                                myHtl=Integer.parseInt(requestHtl);
                        }
                        catch (Exception e) {}
                }

                // Welcome to 'I hate you' theatre, with your 
                // host, dj fish!
                FileBucket metadata = new FileBucket();
                FileBucket data = new FileBucket();
                GetRequestProcess rp=new GetRequestProcess(requestUri, myHtl, 
data, new FileBucketFactory(), 0, true, null);
                Request r;
                while((r=rp.getNextRequest()) != null)
                {
                        try 
                        {
                                Client c = clientFactory.getClient(r);
                                c.start();
                        }
                        catch (IOException e)
                        {
                                PrintWriter pw = resp.getWriter();
                                resp.setContentType("text/plain");
                                pw.println("Society has collapsed!");
                                pw.println("Lets build villeges, and towns, and 
play each other at cricket!");
                                return;
                        }
                }

                if(rp.failed())
                {
                        PrintWriter pw = resp.getWriter();
                        resp.setContentType("text/plain");
                        pw.println("ERROR: Your key didn't work.  try a higher 
htl or something.");
                        pw.println("Someone should fix the call to 
writeErrorMessage to display that nice non-threatening freenet error message 
correctly");
                        //writeErrorMessage(rp.origThrowable, resp, null, 
requestString, null, myHtl, null, null, 1);
                        return;
                }

                // This is incredibly nasty - what's the *correct* way to do 
this?
                ReadInputStream myMetadataStream=new 
ReadInputStream(rp.metadataBucket.getInputStream());
                // skip ahead to the main data....
                try
                {
                        while(!myMetadataStream.readToEOF('\n', 
'\r').equalsIgnoreCase("Document"));
                }
                catch (Exception e)
                {
                        PrintWriter pw = resp.getWriter();
                        resp.setContentType("text/plain");
                        pw.println("Shouldn't happen...");
                        return;
                }
                FieldSet streamMetadata=new FieldSet();
                streamMetadata.parseFields(myMetadataStream);

                // Check that this is a correct stream
                FieldSet streamSpecific=streamMetadata.getSet("Stream");
                if(streamSpecific == null)
                {
                        PrintWriter pw = resp.getWriter();
                        resp.setContentType("text/plain");
                        pw.println("Invalid stream format: 
"+streamSpecific.get("Stream.Format"));
                        return;
                }
                if( !streamSpecific.get("Format").equalsIgnoreCase("Fish") &&
                        
!streamSpecific.get("Format").equalsIgnoreCase("fproxy") )
                {
                        PrintWriter pw = resp.getWriter();
                        resp.setContentType("text/plain");
                        pw.println("Invalid stream format: 
"+streamSpecific.get("Format"));
                        return;
                }

                // get the URI
                String myUri=streamSpecific.get("uri");
                if(myUri==null)
                {
                        myUri=requestString;
                }

                String mimetype=streamSpecific.get("type");
                if(mimetype==null)
                {
                        mimetype="audio/ogg";
                }

                String headerKey=streamSpecific.get("header");
                if(headerKey==null)
                {
                        headerKey="false";
                }

                // don't you love these big try/catch waterfalls, it's so much 
nicer
                // than Integer,parseInt behaving sanely, yes.
                int myStartChunk, myEndChunk, chunkSeconds, fecType, fecn, feck;
                try {
                        
myStartChunk=Integer.parseInt(streamSpecific.get("StartChunk"), 16);
                } catch (Exception e) {
                        myStartChunk= -1;
                }

                try {
                        
myEndChunk=Integer.parseInt(streamSpecific.get("EndChunk"), 16);
                } catch (Exception e) {
                        myEndChunk= -1;
                }

                try {
                        
chunkSeconds=Integer.parseInt(streamSpecific.get("ChunkSeconds"), 16);
                        // if chunk seconds exists. we should adjust the queue 
length to be in minutes!
                        int t=queueLength*60;
                        queueLength=t/chunkSeconds;

                        // round up, not down!
                        if((queueLength*chunkSeconds)<t)
                                queueLength++;
                } catch (Exception e) {
                        chunkSeconds=0;
                }

                try {
                        fecType=Integer.parseInt(streamSpecific.get("fecType"), 
16);
                } catch (Exception e) {
                        fecType= -1;
                }

                FieldSet fecSpecific=streamSpecific.getSet("fec");
                try {
                        fecn=Integer.parseInt(fecSpecific.get("n"), 16);
                } catch (Exception e) {
                        fecn= -1;
                }

                try {
                        feck=Integer.parseInt(fecSpecific.get("k"), 16);
                } catch (Exception e) {
                        feck= -1;
                }

                // For some bizzare reason, not setting this *first* will
                // cause my particular JVM to shit itself in a rather unfun
                // way.  AngyMunkey!
                //
                // This being said, on blackdown's 1.3 JVM, which co-incidently 
is 
                // the ONLY one which will both compile and run fred on my 
system,
                // reching the end of this function will sig11 the JVM anyhow.
                // I believe this to be sun/blackdown's anti-kiddie-porn filter 
:-p.
                //
                // So, waht I need, is a way to signal the JVM that this is 
just audio :-p.
                resp.setStatus(HttpServletResponse.SC_OK);
                resp.setContentType(mimetype);
                OutputStream out = resp.getOutputStream();

                // Get the FEC decoder
                FECCode 
fecbitch=FECCodeFactory.getDefault().createFECCode(feck, fecn);

                StreamChunkRequestor[] requestQueue=new 
StreamChunkRequestor[queueLength];
                // fill the head of the queue
                int currentChunk=myStartChunk;
                for(int c=0;c<queueLength;c++)
                {
                        // get the ASF or other encapsulation format header if 
it exists (as key 0)
                        if(c==0 && headerKey.equalsIgnoreCase("true"))
                        {
                                requestQueue[c]=new StreamChunkRequestor(myUri, 
0, fecType, fecn, feck, fecbitch, htlStep);
                                if(currentChunk==0)
                                        currentChunk++;
                        }
                        else
                        {
                                requestQueue[c]=new StreamChunkRequestor(myUri, 
currentChunk, fecType, fecn, feck, fecbitch, htlStep);
                                currentChunk++;
                        }
                        Thread t=new Thread(requestQueue[c]);
                        t.start();
                }
                while(true)
                {
                        boolean allWorking=true;
                        boolean failedBlock=false;
                        // check if the queue is full
                        for(int c=0;c<queueLength;c++)
                        {
                                // this request is finished progress
                                if(requestQueue[c].inProgress==true)
                                {
                                        allWorking=false;
                                }
                                else
                                {
                                        // check if all is kosher, or if there
                                        // is a block in the queue that failed 
to 
                                        // retrieve
                                        if(requestQueue[c].success==false)
                                                failedBlock=true;
                                }
                        }

                        // if we have all of the blocks in the queue, head on up
                        if(allWorking)
                        {
                                if(requestQueue[0].success)
                                {
                                        // write the data at the head of the
                                        // queue.
                                        out.write(requestQueue[0].outputData);
                                        // pop the top
                                        for(int c=1;c<queueLength;c++)
                                                
requestQueue[c-1]=requestQueue[c];

                                        // append the next block to the queue
                                        // if we havn't failed a block
                                        if(!failedBlock)
                                        {
                                                requestQueue[queueLength-1]=new 
StreamChunkRequestor(myUri, currentChunk, fecType, fecn, feck, fecbitch, 
htlStep);
                                                Thread t=new 
Thread(requestQueue[queueLength-1]);
                                                t.start();
                                                currentChunk++;
                                        }
                                }
                                else
                                {
                                        // When you walk away, you don't hear 
me say, Please.... oh baby, don't go
                                        // simple and clean is the way you're 
making me feel tonight!
                                        //System.out.println("Cleanly 
finished!");
                                        return;
                                }
                        }
                        try {
                                Thread.sleep(1000);
                        } catch (InterruptedException e) {}
                }
        }

        class StreamPartRequestor implements Runnable
        {
                public boolean success;
                public boolean inProgress;
                public int finalSize;
                public FileBucket data = new FileBucket();
                FreenetURI uri;
                int htl;
                FECCode fecbitch;
                int htlStep;

                StreamPartRequestor(String myuri, FECCode ifecbitch, int 
iHtlStep)
                {
                        try {
                                uri=new FreenetURI(myuri);
                        } catch (Exception e) {
                                System.out.println("Arg! Bad things happened in 
StreamPartRequestor");
                        }
                        htl=0;
                        success=inProgress=false;
                        finalSize=0;
                        fecbitch=ifecbitch;
                        htlStep=iHtlStep;
                }

                public void run()
                {
                        inProgress=true;
                        htl=htl+htlStep;
                        //System.out.println("Requesting "+uri+" at htl="+htl);

                        GetRequestProcess rp=new GetRequestProcess(uri, htl, 
data, new FileBucketFactory(), 0, true, null);
                        Request r;
                        while((r=rp.getNextRequest()) != null)
                        {
                                try 
                                {
                                        Client c = clientFactory.getClient(r);
                                        c.start();
                                }
                                catch (IOException e)
                                {
                                        System.out.println("You are here.  (You 
shouldn't be... bad things in StreamPartRequestor.run())");
                                }
                        }
                        if(!rp.failed())
                        {
                                // Get the final size for the completed block
                                try {
                                        ReadInputStream myMetadataStream=new 
ReadInputStream(rp.metadataBucket.getInputStream());
                                        // skip ahead to the main data....
                                        try
                                        {
                                                
while(!myMetadataStream.readToEOF('\n', '\r').equalsIgnoreCase("EndPart"));
                                        } catch (Exception e) {}

                                        FieldSet streamMetadata=new FieldSet();
                                        
streamMetadata.parseFields(myMetadataStream);
                                        FieldSet 
streamSpecific=streamMetadata.getSet("Stream");
                                        FieldSet 
fecSpecific=streamSpecific.getSet("fec");
                                        
finalSize=Integer.parseInt(fecSpecific.get("actualSize"), 16);
                                } catch (Exception e) {}

                                success=true;
                                //System.out.println("Retrieved block!  final 
size is "+finalSize);
                        }
                        inProgress=false;
                }
        }

        class StreamChunkRequestor implements Runnable
        {
                public boolean success;
                public boolean inProgress;
                String myUri;
                int myChunk;
                int fecType;
                int fecn;
                int feck;
                int htlStep;
                byte [] outputData;
                FECCode fecbitch;

                // welcome to iFreenet!
                StreamChunkRequestor(String iUri, int iChunk, int ifecType, int 
ifecn, int ifeck, FECCode ifecbitch, int iHtlStep)
                {
                        success=false;
                        inProgress=false;

                        myUri=iUri;
                        myChunk=iChunk;
                        fecType=ifecType;
                        fecn=ifecn;
                        feck=ifeck;
                        fecbitch=ifecbitch;
                        htlStep=iHtlStep;
                }

                public void run()
                {
                        inProgress=true;
                        StreamPartRequestor myRequestors[]=new 
StreamPartRequestor[fecn];

                        // request the chunks
                        for(int c=0;c<fecn;c++)
                        {
                                String realUri=myUri+"/"+myChunk+"/"+c;
                                myRequestors[c]=new 
StreamPartRequestor(realUri, fecbitch, htlStep);
                        }

                        int retrievedBlocks;
                        int progressBlocks;
                        do
                        {
                                retrievedBlocks=0;
                                progressBlocks=0;
                                for(int c=0;c<fecn;c++)
                                {
                                        if(myRequestors[c].inProgress==false)
                                        {
                                                
if(myRequestors[c].success==true)
                                                {
                                                        retrievedBlocks++;
                                                }
                                                else 
if(myRequestors[c].htl<maxHtl)
                                                {
                                                        Thread t=new 
Thread(myRequestors[c]);
                                                        t.start();
                                                        progressBlocks++;
                                                }
                                        }
                                        else
                                        {
                                                progressBlocks++;
                                        }
                                }
                                //System.out.println("Retrieved Blocks: 
"+retrievedBlocks);
                                //System.out.println("Blocks in progress: 
"+progressBlocks);
                                // sleep for a second
                                try {
                                        Thread.sleep(1000);
                                } catch (InterruptedException e) {}
                        } while(retrievedBlocks<feck && progressBlocks>0);

                        if(retrievedBlocks>=feck)
                        {

                                // create the structures for the decoder
                                Buffer[] landingZone= new Buffer[feck];
                                int[] index=new int[feck];
                                int lastBlock=0;
                                int finalSize=0;

                                for(int c=0;(c<fecn && lastBlock<feck);c++)
                                {
                                        if(myRequestors[c].success==true)
                                        {
                                                try {
                                                        // nasty, nasty, nasty, 
nasty dounle hadnling here....
                                                        byte[] b=new 
byte[(int)myRequestors[c].data.size()];
                                                        
myRequestors[c].data.getInputStream().read(b);
                                                        
landingZone[lastBlock]=new Buffer(b);
                                                        
finalSize=myRequestors[c].finalSize;

                                                } catch (IOException e)
                                                {
                                                        // somebody fucking 
some something!!!!
                                                }
                                                index[lastBlock]=c;
                                                lastBlock++;
                                        }
                                }

                                // I'd prefer to use System.arraycopy() here, 
but it
                                // seems to not quite do what I want, according 
to the
                                // code I am reading.  Arg.
                                outputData=new byte[finalSize];
                                int off=0;
                                for(int c=0;(c<feck && off<finalSize);c++)
                                {
                                        for(int d=0;(d<landingZone[c].b.length 
&& off<finalSize);d++)
                                        {
                                                
outputData[off]=landingZone[c].b[d];
                                                off++;
                                        }
                                }

                                // the chills that 
                                // you spill up my back
                                // keep me filled 
                                // with satistfaction
                                // when we're done
                                // satisfaction 
                                // oh what's the harm?
                                success=true;
                        }

                        inProgress=false;
                }
        }
}

-------------- next part --------------
An HTML attachment was scrubbed...
URL: 
<https://emu.freenetproject.org/pipermail/devl/attachments/20030117/e6ee53e5/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 230 bytes
Desc: not available
URL: 
<https://emu.freenetproject.org/pipermail/devl/attachments/20030117/e6ee53e5/attachment.pgp>

Reply via email to