Roxen.git / server / modules / icecast / icecast.pike

version» Context lines:

Roxen.git/server/modules/icecast/icecast.pike:1:   inherit "module"; - constant cvs_version="$Id: icecast.pike,v 1.5 2001/04/11 08:45:49 per Exp $"; + constant cvs_version="$Id: icecast.pike,v 1.6 2001/04/11 14:05:46 per Exp $";   constant thread_safe=1;      #define BSIZE 8192   #define METAINTERVAL 4192      #include <module.h>   #include <roxen.h>   #include <stat.h>   #include <request_trace.h>   
Roxen.git/server/modules/icecast/icecast.pike:26:    Stdio.File next_file();    string pl_name();    mapping metadata();    void add_md_callback( function(mapping:void) f );    void remove_md_callback( function(mapping:void) f );   };      class MPEGStream( Playlist playlist )   {    array(function) callbacks = ({}); -  int bitrate; -  -  int stream_position; // 1000:th of a second. -  int stream_start = time()*1000+(int)(time(time())*1000); +  int bitrate; // bits/second +  int stream_start=realtime(), stream_position; // ┬ÁSeconds    Stdio.File fd;       string status()    {    return ""+playlist->status()+"<br />"+ -  sprintf( "Uptime: %.1fs Bitrate: %dKbit/sec", -  stream_position/1000.0, +  sprintf( "Stream position: %.1fs Bitrate: %dKbit/sec", +  stream_position/1000000.0,    bitrate/1000 );    }          void add_callback( function callback )    {    callbacks += ({ callback });    }       void remove_callback( function callback )
Roxen.git/server/modules/icecast/icecast.pike:61:    void call_callbacks( mixed ... args )    {    foreach( callbacks, function f )    if( mixed e = catch( f(@args) ) )    {    werror(describe_backtrace( e ) );    remove_callback( f );    }    }    +  static int last_hrtime, base_time; +     int realtime()    { -  return (time()*1000+(int)(time(time())*1000)) - stream_start; +  if(!last_hrtime) +  { +  last_hrtime = gethrtime(); +  base_time = 0;    } -  +  else +  { +  int nt = gethrtime(); +  base_time += nt-last_hrtime; +  last_hrtime = nt; +  } +  return base_time; +  }       void destroy()    {    catch(fd->set_blocking());    catch(fd->close());    }       void feeder_thread( )    {    while( sizeof(callbacks) && -  realtime() > stream_position ) +  ((realtime()-stream_start) > stream_position) )    {    string frame = get_frame();    while( !frame )    {    fd->set_blocking();    fd->close();    fd = playlist->next_file();    buffer = 0;    bpos = 0;    frame = get_frame();    } -  // Actually, this is supposed to be quite constant. -  stream_position += strlen(frame)*8000 / bitrate; -  werror( "%d\n", strlen(frame)*8000 / bitrate ); +  // Actually, this is supposed to be quite constant, but not all +  // frames are the same size. +  stream_position += strlen(frame)*8000000 / bitrate;    call_callbacks( frame );    }    if(!sizeof(callbacks))    {    stream_position = 0; -  stream_start = (time()*1000+(int)(time(time())*1000)); +  stream_start = realtime();    }    call_out( feeder_thread, 0.02 );    }       void start()    {    feeder_thread( ); // not actually a thread right now.    }       // Low level code.
Roxen.git/server/modules/icecast/icecast.pike:253:    return data + q;    }    }    return 0;    }   }      class Location( string location,    string initial,    MPEGStream stream, -  int max_connections ) +  int max_connections, +  string url, +  string name )      {    int accessed, denied;    int connections;       int total_time, max_time, successful;       array(Connection) conn = ({});       mapping handle( RequestID id )
Roxen.git/server/modules/icecast/icecast.pike:302:   // if( use_metadata )   // metahd = "icy-metaint:"+METAINTERVAL+"\r\n";    }       if( id->request_headers[ "x-audiocast-udpport" ] )    {    protocol = "AudioCast";    i = ("HTTP/1.0 200 OK\r\n"    "Server: "+roxen.version()+"\r\n"    "Content-type: audio/mpeg\r\n" -  "x-audiocast-name:"+(meta->name||meta->path)+"\r\n" +     "x-audiocast-gengre:"+(meta->gengre||"unknown")+"\r\n" -  "x-audiocast-url:"+("http://"+id->misc->host+ -  query_location()+location)+"\r\n"+ +  +((meta->url||url)?"x-audiocast-url:"+(meta->url||url)+"\r\n":"")+ +  "x-audiocast-name:"+(meta->name||name||meta->path)+"\r\n"    "x-audiocast-streamid:1\r\n"+metahd+    "x-audiocast-public:1\r\n"    "x-audiocast-bitrate:"+(stream->bitrate/1000)+"\r\n"    "x-audiocast-description:Served by Roxen\r\n"    "\r\n" );    }    else    {    i = ("ICY 200 OK\r\n"    "Server: "+roxen.version()+"\r\n"    "Content-type: audio/mpeg\r\n"    "icy-notice1:This stream requires a shoutcast compatible player.\r\n"    "icy-notice2:Roxen mod_mp3\r\n"+metahd+ -  "icy-name:"+(meta->name||meta->path)+"\r\n" +  "icy-name:"+(meta->name||name||meta->path)+"\r\n"    "icy-gengre:"+(meta->gengre||"unknown")+"\r\n" -  "icy-url:"+("http://"+id->misc->host+ -  query_location()+location)+"\r\n"+ +  +((meta->url||url)?"icy-url:"+(meta->url||url)+"\r\n":"")+    "icy-pub:1\r\n"    "icy-br:"+(stream->bitrate/1000)+"\r\n"    "\r\n" );    }    if( initial )    i += initial;       conn += ({ Connection( id->my_fd, i,protocol,use_metadata,    stream,    lambda( Connection c ){
Roxen.git/server/modules/icecast/icecast.pike:433:    current_md->name || current_md->path,    "")+"\0";    while( strlen(s) & 15 ) s+= "\0";    s[0]=strlen(s);    return s;    }       static void callback( string frame )    {    buffer += ({ frame }); -  if( sizeof( buffer ) > 10 ) +  if( sizeof( buffer ) > 100 )    {    skipped++;    buffer = buffer[1..];    }    if( sizeof( buffer ) == 1 ) -  send_more(); +  { +  remove_call_out( send_more ); +  call_out( send_more, 0.01 );    } -  +  }       int headers_done;       static void send_more()    {    if( !strlen(current_block) )    {    headers_done = 1;    if( !sizeof(buffer) ) -  { +     return; -  } +     current_block = buffer[0];       if( do_meta )    {    if( (strlen( current_block ) + sent_bytes) - last_meta >= METAINTERVAL )    {    last_meta = (strlen( current_block ) + sent_bytes);    current_block += gen_metadata();    }    }
Roxen.git/server/modules/icecast/icecast.pike:517:    stream->playlist->add_md_callback( md_callback );    }   }         class VarStreams   {    inherit Variable.List;    constant type="StreamList";    -  // location +  // loc    // playlist    // jingle    // maxconn -  +  // URL +  // name    string render_row( string prefix, mixed val, int width )    {    string res = "<input type=hidden name='"+prefix+"' value='"+prefix+"' />"; -  array split = val/"\0"; -  res += (query_location()+"<input name='"+prefix+"loc' value='"+ -  Roxen.http_encode_string(split[0])+"' size=20/>"); +  +  mapping m = decode_stream( val ); +  +  res += "<table>"; +  +  +  res += "<tr><td>Location "+query_location()+"</td><td>"; +  res += ("<input name='"+prefix+"loc' value='"+ +  Roxen.html_encode_string(m->location||"")+"' size=20/>"); +  res += "</td><td>Source</td><td>";    res += "<select name='"+prefix+"playlist'>";    mapping pl;    foreach( sort(indices(pl=playlists(0))), string p ) -  if( pl[ p ]->sname() == split[1] ) -  res += "<option value='"+pl[p]->sname()+"' selected='t'>"+p+"</option>"; +  if( pl[ p ]->sname() == m->playlist ) +  res +="<option value='"+pl[p]->sname()+"' selected='t'>"+p+"</option>";    else -  res += "<option value='"+pl[p]->sname()+"'>"+p+"</option>"; -  res += "</select><br />"; -  res += ("conn: <input name='"+prefix+"conn' value='"+ -  ((int)split[3]||10)+"' size=5/>"); -  res += ("jingle: <input name='"+prefix+"jingle' value='"+ -  Roxen.http_encode_string(split[2])+"' size=40/>"); -  return res; +  res +="<option value='"+pl[p]->sname()+"'>"+p+"</option>"; +  res += "</select></td></tr><tr>"; +  +  res += ("<td>Max. conn</td><td><input name='"+prefix+"conn' value='"+ +  (m->maxconn||10)+"' size=5/></td> "); +  +  res += ("<td>Name</td><td> <input name='"+prefix+"name' value='"+ +  Roxen.html_encode_string(m->name || "")+"' size=20/></td></tr>"); +  +  res += ("<tr><td>Jingle</td><td colspan=3>" +  "<input name='"+prefix+"jingle' value='"+ +  Roxen.html_encode_string(m->jingle || "")+"' size=50/></td></tr>"); +  +  res += ("<td>URL</td><td colspan=3> <input name='"+prefix+"url' value='"+ +  Roxen.html_encode_string(m->url || "")+"' size=50/></td>"); +  +  return res+"</tr></table>";    }       string transform_from_form( string v, mapping va )    {    if( v == "" ) return "\0\0\0\0";    v = v[strlen(path())..];    return va[v+"loc"]+"\0"+va[v+"playlist"]+"\0"+va[v+"jingle"] -  +"\0"+va[v+"conn"]; +  +"\0"+va[v+"conn"]+"\0"+va[v+"url"]+"\0"+va[v+"name"];    }    -  array(mapping) get_streams() +  mapping decode_stream( string s )    { -  array res = ({}); -  foreach( query(), string s ) -  { +     mapping m = ([]);    array a = s/"\0";    m->location = a[0]; -  m->playlist = a[1]; -  m->jingle = a[2]; -  m->maxconn = (int)a[3]; -  if( !strlen( m->jingle ) ) -  m_delete( m, "jingle" ); -  res += ({ m }); +  if( sizeof( a ) > 1 ) m->playlist = a[1]; +  if( sizeof( a ) > 2 ) m->jingle = a[2]; +  if( sizeof( a ) > 3 ) m->maxconn = (int)a[3]; +  if( sizeof( a ) > 4 ) m->url = a[4]; +  if( sizeof( a ) > 5 ) m->name = a[5]; +  if( !strlen( m->jingle ) ) m_delete( m, "jingle" ); +  if( !strlen( m->url ) ) m_delete( m, "url" ); +  if( !strlen( m->name ) ) m_delete( m, "name" ); +  return m;    } -  return res; +  array(mapping) get_streams() +  { +  return map( query(), decode_stream );    }   }      void create()   {    defvar( "streams", VarStreams( ({}), 0, _(0,"Streams"),    _(0,"All the streams") ) );    defvar("location", "/strm/",    _(0,"Mount point"), TYPE_LOCATION|VAR_INITIAL,    _(0,"Where the module will be mounted in the site's virtual "
Roxen.git/server/modules/icecast/icecast.pike:618:    }    if( !mps )    continue;    if( !locations[ strm->location ] )    locations[ strm->location ] =    Location( strm->location,    (strm->jingle?    Stdio.read_file( strm->jingle ) :    0 ),    mps, -  (int)strm->maxconn ); +  (int)strm->maxconn, +  strm->url, +  strm->name );    }   }      void start()   {    call_out( st2, 0.5 );   }      mapping find_file( string f, RequestID id )   {