changeset 9850:564678d0bc15

A new stream wich allow access to MPlayer stream accross the network. URL is mpst://host[:port]/remote_url where remote_url is any valid MPlayer url.
author albeu
date Sun, 06 Apr 2003 16:33:13 +0000
parents 5b649442fe72
children 6a348d3ed626
files libmpdemux/netstream.h libmpdemux/stream_netstream.c
diffstat 2 files changed, 418 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/netstream.h	Sun Apr 06 16:33:13 2003 +0000
@@ -0,0 +1,122 @@
+
+/*
+ * Common stuff for netstream
+ * Packets and so on are defined here along with a few helpers
+ * wich are used by both the client and the server
+ */
+
+typedef struct mp_net_stream_packet_st {
+  uint16_t len;
+  uint8_t cmd;
+  char data[0];
+} __attribute__ ((packed))  mp_net_stream_packet_t;
+
+#define PACKET_MAX_SIZE 4096
+
+// Commands sent by the client
+#define NET_STREAM_OPEN 0
+// data is the url
+#define NET_STREAM_FILL_BUFFER 1
+// data is an uint16 wich is the max len of the data to return
+#define NET_STREAM_SEEK 3
+// data is an uint64 wich the pos where to seek
+#define NET_STREAM_CLOSE 4
+// no data
+#define NET_STREAM_RESET 5
+// no data
+
+// Server response
+#define NET_STREAM_OK 128
+// Data returned if open is successful
+typedef struct mp_net_stream_opened_st {
+  uint32_t file_format;
+  uint32_t flags;
+  uint32_t sector_size;
+  uint64_t start_pos;
+  uint64_t end_pos;
+}  __attribute__ ((packed))  mp_net_stream_opened_t;
+// FILL_BUFFER return the data
+// CLOSE return nothing
+#define NET_STREAM_ERROR 129
+// Data is the error message (if any ;)
+
+static int net_read(int fd, char* buf, int len) {
+  int r = 0;
+  while(len) {
+    r = read(fd,buf,len);
+    if(r <= 0) {
+      if(errno == EINTR) continue;
+      if(r < 0)
+	mp_msg(MSGT_NETST,MSGL_ERR,"Read failed: %s\n",strerror(errno));
+      return 0;
+    }
+    len -= r;
+  }
+  return 1;
+}
+
+static mp_net_stream_packet_t* read_packet(int fd) {
+  uint16_t len;
+  mp_net_stream_packet_t* pack = 
+    (mp_net_stream_packet_t*)malloc(sizeof(mp_net_stream_packet_t));
+  
+  if(!net_read(fd,(char*)pack,sizeof(mp_net_stream_packet_t))) {
+    free(pack);
+    return NULL;
+  }
+  
+  if(pack->len < sizeof(mp_net_stream_packet_t)) {
+    mp_msg(MSGT_NETST,MSGL_WARN,"Got invalid packet (too small: %d)\n",pack->len);
+    free(pack);
+    return NULL;
+  }
+  if(pack->len > PACKET_MAX_SIZE) {
+    mp_msg(MSGT_NETST,MSGL_WARN,"Got invalid packet (too big: %d)\n",pack->len);
+    free(pack);
+    return NULL;
+  }
+  len = pack->len;
+  if(len > sizeof(mp_net_stream_packet_t)) {
+    pack = realloc(pack,len);
+    if(!pack) {
+      mp_msg(MSGT_NETST,MSGL_ERR,"Failed to get memory for the packet (%d bytes)\n",len);
+      return NULL;
+    }
+    if(!net_read(fd,pack->data,len - sizeof(mp_net_stream_packet_t)))
+      return NULL;
+  }
+  //  printf ("Read packet %d %d %d\n",fd,pack->cmd,pack->len);
+  return pack;
+}
+
+static int net_write(int fd, char* buf, int len) {
+  int w;
+  while(len) {
+    w = write(fd,buf,len);
+    if(w <= 0) {
+      if(errno == EINTR) continue;
+      if(w < 0)
+	mp_msg(MSGT_NETST,MSGL_ERR,"Write failed: %s\n",strerror(errno));
+      return 0;
+    }
+    len -= w;
+  }
+  return 1;
+}
+
+static int write_packet(int fd, uint8_t cmd,char* data,int len) {
+  mp_net_stream_packet_t* pack = malloc(len + sizeof(mp_net_stream_packet_t));
+  
+  if(len > 0 && data)
+    memcpy(pack->data,data,len);
+  pack->len = len + sizeof(mp_net_stream_packet_t);
+  pack->cmd = cmd;
+  
+  //  printf("Write packet %d %d (%p) %d\n",fd,cmd,data,len);
+  if(net_write(fd,(char*)pack,pack->len)) {
+    free(pack);
+    return 1;
+  }
+  free(pack);
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/stream_netstream.c	Sun Apr 06 16:33:13 2003 +0000
@@ -0,0 +1,296 @@
+/*
+ *  stream_netstream.c
+ *
+ *	Copyright (C) Alban Bedel - 04/2003
+ *
+ *  This file is part of MPlayer, a free movie player.
+ *	
+ *  MPlayer 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 2, or (at your option)
+ *  any later version.
+ *   
+ *  MPlayer 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 GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ *
+ */
+
+/*
+ *  Net stream allow you to access MPlayer stream accross a tcp
+ *  connection.
+ *  Note that at least mf and tv use a dummy stream (they are
+ *  implemented at the demuxer level) so you won't be able to
+ *  access those :(( but dvd, vcd and so on should work perfectly
+ *  (if you have the bandwidth ;)
+ *   A simple server is in TOOLS/netstream.
+ *
+ */
+
+
+#include "config.h"
+
+#ifdef STREAMING
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "mp_msg.h"
+#include "stream.h"
+#include "help_mp.h"
+#include "../m_option.h"
+#include "../m_struct.h"
+
+#include "netstream.h"
+
+static struct stream_priv_s {
+  char* host;
+  int port;
+  char* url;
+} stream_priv_dflts = {
+  NULL,
+  10000,
+  NULL
+};
+
+#define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
+/// URL definition
+static m_option_t stream_opts_fields[] = {
+  {"hostname", ST_OFF(host), CONF_TYPE_STRING, 0, 0 ,0, NULL},
+  {"port", ST_OFF(port), CONF_TYPE_INT, M_OPT_MIN, 1 ,0, NULL},
+  {"filename", ST_OFF(url), CONF_TYPE_STRING, 0, 0 ,0, NULL},
+  { NULL, NULL, 0, 0, 0, 0,  NULL }
+};
+static struct m_struct_st stream_opts = {
+  "netstream",
+  sizeof(struct stream_priv_s),
+  &stream_priv_dflts,
+  stream_opts_fields
+};
+
+//// When the cache is running we need a lock as
+//// fill_buffer is called from another proccess
+static int lock_fd(int fd) {
+  struct flock lock;
+
+  memset(&lock,0,sizeof(struct flock));
+  lock.l_type = F_WRLCK;
+
+  mp_msg(MSGT_STREAM,MSGL_DBG2, "Lock (%d)\n",getpid());
+  do {    
+    if(fcntl(fd,F_SETLKW,&lock)) {
+      if(errno == EAGAIN) continue;
+      mp_msg(MSGT_STREAM,MSGL_ERR, "Failed to get the lock: %s\n",
+	     strerror(errno));
+      return 0;
+    }
+  } while(0);
+  mp_msg(MSGT_STREAM,MSGL_DBG2, "Locked (%d)\n",getpid());
+  return 1;
+}
+
+static int unlock_fd(int fd) {
+  struct flock lock;
+
+  memset(&lock,0,sizeof(struct flock));
+  lock.l_type = F_UNLCK;
+
+  mp_msg(MSGT_STREAM,MSGL_DBG2, "Unlock (%d)\n",getpid());
+  if(fcntl(fd,F_SETLK,&lock)) {
+    mp_msg(MSGT_STREAM,MSGL_ERR, "Failed to release the lock: %s\n",
+	   strerror(errno));
+    return 0;
+  }
+  return 1;
+}
+
+static mp_net_stream_packet_t* send_net_stream_cmd(stream_t *s,uint16_t cmd,char* data,int len) {
+  mp_net_stream_packet_t* pack;
+
+  // Cache is enabled : lock
+  if(s->cache_data && !lock_fd(s->fd))
+    return NULL;
+  // Send a command
+  if(!write_packet(s->fd,cmd,data,len)) {
+    if(s->cache_data) unlock_fd(s->fd);
+    return 0;
+  }
+  // Read the response
+  pack = read_packet(s->fd);
+  // Now we can unlock
+  if(s->cache_data) unlock_fd(s->fd);
+
+  if(!pack)
+    return NULL;
+
+  switch(pack->cmd) {
+  case NET_STREAM_OK:
+    return pack;
+  case NET_STREAM_ERROR:
+    if(pack->len > sizeof(mp_net_stream_packet_t))
+      mp_msg(MSGT_STREAM,MSGL_ERR, "Fill buffer failed: %s\n",pack->data);
+    else
+      mp_msg(MSGT_STREAM,MSGL_ERR, "Fill buffer failed\n");
+    free(pack);
+    return NULL;
+  }
+  
+  mp_msg(MSGT_STREAM,MSGL_ERR, "Unknow response to %d: %d\n",pack->cmd);
+  free(pack);
+  return NULL;
+}
+
+static int fill_buffer(stream_t *s, char* buffer, int max_len){
+  uint16_t len = max_len;
+  mp_net_stream_packet_t* pack;
+
+  pack = send_net_stream_cmd(s,NET_STREAM_FILL_BUFFER,(char*)&len,2);
+  if(!pack) {
+    return -1;
+  }
+  len = pack->len - sizeof(mp_net_stream_packet_t);
+  if(len > max_len) {
+    mp_msg(MSGT_STREAM,MSGL_ERR, "Got a too big a packet %d / %d\n",len,max_len);
+    free(pack);
+    return 0;
+  }
+  if(len > 0)
+    memcpy(buffer,pack->data,len);
+  free(pack);
+  return len;
+}
+
+
+static int seek(stream_t *s,off_t newpos) {
+  uint64_t pos = (uint64_t)newpos;
+  mp_net_stream_packet_t* pack;
+  
+  pack = send_net_stream_cmd(s,NET_STREAM_SEEK,(char*)&pos,8);
+  if(!pack) {    
+    return 0;
+  }
+  s->pos = newpos;
+  free(pack);
+  return 1;
+}
+
+static int net_stream_reset(struct stream_st *s) {
+  mp_net_stream_packet_t* pack;
+  
+  pack = send_net_stream_cmd(s,NET_STREAM_RESET,NULL,0);  
+  if(!pack) {
+    return 0;
+  }
+  free(pack);
+  return 1;
+}
+ 
+static int control(struct stream_st *s,int cmd,void* arg) {
+  switch(cmd) {
+  case STREAM_CTRL_RESET:
+    return net_stream_reset(s);
+  }
+  return STREAM_UNSUPORTED;
+}
+
+static void close_s(struct stream_st *s) {
+  mp_net_stream_packet_t* pack;
+  
+  pack = send_net_stream_cmd(s,NET_STREAM_CLOSE,NULL,0);
+  if(pack)
+    free(pack);
+}
+
+static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
+  int f;
+  struct stream_priv_s* p = (struct stream_priv_s*)opts;
+  mp_net_stream_packet_t* pack;
+  mp_net_stream_opened_t* opened;
+
+  if(mode != STREAM_READ)
+    return STREAM_UNSUPORTED;
+
+  if(!p->host) {
+    mp_msg(MSGT_OPEN,MSGL_ERR, "We need an host name (ex: mpst://server.net/cdda://5)\n");
+    m_struct_free(&stream_opts,opts);
+    return STREAM_ERROR;
+  }
+  if(!p->url || strlen(p->url) == 0) {
+    mp_msg(MSGT_OPEN,MSGL_ERR, "We need a remote url (ex: mpst://server.net/cdda://5)\n");
+    m_struct_free(&stream_opts,opts);
+    return STREAM_ERROR;
+  }
+
+  f = connect2Server(p->host,p->port);
+  if(f < 0) {
+    mp_msg(MSGT_OPEN,MSGL_ERR, "Connection to %s:%d failed\n",p->host,p->port);
+    m_struct_free(&stream_opts,opts);
+    return STREAM_ERROR;
+  }
+  stream->fd = f;
+  /// Now send an open command
+  pack = send_net_stream_cmd(stream,NET_STREAM_OPEN,p->url,strlen(p->url) + 1);
+  if(!pack) {
+    goto error;
+  }
+  
+  if(pack->len != sizeof(mp_net_stream_packet_t) + 
+     sizeof(mp_net_stream_opened_t)) {
+    mp_msg(MSGT_OPEN,MSGL_ERR, "Invalid open response packet len (%d bytes)\n",pack->len);
+    free(pack);
+    goto error;
+  }
+  
+  opened = (mp_net_stream_opened_t*)pack->data;
+  *file_format = opened->file_format;
+  stream->flags = opened->flags;
+  stream->sector_size = opened->sector_size;
+  stream->start_pos = opened->start_pos;
+  stream->end_pos = opened->end_pos;
+
+  stream->fill_buffer = fill_buffer;
+  stream->control = control;
+  if(stream->flags & STREAM_SEEK)
+    stream->seek = seek;
+  stream->close = close_s;
+
+  free(pack);
+  m_struct_free(&stream_opts,opts);
+
+  return STREAM_OK;
+
+  error:
+  close(f);
+  m_struct_free(&stream_opts,opts);
+  return STREAM_ERROR;
+}
+
+stream_info_t stream_info_netstream = {
+  "Net stream",
+  "netstream",
+  "Albeu",
+  "",
+  open_s,
+  { "mpst",NULL },
+  &stream_opts,
+  1 // Url is an option string
+};
+
+#endif