Index: stream/Makefile =================================================================== --- stream/Makefile (révision 22344) +++ stream/Makefile (copie de travail) @@ -21,6 +21,7 @@ SRCS-$(MPLAYER_NETWORK) += stream_netstream.c \ asf_mmst_streaming.c \ asf_streaming.c \ + zattoo.c \ cookies.c \ http.c \ network.c \ Index: stream/zattoo.c =================================================================== --- stream/zattoo.c (révision 0) +++ stream/zattoo.c (révision 0) @@ -0,0 +1,1419 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "mp_msg.h" +#include "help_mp.h" + +#ifndef HAVE_WINSOCK2 +#define closesocket close +#else +#include +#endif + +#include "url.h" +#include "http.h" +#include "libmpdemux/asf.h" + +#include "stream.h" +#include "libmpdemux/demuxer.h" + +#include "network.h" +#include "tcp.h" +#define me2be_64(x) be2me_64(x) +#define me2be_32(x) be2me_32(x) +#define me2be_16(x) be2me_16(x) +#define QUEUE_LEN 30 +#define PREBUFFER_SIZE 131072 + +extern int network_bandwidth; + +#define MINPOINTS 11 +#define FNUMBERL3_MARGE 0x400 +#include +#define min(a,b) (((a)<(b))?(a):(b)) + +/*FIXME: replace printf by internal error messages*/ +/*TODO: write matroska reference number*/ + +// ignored messages: login session, init0 and resp, init2 resp, POST except key update, side msgs to addr_server + +uint8_t addr_server[4]={81,221,34,6}; +uint8_t outmagick[2]={0x13,0x8a}; + +enum _msg_type_t + { + MSG_TYPE_VIDEO, + MSG_TYPE_AUDIO, + MSG_TYPE_META1,//ignored + MSG_TYPE_META2,//ignored + MSG_TYPE_STREAMHEADER, + MSG_TYPE_KEYUPDATE, + MSG_TYPE_IGNORED1,//ignored + MSG_TYPE_IGNORED2,//ignored + MSG_TYPE_INIT1, + MSG_TYPE_INIT2, + MSG_TYPE_ADDRREQ1, + MSG_TYPE_ADDRREQ2, + MSG_TYPE_ADDRRESP1, + MSG_TYPE_ADDRRESP2, + MSG_TYPE_LOGINREQ1, + MSG_TYPE_LOGINRESP1, + NUM_MSG_TYPE + } ; +typedef enum _msg_type_t msg_type_t; + +uint8_t msg_magicks[NUM_MSG_TYPE][12]= + { + [MSG_TYPE_VIDEO] + ={0x03, 0x05, 0x01, 0x0f, 0x0a, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_AUDIO] + ={0x03, 0x05, 0x01, 0x11, 0x0a, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_META1] + ={0x03, 0x05, 0x01, 0x10, 0x0a, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_META2] + ={0x03, 0x05, 0x01, 0x12, 0x0a, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_STREAMHEADER] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x82, 0x14, 0x0a, 0x00, 0x13, 0x8b}, + [MSG_TYPE_KEYUPDATE] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x2d, 0x14, 0x0a, 0x00, 0x13, 0x8b}, + [MSG_TYPE_IGNORED1]//in stream + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x19, 0x14, 0x0a, 0x00, 0x13, 0x8b}, + [MSG_TYPE_IGNORED2] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x87, 0x14, 0x0a, 0x00, 0x13, 0x8b}, + [MSG_TYPE_INIT1] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x02, 0x73, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_INIT2] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x07, 0x73, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_ADDRREQ1] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x01, 0x73, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_ADDRREQ2] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x21, 0x73, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_ADDRRESP1] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x81, 0x00, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_ADDRRESP2] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0xa1, 0x00, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_LOGINREQ1] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x20, 0x73, 0x0a, 0x00, 0x00, 0x00}, + [MSG_TYPE_LOGINRESP1] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0xa0, 0x00, 0x0a, 0x00, 0x00, 0x00}, + /* [MSG_TYPE_LOGINREQ2] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0x21, 0x73, 0x0a, 0x00, 0x00, 0x00},*/ + /* [MSG_TYPE_LOGINRESP2] + ={0x03, 0x04, 0x00, 0x04, 0x0a, 0x00, 0xa1, 0x00, 0x0a, 0x00, 0x00, 0x00},*/ + + + }; +#define MSG_TYPE_LOGINREQ2 MSG_TYPE_ADDRREQ2 +#define MSG_TYPE_LOGINRESP2 MSG_TYPE_ADDRRESP2 + + +unsigned char h264init[]={0x00,0x00,0x00,0x01,0x67,0x4d,0x40,0x33, + 0x92,0x54,0x0b,0x04,0xb4,0x20,0x00,0x00, + 0x03,0x00,0x40,0x00,0x00,0x0c,0xd1,0xe3, + 0x06,0x54,0x00,0x00,0x00,0x01,0x68,0xee, + 0x3c,0x80}; +char audioprivate[2]={0x11,0x90}; + +char login_req1_magick2[49]={ + 0x02, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x2c,0xd3,0xe7,0x79, + 0x10,0x20,0xcb,0x01, + 0x77,0x00,0x00,0x02, + 0xa0,0xd8,0x12,0x00, + 0xd8,0x89,0x3f,0x02, + 0x2b,0xb7,0xe7,0x79, + 0xd8,0x89,0x3f,0x02, + 0x03,0xb7,0xe7,0x79, + 0xd8,0x89,0x3f,0x02, + 0xf1,0xb6,0xe7,0x79 +}; + +uint8_t init1_magick2[16]={0x00,0x0f,0x05,0x37, + 0x00,0x00,0x73,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00}; +uint8_t init1_magick3[5]={0xff,0xff,0x39,0x36,0x00}; +uint8_t init2_magick2[16]={0x00,0x00,0x7a,0x12, + 0x00,0x00,0x73,0x00, + 0x00,0x00,0xf4,0x24, + 0x00,0x00,0x00,0x00}; +char addr2_magick2[8]={ + 0x00,0x00,0x73,0x00, + 0x00,0x00,0x00,0x02 +}; + +#define EBML_VERSION_ID 0x286 +#define EBML_DOCTYPE_ID 0x282 +#define BLOCK_TYPE_ID 0x21 +#define BLOCKGROUP_TYPE_ID 0x20 +#define TIMEPOS_TYPE_ID 0x67 +#define CLUSTER_TYPE_ID 0xf43b675 +#define EBML_DOCTYPE_READ_VERSION_ID 0x285 +#define EBML_HEAD_TYPE_ID 0xa45dfa3 +#define TIMESCALE_TYPE_ID 0xad7b1 +#define SEGINFO_TYPE_ID 0x549a966 +#define TRACKNUMBER_TYPE_ID 0x57 +#define TRACKUID_TYPE_ID 0x33c5 +#define TRACKTYPE_TYPE_ID 0x3 +#define TRACKENABLED_TYPE_ID 0x39 +#define TRACKDEFAULT_TYPE_ID 0x8 +#define TRACKFORCED_TYPE_ID 0x15aa +#define TRACKLACING_TYPE_ID 0x1c +#define TRACKMINCACHE_TYPE_ID 0x2de7 +#define TRACKMAXBLOCKADDITION_TYPE_ID 0x15ee +#define TRACKCODEC_TYPE_ID 0x6 +#define TRACK_TYPE_ID 0x2e +#define TRACKS_TYPE_ID 0x654ae6b +#define FLAG_INTERLACED_TYPE_ID 0x1a +#define PIXELWIDTH_TYPE_ID 0x30 +#define PIXELHEIGHT_TYPE_ID 0x3a +#define VIDEO_TYPE_ID 0x60 +#define AUDIO_TYPE_ID 0x61 +#define SAMPLEFREQ_TYPE_ID 0x35 +#define CHANNELS_TYPE_ID 0x1f +#define TRACKCODECPRIVATE_TYPE_ID 0x23a2 +#define TRACKDECODEALL_TYPE_ID 0x2a + + +struct l1_header +{ + uint8_t magick[12]; + uint32_t channel1; + uint8_t srcip_external[4]; + uint8_t srcip_internal[4]; + uint8_t destip[4]; + uint32_t fnumber; + uint32_t user_id; + uint8_t magick2[2]; + uint16_t size; +}__attribute__ ((packed)); + + +struct l2_header +{ + uint8_t kselector; + uint8_t iv[16]; +} __attribute__ ((packed)); + +struct l3_header +{ + /*TORES*/ + uint16_t magick1; //8060/80e0 ??? + uint16_t fnumber; /*be*/ + uint32_t timepos; /* !! different units !!*/ + uint32_t magick2;// some stream id???? + /*TORES*/ + uint8_t flags1, flags2; +} __attribute__ ((packed)); + +struct key_update_msg +{ + uint8_t iv[0x10]; + uint8_t enc[0x30]; +} __attribute__ ((packed)); + + +struct login_ticket +{ + uint32_t sth1; + uint32_t user_id; + uint8_t modulus[128]; + uint8_t privexp[3]; + uint8_t sth2[2]; + uint8_t sth3[22]; + uint32_t user_id2; + uint8_t sth4[11]; + uint8_t sth5[256]; +} __attribute__ ((packed)); +struct addr_req2 +{ + //40 + uint32_t num2; + uint8_t reserved2[8]; + uint8_t magick2[8]; + uint32_t sth4; + uint8_t sig[128]; +} __attribute__ ((packed)); +struct addr_resp2 +{ + //76=4c + uint8_t sth1[36]; + uint8_t ipanswer[4]; + uint32_t channel1; + uint8_t sth[16]; +} __attribute__ ((packed)); + + +//variable size +/*struct login_req1 +{ +} __attribute__ ((packed)); + +//40 +asciiz name +uint8_t sth[50] //??nearly always x48, magick2 +//uint8_t magick2[49] +modulus +010001(pexp) +0003(sth) +*/ +struct login_req2 +{ + //40 + uint8_t sig[128]; +} __attribute__ ((packed)); + +/*struct login_resp1 +{ + uint8_t msg_magick1[6]; + uint8_t msg_type;//tocheck + uint8_t sth1[18]; + uint32_t user_id; + uint8_t sth2[52]; +} __attribute__ ((packed));*/ + +struct login_resp1 +{ + uint32_t sth1; + uint32_t user_id; + uint8_t sth2[32]; +} __attribute__ ((packed)); + +struct login_resp2 +{ + struct login_ticket lticket;//434b +} __attribute__ ((packed)); + +struct init1 +{ + //40 + uint32_t msg_num2;//nimp + uint32_t msg_num3;//nimp + //48 + uint8_t magick2[16];//fail, nimp + //64 + uint8_t sth4[4];//nimp + uint8_t magick3[5];//ok + struct login_ticket lticket; +} __attribute__ ((packed)); + +struct init2 +{ + //40 + uint32_t msg_num2;//nimp + uint32_t msg_num3;//nimp + uint8_t magick2[16];//ok +} __attribute__ ((packed)); + +struct stream_header +{ + uint8_t sth1[0xa0]; + uint8_t keys_enc[0x80]; +} __attribute__ ((packed)); + +struct key_sent +{ + uint8_t key_id; + uint8_t sth1[2]; + uint8_t key[16]; +} __attribute__ ((packed)); + +struct header_keys_dec +{ + uint8_t seskey[16]; + struct key_sent keys[2]; +} __attribute__ ((packed)); + + +struct frame +{ + struct l3_header head; + uint8_t *data; + int len; +}; + +struct ztv_connect +{ + uint8_t ip[4]; + uint32_t channel1; + uint8_t buffer[PREBUFFER_SIZE]; + int buf_pos; + int buf_len; + int fd; + char ipname[40]; +}; + +struct ztv_info +{ + RSA *myrsa; + uint8_t keys[256][16]; + int hasakey[256]; + uint8_t seskey[16]; + int matroska_sent, h264sent; + int naudpackets, nvidpackets; + int framesdisp; + uint32_t audiostart, videostart; + struct frame video_queue[QUEUE_LEN+1], audio_queue[QUEUE_LEN+1]; + int video_queue_current_size; + int audio_queue_current_size; + char *tbuf; + char *tptr; + int tlen; + int video_inited; + char videobuf[409600]; + int videobuf_pos; + int videobuf_time; + char audiobuf[409600]; + int audiobuf_pos; + int audiobuf_time; + struct login_ticket lticket; + struct ztv_connect maincon; + uint32_t sendnum; + uint32_t user_id; +}; + +struct key_update_msg_dec +{ + uint8_t sth1[24]; + struct key_sent key; +} __attribute__ ((packed)); + + +#define DATA(x) ((struct ztv_info *)x->priv) + +static int +move_buffer(struct ztv_connect *connect) +{ + if(connect->buf_pos!=0) + memmove(connect->buffer, connect->buffer+connect->buf_pos, + connect->buf_len); + connect->buf_pos=0; + return 0; +} + + +static int +cmp_fnumberl3(struct l3_header a, struct l3_header b) +{ + /* Fix the overflow*/ + if (be2me_16(a.fnumber)(uint16_t)~FNUMBERL3_MARGE) + return +1; + if (be2me_16(b.fnumber)(uint16_t)~FNUMBERL3_MARGE) + return -1; + if (be2me_16(a.fnumber)be2me_16(b.fnumber)) + return +1; + return 0; +} + +static void +push_frame(struct frame *frame_queue, + int *frame_queue_current_size, + unsigned char *buf, + int len) +{ + int i,j; + struct frame cframe; + + if (len<=sizeof(cframe.head)) + { + /*error message*/ + return; + } + memcpy(&cframe.head,buf,sizeof(cframe.head)); + cframe.data=(unsigned char *)malloc(len-sizeof(cframe.head)); + memcpy(cframe.data,buf+sizeof(cframe.head),len-sizeof(cframe.head)); + cframe.len=len-sizeof(cframe.head); + + for(i=0;i<*frame_queue_current_size;i++) + if(cmp_fnumberl3(frame_queue[i].head,cframe.head)>0) + break; + for(j=*frame_queue_current_size-1;j>=i;j--) + frame_queue[j+1]=frame_queue[j]; + frame_queue[i]=cframe; + + (*frame_queue_current_size)++; +} + +struct frame +pop_frame(struct frame *frame_queue, + int *frame_queue_current_size) +{ + struct frame cframe; + int i; + cframe=frame_queue[0]; + for(i=1;i<*frame_queue_current_size;i++) + frame_queue[i-1]=frame_queue[i]; + (*frame_queue_current_size)--; + return cframe; +} + +void +free_queue(struct frame *frame_queue, + int *frame_queue_current_size) +{ + int i; + return; + for(i=0;i<*frame_queue_current_size;i++) + free (frame_queue[i].data); + *frame_queue_current_size=0; +} + + +static int +ztv_http_down(stream_t *stream, struct ztv_connect *connect, int minsize) +{ + int was_read=0, was_read_total=0; + move_buffer(connect); + if (minsize && connect->buf_len>=minsize) + return 0; + // do + while( connect->buf_lenfd, connect->buffer+connect->buf_pos + +connect->buf_len, + // minsize-connect->buf_len + PREBUFFER_SIZE-connect->buf_pos-connect->buf_len,0); + if (was_read<0) + { + printf("returned %d\n",was_read); + return -1; + } + connect->buf_len+=was_read; + was_read_total+=was_read; + } + //while(was_read || connect->buf_lenbuffer+connect->buf_pos,pattern,psize)) + { + connect->buf_pos++; + connect->buf_len--; + ret=ztv_http_down(stream, connect, psize); + if(ret<0) + return ret; + } + connect->buf_pos+=psize; + connect->buf_len-=psize; + + return 0; +} + +static msg_type_t +get_msg_type(struct l1_header *head) +{ + int i; + for(i=0;imagick,msg_magicks[i], sizeof(head->magick))) + break; + return i; +} + +static int +ztv_http_parse_stream_packet(stream_t *stream, struct ztv_connect *connect, + uint8_t *packet, int packet_size, + msg_type_t msg_type) +{ + struct l2_header headl2; + EVP_CIPHER_CTX ctx; + char obuf[50000]; + int olen=0; + int t=0, i; + + memset(&ctx,0, sizeof(ctx)); + if(packet_size<=sizeof(struct l2_header)) + { + printf("dwarf of size %d dropped\n", packet_size); + return -1; + } + memcpy(&headl2, packet, sizeof(struct l2_header)); + if(!DATA(stream)->hasakey[headl2.kselector]) + { + printf("No key:%02x\n",headl2.kselector); + return -1; + } + EVP_DecryptInit(&ctx, EVP_aes_128_cbc(), + DATA(stream)->keys[headl2.kselector], + headl2.iv); + EVP_DecryptUpdate (&ctx, obuf, &olen, packet+sizeof(struct l2_header), + packet_size-sizeof(struct l2_header)); + EVP_DecryptFinal(&ctx,obuf+olen,&t); + olen+=t; + EVP_CIPHER_CTX_cleanup(&ctx); + + /* Push it to appripriate queue*/ + switch(msg_type) + { + case MSG_TYPE_AUDIO: + push_frame(DATA(stream)->audio_queue, + &DATA(stream)->audio_queue_current_size, obuf,olen); + if(DATA(stream)->naudpackets==0) + DATA(stream)->audiobuf_time=DATA(stream)->audiostart=be2me_32(((struct l3_header *)obuf)->timepos); + DATA(stream)->naudpackets++; + break; + case MSG_TYPE_VIDEO: + push_frame(DATA(stream)->video_queue, + &DATA(stream)->video_queue_current_size, obuf,olen); + if(DATA(stream)->nvidpackets==0) + DATA(stream)->videobuf_time=DATA(stream)->videostart=be2me_32(((struct l3_header *)obuf)->timepos); + DATA(stream)->nvidpackets++; + } + return msg_type; + +} + +static int +ztv_http_parse_keyupdate(stream_t *stream, struct ztv_connect *connect, + uint8_t *packet, int packet_size, + msg_type_t msg_type) +{ + char *end, sav; + int len; + struct key_update_msg *kupd; + struct key_update_msg_dec *kupd_dec; + EVP_CIPHER_CTX ctx; + unsigned char obuf[4096]; + int olen,t, i; + memset(&ctx,0, sizeof(ctx)); + + if(packet_size!=sizeof(struct key_update_msg)) + return -1; + kupd=(struct key_update_msg *) packet; + + EVP_DecryptInit(&ctx, EVP_aes_128_cbc(), + DATA(stream)->seskey, + kupd->iv); + EVP_DecryptUpdate (&ctx, obuf, &olen, kupd->enc, + sizeof(kupd->enc)); + EVP_DecryptFinal(&ctx,obuf+olen,&t); + olen+=t; + EVP_CIPHER_CTX_cleanup(&ctx); + if(olen!=sizeof(struct key_update_msg_dec)) + return -1; + + kupd_dec=(struct key_update_msg_dec *)obuf; + memcpy(DATA(stream)->keys[kupd_dec->key.key_id], &(kupd_dec->key.key),16); + DATA(stream)->hasakey[kupd_dec->key.key_id]=1; + return 0; +} + +static int +ztv_http_parse_streamheader(stream_t *stream, struct ztv_connect *connect, + uint8_t *packet, int packet_size, + msg_type_t msg_type) +{ + + unsigned char buf2[1024]; + int outlen; + int i; + struct stream_header head; + struct header_keys_dec keys; + + if(packet_size!=sizeof(struct stream_header)) + return -1; + + memcpy(&head, packet, sizeof(head)); + outlen=DATA(stream)->myrsa->meth->rsa_priv_dec(sizeof(head.keys_enc), + head.keys_enc, + buf2,DATA(stream)->myrsa,4); + if(outlenseskey, keys.seskey, 16); + memcpy (DATA(stream)->keys[keys.keys[0].key_id], keys.keys[0].key, 16); + memcpy (DATA(stream)->keys[keys.keys[1].key_id], keys.keys[1].key, 16); + DATA(stream)->hasakey[keys.keys[0].key_id]=1; + DATA(stream)->hasakey[keys.keys[1].key_id]=1; + return 0; +} + + +static char * +ztv_http_fetch_packet(stream_t *stream, struct ztv_connect *connect, + msg_type_t *msg_type_out, int *packet_size) +{ + int report=1; + int i; + struct l1_header head; + msg_type_t msg_type; + uint8_t *packet; + *packet_size=0; + *msg_type_out=NUM_MSG_TYPE; + while(1) + { + if(ztv_http_down(stream, connect, sizeof(struct l1_header))<0) + return 0; + /* FIXME: Do we need to read the Content-Length?*/ + while (!memcmp("POST ", connect->buffer+connect->buf_pos, + sizeof("POST ")-1) + || !memcmp("HTTP/", connect->buffer+connect->buf_pos, + sizeof("HTTP/")-1)) + { + if(seek_to_pattern(stream, connect, "\xd\xa\xd\xa", 4)<0) + return 0; + if(ztv_http_down(stream, connect, sizeof(struct l1_header))<0) + return 0; + } + memcpy(&head,(struct l1_header *)(connect->buffer+connect->buf_pos), + sizeof(head)); + msg_type=get_msg_type(&head); + if (msg_type==NUM_MSG_TYPE) + { + if(report) + { + printf("Unknown message "); + for(i=0;ibuf_pos++; + connect->buf_len--; + continue; + } + + connect->buf_pos+=sizeof(struct l1_header); + connect->buf_len-=sizeof(struct l1_header); + /* FIXME: support for frames>2048b*/ + if(be2me_16(head.size)>PREBUFFER_SIZE-connect->buf_pos) + { + printf("giant of size %d dropped\n",be2me_16(head.size)); + *packet_size=0; + *msg_type_out=NUM_MSG_TYPE; + return 0; + } + ztv_http_down(stream,connect,be2me_16(head.size)); + packet=(uint8_t *)malloc(be2me_16(head.size)); + memcpy(packet, connect->buffer+connect->buf_pos, + be2me_16(head.size)); + *packet_size=be2me_16(head.size); + *msg_type_out=msg_type; + connect->buf_pos+=be2me_16(head.size); + connect->buf_len-=be2me_16(head.size); + return packet; + } +} + + +static int +ztv_http_send_packet(stream_t *stream, struct ztv_connect *connect, + uint8_t *packet, int packet_size, msg_type_t msg_type) +{ + struct l1_header head; + char *buf; + char ipnamepad[20]; + int len; + int i; + buf=malloc(sizeof(struct l1_header)+packet_size+200); + for(i=0;i+strlen(connect->ipname)<15;i++) + ipnamepad[i]=' '; + ipnamepad[i]=0; + sprintf(buf, "POST http://%s%s HTTP/1.1\r\nHost: %s%s\r\nContent-Length: %d\r\n\r\n", + connect->ipname, ipnamepad, ipnamepad, connect->ipname, + sizeof(struct l1_header)+packet_size); + len=strlen(buf); + memset(&head,0,sizeof(head)); + memcpy(head.magick,msg_magicks[msg_type],sizeof(head.magick)); + head.channel1=connect->channel1; + head.user_id=DATA(stream)->user_id; + memcpy(head.destip,connect->ip,sizeof(head.destip)); + head.fnumber=me2be_32(DATA(stream)->sendnum++); + head.size=me2be_16(packet_size); + /* FIXME: put the src_ip*/ + memcpy(head.srcip_internal,"\xc0\xa8\x01\x64",sizeof(head.srcip_internal)); + memcpy(head.magick2,outmagick,sizeof(head.magick2)); + + memcpy(buf+len, &head, sizeof(head)); + len+=sizeof(head); + memcpy(buf+len, packet, packet_size); + len+=packet_size; + send (connect->fd, buf, len, 0); + + return 0; +} + + + +static int +load_rsa(stream_t *stream, char *fname) +{ + BIO *fbio=0; + EVP_PKEY *pkey; + fbio=BIO_new(BIO_s_file()); + if (BIO_read_filename(fbio,fname)<=0) + { + printf("Error reading %s\n", fname); + } + pkey=PEM_read_bio_PrivateKey(fbio,0,0,0); + if (pkey) + { + DATA(stream)->myrsa=EVP_PKEY_get1_RSA(pkey); + BIO_free_all(fbio); + return 1; + } + BIO_free_all(fbio); + printf("Error loading RSA key \n"); + return 0; +} + +static int +load_init(stream_t *stream, char *fname) +{ + FILE *f; + struct init1 init; + f=fopen(fname, "rb"); + if(!f) + return 0; + fseek(f,40,SEEK_SET); + if(fread(&init,1,sizeof(init),f)!=sizeof(init)) + { + fclose(f); + return 0; + } + memcpy (&DATA(stream)->lticket, &init.lticket, sizeof(init.lticket)); + DATA(stream)->user_id=init.lticket.user_id; + fclose(f); + return 1; +} + + +struct init2 +generate_init2(stream_t *stream) +{ + struct init2 ret; + memset(&ret,0,sizeof(ret)); + memcpy(ret.magick2,init2_magick2, + sizeof(ret.magick2)); + return ret; +} + +static int +ztv_http_connect_open(stream_t *stream, struct ztv_connect *connect) +{ + sprintf(connect->ipname,"%d.%d.%d.%d", + connect->ip[0],connect->ip[1], + connect->ip[2],connect->ip[3]); + connect->fd=connect2Server(connect->ipname,80,0); + connect->buf_pos=connect->buf_len=0; + if( connect->fd<0 ) + return -1; + return 0; + +} + +static int +ztv_http_connect_close(stream_t *stream, struct ztv_connect *connect) +{ + closesocket(connect->fd); + return 0; +} + + +static int +get_addr(stream_t *stream, char *channel) +{ + char buf[8192]; + struct addr_req2 ar2; + struct addr_resp2 r2; + int outlen; + struct ztv_connect addrcon; + msg_type_t msg_type; + int size; + + char *packet; + + memset(&addrcon,0,sizeof(addrcon)); + memcpy(addrcon.ip,addr_server,sizeof(addrcon.ip)); + + memcpy(buf, &(DATA(stream)->lticket), sizeof(DATA(stream)->lticket)); + strncpy(buf+sizeof(DATA(stream)->lticket), channel, 200); + ztv_http_connect_open(stream, &addrcon); + + ztv_http_send_packet(stream, &addrcon, + buf, sizeof(DATA(stream)->lticket)+strlen(channel), + MSG_TYPE_ADDRREQ1); + packet=ztv_http_fetch_packet(stream, &addrcon, &msg_type, &size); + + if(msg_type!=MSG_TYPE_ADDRRESP1) + { + free(packet); + ztv_http_connect_close(stream, &addrcon); + return -1; + } + + if(size!=16) + { + free(packet); + ztv_http_connect_close(stream, &addrcon); + return -1; + } + outlen=DATA(stream)->myrsa->meth->rsa_priv_enc(16, + packet, + &ar2.sig,DATA(stream)->myrsa,1); + free(packet); + if(outlen<128) + return -1; + memcpy(buf,&ar2,sizeof(ar2)); + strcpy(buf+sizeof(ar2),channel); + ztv_http_send_packet(stream, &addrcon, + buf, sizeof(ar2)+strlen(channel), + MSG_TYPE_ADDRREQ2); + packet=ztv_http_fetch_packet(stream, &addrcon, &msg_type, &size); + if(msg_type!=MSG_TYPE_ADDRRESP2) + { + free(packet); + ztv_http_connect_close(stream, &addrcon); + return -1; + } + + if(size!=sizeof(r2)) + { + free(packet); + ztv_http_connect_close(stream, &addrcon); + return -1; + } + memcpy(&r2,packet,sizeof(r2)); + free(packet); + memcpy(DATA(stream)->maincon.ip,r2.ipanswer,sizeof(r2.ipanswer)); + DATA(stream)->maincon.channel1=r2.channel1; + + printf("ip:%d.%d.%d.%d id:%x\n",r2.ipanswer[0],r2.ipanswer[1],r2.ipanswer[2],r2.ipanswer[3],r2.channel1); + ztv_http_connect_close(stream, &addrcon); + return 0; + + return 0; +} + + + + + +static msg_type_t +ztv_http_dispatch_packet(stream_t *stream, struct ztv_connect *connect) +{ + char *packet; + msg_type_t msg_type; + int size; + packet=ztv_http_fetch_packet(stream, connect, &msg_type, &size); + switch(msg_type) + { + case MSG_TYPE_VIDEO: + case MSG_TYPE_AUDIO: + ztv_http_parse_stream_packet(stream, connect, packet, + size, msg_type); + break; + case MSG_TYPE_KEYUPDATE: + ztv_http_parse_keyupdate(stream, connect, packet, + size, msg_type); + break; + case MSG_TYPE_STREAMHEADER: + ztv_http_parse_streamheader(stream, connect, packet, + size, msg_type); + break; + } + return msg_type; +} + + +static int +send_init(stream_t *stream, struct ztv_connect *connect) +{ + char buf[2048]; + int len; + struct init1 init; + memset(&init,0,sizeof(init)); + memcpy(init.magick2,init1_magick2, + sizeof(init.magick2)); + memcpy(init.magick3,init1_magick3, + sizeof(init.magick3)); + memcpy(&init.lticket,&DATA(stream)->lticket, + sizeof(init.lticket)); + /* FIXME: error check*/ + ztv_http_send_packet(stream, connect, + &init, sizeof(init), MSG_TYPE_INIT1); + return 0; +} + + +static int +ztv_feed_from_tbuf(stream_t *stream, char* buffer, int max_len) +{ + int w; + if(DATA(stream)->tptr) + { + w=min(DATA(stream)->tlen,max_len); + memcpy(buffer, DATA(stream)->tptr,w); + + DATA(stream)->tptr+=w; + DATA(stream)->tlen-=w; + if (DATA(stream)->tlen<=0) + { + free(DATA(stream)->tbuf); + DATA(stream)->tlen=0; + DATA(stream)->tbuf=DATA(stream)->tptr=0; + } + return w; + } + return 0; +} + +static int +write_matroska_number(char *buf, uint64_t num) +{ + int i,j; + for(j=1;j<7;j++) + if(num<(((uint64_t)1)<<(j*7))-1) + break; + *buf=(num>>((j-1)*8)) + (1<<(8-j)); + for(i=1;i>((j-1-i)*8))&0xff; + return j; +} + +static int +write_matroska_signed_number(char *buf, long long num) +{ + int i,j; + long long abs; + long long offs; + abs=num; + if(abs<0) + abs=-abs; + for(j=1;j<7;j++) + if(abs<=((((uint64_t)1)<<(j*7-1))-1)) + break; + offs=num+((((uint64_t)1)<<(j*7-1))-1); + *buf=(offs>>((j-1)*8)) + (1<<(8-j)); + for(i=1;i>((j-1-i)*8))&0xff; + return j; + return 8; +} + + + + +static int +write_matroska_field(char *out, int id, char *in, int inlen, int delay) +{ + int pos=0; + pos+=write_matroska_number(pos+out,id); + pos+=write_matroska_number(pos+out,inlen); + memcpy(pos+out, in,inlen-delay); + return pos+inlen-delay; +} + +int +write_matroska_uint(char *out, int id, uint64_t in) +{ + char buf[10]; + int i, j; + for(i=0;i<8;i++) + if(in<(((uint64_t)1)<<(i*8))) + break; + if(i==0) + i++; + for(j=0;j>(8*(i-j-1)))&0xff; + return write_matroska_field(out,id,buf,i,0); +} + +//FIXME: check if we are already on be-machine +int +write_matroska_float(char *out, int id, double in) +{ + double tmp; + char buf1[sizeof(tmp)]; + char buf2[sizeof(tmp)]; + int i; + tmp=in; + memcpy(buf1,&tmp,sizeof(tmp)); + for(i=0;itbuf=DATA(stream)->tptr + =cluster; + DATA(stream)->tlen=clussize; + + return DATA(stream)->tbuf+clussize; +} + +static void +ztv_matroska_write_audio(stream_t *stream,uint8_t *buf, uint64_t timepos, int len, + int numoflaces) +{ + uint8_t *blockcnt, *blockptr; + char *block; + int blocksize; + char *clustercnt; + int ccntsize; + char *cluster; + int clussize; + int i; + uint16_t *lacearr; + + blockcnt=malloc(len+14); + blockcnt[0]=0x82; + memcpy(blockcnt+1, "\0\0",2); + blockcnt[3]=6; + /*generate EBML lacing*/ + blockptr=blockcnt+4; + *blockptr=numoflaces-1; + blockptr++; + lacearr=((uint16_t *)buf); + blockptr+=write_matroska_number(blockptr,be2me_16(lacearr[0])/8); + for(i=1;itbuf=DATA(stream)->tptr=cluster; + DATA(stream)->tlen=clussize; + + + + return DATA(stream)->tbuf+clussize; +} + + +static void +send_matroska(stream_t *stream) +{ + char *buf; + int size; + buf=malloc(4096); + DATA(stream)->matroska_sent=1; + size=generate_matroska_header(buf); + DATA(stream)->tbuf=DATA(stream)->tptr=buf; + DATA(stream)->tlen=size; +} + + +static int +ztv_http_fill_buffer(stream_t *stream, char* buffer, int max_len) +{ + /* FIXME: check maxlen*/ + int t; + msg_type_t lbt=NUM_MSG_TYPE; + struct frame curf; + int w; + int packet_ready=0; + if(!DATA(stream)->matroska_sent) + { + send_matroska(stream); + return ztv_feed_from_tbuf(stream, buffer, max_len); + } + if ((w=ztv_feed_from_tbuf(stream,buffer,max_len))) + return w; + while(!packet_ready) + { + while (DATA(stream)->video_queue_current_size+DATA(stream)->audio_queue_current_sizemaincon)); + if(t==NUM_MSG_TYPE) + return 0; + if(t==MSG_TYPE_VIDEO || t==MSG_TYPE_AUDIO) + lbt=t; + } + switch(lbt) + { + /*video*/ + case MSG_TYPE_VIDEO: + curf=pop_frame(DATA(stream)->video_queue, + &DATA(stream)->video_queue_current_size); + if (!DATA(stream)->video_inited + && !(curf.head.flags1==0x7c && curf.head.flags2==0x85)) + { + free(curf.data); + continue; + } + DATA(stream)->video_inited=1; + if(DATA(stream)->videobuf_pos + &&((curf.head.flags1==0x5c && curf.head.flags2==0x81) + ||(curf.head.flags1==0x7c && curf.head.flags2==0x85) + ||(curf.head.flags1==0x41 && curf.head.flags2==0x9a))) + { + /* FIXME: integer overflow*/ + ztv_matroska_write_video + (stream,DATA(stream)->videobuf, + (((uint64_t)(DATA(stream)->videobuf_time + -DATA(stream)->videostart))*100)/9, + DATA(stream)->videobuf_pos); + DATA(stream)->videobuf_time=be2me_32(curf.head.timepos); + DATA(stream)->videobuf_pos=0; + packet_ready=1; + } + if(!DATA(stream)->h264sent) + { + memcpy(DATA(stream)->videobuf+DATA(stream)->videobuf_pos, + h264init, sizeof(h264init)); + DATA(stream)->videobuf_pos+=sizeof(h264init); + DATA(stream)->h264sent=1; + } + /* FIXME: quite ugly*/ + if(curf.head.flags1==0x5c && curf.head.flags2==0x81) + { + memcpy(DATA(stream)->videobuf+DATA(stream)->videobuf_pos, + "\0\0\0\1\x41", 5); + DATA(stream)->videobuf_pos+=5; + } + // I-frame + if(curf.head.flags1==0x7c && curf.head.flags2==0x85) + { + memcpy(DATA(stream)->videobuf+DATA(stream)->videobuf_pos, + "\0\0\0\1\x65", 5); + DATA(stream)->videobuf_pos+=5; + } + if(curf.head.flags1==0x41 && curf.head.flags2==0x9a) + { + memcpy(DATA(stream)->videobuf+DATA(stream)->videobuf_pos, + "\0\0\0\1\x41\x9a", 6); + DATA(stream)->videobuf_pos+=6; + } + + memcpy(DATA(stream)->videobuf+DATA(stream)->videobuf_pos, + curf.data,curf.len); + DATA(stream)->videobuf_pos+=curf.len; + free(curf.data); + break; + + /*audio*/ + case MSG_TYPE_AUDIO: + curf=pop_frame(DATA(stream)->audio_queue, &DATA(stream)->audio_queue_current_size); + if (!DATA(stream)->video_inited ) + { + free(curf.data); + continue; + } + /* FIXME: integer overflow*/ + ztv_matroska_write_audio + (stream,curf.data,(((uint64_t)(be2me_32(curf.head.timepos) + -DATA(stream)->audiostart))*125)/6, + curf.len, curf.head.flags2>>4); + free(curf.data); + packet_ready=1; + break; + } + } + return ztv_feed_from_tbuf(stream, buffer, max_len); +} + +static void +ztv_http_close(struct stream_st *stream) +{ + closesocket (DATA(stream)->maincon.fd); + free_queue(DATA(stream)->video_queue, + &DATA(stream)->video_queue_current_size); + free_queue(DATA(stream)->audio_queue, + &DATA(stream)->audio_queue_current_size); + DATA(stream)->myrsa->meth->finish(DATA(stream)->myrsa); +} + + +/* FIXME: for now we use dumped files*/ +int +ztv_http_open (stream_t *stream) +{ + char *purl=stream->url+sizeof("ztvhttp://:")-1, *virg, *virg2; + struct init2 init2i; + + stream->priv=malloc(sizeof(struct ztv_info)); + memset(stream->priv,0, sizeof(struct ztv_info)); + + if(!(virg=strchr(purl,':'))) + return STREAM_UNSUPORTED; + *virg=0; + if(!(virg2=strchr(virg+1,':'))) + return STREAM_UNSUPORTED; + *virg2=0; + + if(!load_rsa(stream, virg+1)) + return STREAM_UNSUPORTED; + + /* FIXME: urgent*/ + DATA(stream)->sendnum=0x3be; + + if(!load_init(stream,purl)) + return STREAM_UNSUPORTED; + get_addr(stream, virg2+1); + ztv_http_connect_open(stream, &(DATA(stream)->maincon)); + + send_init(stream, &(DATA(stream)->maincon)); + // ztv_http_down(stream,&(DATA(stream)->maincon),PREBUFFER_SIZE); + init2i=generate_init2(stream); + ztv_http_send_packet(stream, &(DATA(stream)->maincon), + &init2i, sizeof(init2i), MSG_TYPE_INIT2); + + stream->fill_buffer=ztv_http_fill_buffer; + stream->close=ztv_http_close; + move_buffer(&(DATA(stream)->maincon)); + return STREAM_OK; +} + +static int open_s(stream_t *stream,int mode, void* opts, int* file_format) { + + /* FIXME: proxy*/ + /* FIXME: opts and mode are ignored*/ + + *file_format=DEMUXER_TYPE_MATROSKA; + if (!strncmp(stream->url,"ztvhttp://:",sizeof("ztvhttp://:")-1)) + return ztv_http_open(stream); + + return STREAM_UNSUPORTED; +} + +stream_info_t stream_info_ztv = { + "zattoo stream", + "null", + "phcoder", + "Reverse engeneered", + open_s, + {"ztvhttp", NULL}, + NULL, + 0 // Urls are an option string +}; + Index: stream/stream.c =================================================================== --- stream/stream.c (révision 22344) +++ stream/stream.c (copie de travail) @@ -48,6 +48,7 @@ extern stream_info_t stream_info_rtsp; extern stream_info_t stream_info_rtp; extern stream_info_t stream_info_udp; +extern stream_info_t stream_info_ztv; extern stream_info_t stream_info_http1; extern stream_info_t stream_info_http2; #endif @@ -101,6 +102,7 @@ &stream_info_asf, &stream_info_pnm, &stream_info_rtsp, + &stream_info_ztv, #ifdef STREAMING_LIVE555 &stream_info_sdp, &stream_info_rtsp_sip,