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>