#include #include #include //Header file for sleep(). man 3 sleep for details. #include #include #include #include // A normal C function that is executed as a thread // when its name is specified in pthread_create() // char *moc_socket_path = "/home/calvin/.moc/socket2"; /* State of the server. */ #define STATE_PLAY 0x01 #define STATE_STOP 0x02 #define STATE_PAUSE 0x03 #define CMD_GET_STATE 0x13 /* get the state */ #define CMD_GET_CTIME 0x0d /* get the current song time */ #define CMD_GET_TAGS 0x2c /* get tags for the currently played file */ #define CMD_GET_FILE_TAGS 0x2f /* get tags for the specified file */ #define CMD_GET_SNAME 0x0f /* get the stream file name */ #define MAX_SEND_STRING 4096 #define RANGE(min, val, max) ((val) >= (min) && (val) <= (max)) /* Definition of events sent by server to the client. */ #define EV_STATE 0x01 /* server has changed the state */ #define EV_CTIME 0x02 /* current time of the song has changed */ #define EV_SRV_ERROR 0x04 /* an error occurred */ #define EV_BUSY 0x05 /* another client is connected to the server */ #define EV_DATA 0x06 /* data in response to a request will arrive */ #define EV_BITRATE 0x07 /* the bitrate has changed */ #define EV_RATE 0x08 /* the rate has changed */ #define EV_CHANNELS 0x09 /* the number of channels has changed */ #define EV_EXIT 0x0a /* the server is about to exit */ #define EV_PONG 0x0b /* response for CMD_PING */ #define EV_OPTIONS 0x0c /* the options has changed */ #define EV_SEND_PLIST 0x0d /* request for sending the playlist */ #define EV_TAGS 0x0e /* tags for the current file have changed */ #define EV_STATUS_MSG 0x0f /* followed by a status message */ #define EV_MIXER_CHANGE 0x10 /* the mixer channel was changed */ #define EV_FILE_TAGS 0x11 /* tags in a response for tags request */ #define EV_AVG_BITRATE 0x12 /* average bitrate has changed (new song) */ #define EV_AUDIO_START 0x13 /* playing of audio has started */ #define EV_AUDIO_STOP 0x14 /* playing of audio has stopped */ /* Events caused by a client that wants to modify the playlist (see * CMD_CLI_PLIST* commands). */ #define EV_PLIST_ADD 0x50 /* add an item, followed by the file name */ #define EV_PLIST_DEL 0x51 /* delete an item, followed by the file name */ #define EV_PLIST_MOVE 0x52 /* move an item, followed by 2 file names */ #define EV_PLIST_CLEAR 0x53 /* clear the playlist */ /* These events, though similar to the four previous are caused by server * which takes care of clients' queue synchronization. */ #define EV_QUEUE_ADD 0x54 #define EV_QUEUE_DEL 0x55 #define EV_QUEUE_MOVE 0x56 #define EV_QUEUE_CLEAR 0x57 /* Flags for the info decoder function. */ enum tags_select { TAGS_COMMENTS = 0x01, /* artist, title, etc. */ TAGS_TIME = 0x02 /* time of the file. */ }; enum moc_state { STOPPED, PLAYING, PAUSED }; enum moc_status { INITIALIZING, CONNECTED, FAILED_TO_CONNECT, ERROR }; struct moc { pthread_mutex_t lock; enum moc_status status; enum moc_state state; char *filename; char *title; char *album; char *artist; int track; int time; int filled; }; extern const char * moc_str_state(enum moc_state s) { static const char *strings[] = { "STOPPED", "PLAYING", "PAUSED"}; return strings[s]; } static char *get_curr_file(int sock); static char *get_data_str(int sock); static void *get_event_data (const int sock, const int type); int send_str (int sock, const char *str); char *get_str (int sock); int send_int (int sock, int i); int get_int (int sock, int *i); void reset_data(struct moc *data) { data->filename = NULL; data->title = NULL; data->album = NULL; data->artist = NULL; data->time = -1; } static char *get_curr_file (const int sock) { send_int (sock, CMD_GET_SNAME); int ev; while(ev != EV_DATA) { get_int(sock, &ev); fprintf(stderr, "EVENT: 0x%02x\n", ev); } return get_str(sock); } static void wait_for_data (const int sock) { int event = -1; do { fprintf(stderr, "waiting for data last was %d\n", event); event = get_int(sock, &event); if (event == EV_EXIT) { fprintf(stderr, "The server exited!"); pthread_exit(NULL); } if(event == EV_STATE) { char *skip; skip = get_str(sock); fprintf(stderr, "got str%s\n", skip); } if (event != EV_DATA) get_event_data(sock, event); } while (event != EV_DATA); } static void *get_event_data (const int sock, const int type) { switch (type) { case EV_PLIST_ADD: case EV_QUEUE_ADD: break; // return recv_item_from_srv (); case EV_PLIST_DEL: case EV_QUEUE_DEL: case EV_STATUS_MSG: case EV_SRV_ERROR: return get_str(sock); case EV_FILE_TAGS: // return recv_tags_data_from_srv (); break; case EV_PLIST_MOVE: break; } return NULL; } static char *get_data_str(int sock) { wait_for_data (sock); return get_str(sock); } int send_str (int sock, const char *str) { int len; len = strlen (str); if (!send_int (sock, len)) return 0; if (send (sock, str, len, 0) != len) return 0; return 1; } char *get_str (int sock) { int len; int res, nread = 0; char *str; if (!get_int(sock, &len)) return NULL; if (!RANGE(0, len, MAX_SEND_STRING)) { printf("Bad string length."); return NULL; } str = (char *)malloc (sizeof(char) * (len + 1)); while (nread < len) { res = recv (sock, str + nread, len - nread, 0); if (res == -1) { printf("recv() failed when getting string: %s\n", strerror(errno)); free (str); return NULL; } if (res == 0) { printf("Unexpected EOF when getting string\n"); free (str); return NULL; } nread += res; } str[len] = 0; return str; } int send_int (int sock, int i) { int res; res = send (sock, &i, sizeof(int), 0); if (res == -1) printf("send() failed: %s", strerror(errno)); return res == sizeof(int) ? 1 : 0; } int get_int (int sock, int *i) { int res; res = recv (sock, i, sizeof(int), 0); if (res == -1) printf("recv() failed when getting int: %s", strerror(errno)); return res == sizeof(int) ? 1 : 0; } static int moc_server_connect () { char *path = moc_socket_path; struct sockaddr_un sock_name; int sock; /* Create a socket */ if ((sock = socket (PF_LOCAL, SOCK_STREAM, 0)) == -1) return -1; sock_name.sun_family = AF_LOCAL; strcpy (sock_name.sun_path, moc_socket_path); if (connect(sock, (struct sockaddr *)&sock_name, SUN_LEN(&sock_name)) == -1) { close (sock); fprintf(stderr, "%s at (%s)\n", strerror(errno), sock_name); return -1; } return sock; } void update_state(struct moc *data, int state) { fprintf(stderr, "moc: state %d, state\n"); if(state == STATE_PLAY) { data->state = PLAYING; fprintf(stderr, "moc: playing\n"); } if(state == STATE_PAUSE) { data->state = PAUSED; fprintf(stderr, "moc: paused\n"); } if(state == STATE_STOP) { data->state = STOPPED; fprintf(stderr, "moc: state stopped\n"); } } void *moc_loop(void *input) { struct moc *data = (struct moc*)input; int srv_sock = -1; srv_sock = moc_server_connect(); if(srv_sock == -1) { pthread_mutex_lock(&data->lock); data->status = FAILED_TO_CONNECT; pthread_mutex_unlock(&data->lock); pthread_exit(NULL); } data->status = CONNECTED; int last_cmd = -1; int event = -1; send_int(srv_sock, CMD_GET_STATE); last_cmd = EV_STATE; while(get_int(srv_sock, &event)) { switch (event) { // handle our data. we MUST know what the last request was otherwise we // don't know how to parse it. case EV_DATA: fprintf(stderr, "moc: EV_DATA\n"); if(last_cmd == EV_CTIME) { get_int(srv_sock, &data->time); fprintf(stderr, "moc: update ctime\n"); } // on state change, we need to figure out what file we have and what // tags it has. else if(last_cmd == EV_STATE) { int state; get_int(srv_sock, &state); update_state(data, state); if(state != STATE_STOP) { char *file = get_curr_file(srv_sock); fprintf(stderr, "moc: current file %s\n", file); // if it's the same file as we had, don't update. if(data->filename == NULL || strcmp(file, data->filename) != 0) { fprintf(stderr, "moc: new file, asking for tags\n"); data->filename = file; // get tags. send_int(srv_sock, CMD_GET_FILE_TAGS); send_str(srv_sock, file); send_int(srv_sock,TAGS_COMMENTS | TAGS_TIME); last_cmd = EV_FILE_TAGS; } } else { reset_data(data); } } else { fprintf(stderr, "moc: shit i have data IDK what it is\n"); } last_cmd = -1; break; case EV_BUSY: fprintf(stderr, "moc: EV_BUSY\n"); break; case EV_CTIME: fprintf(stderr, "moc: EV_CTIME\n"); last_cmd = EV_CTIME; send_int(srv_sock, CMD_GET_CTIME); break; case EV_STATE: fprintf(stderr, "moc: EV_STATE\n"); last_cmd = EV_STATE; send_int(srv_sock, CMD_GET_STATE); break; case EV_EXIT: fprintf(stderr,"moc: EV_EXIT\n"); break; case EV_BITRATE: fprintf(stderr, "moc: EV_BITRATE\n"); break; case EV_RATE: fprintf(stderr, "moc: EV_RATE\n"); break; case EV_CHANNELS: printf("moc: EV_CHANNELS\n"); break; case EV_SRV_ERROR: printf("moc: EV_SRV_ERROR\n"); break; case EV_OPTIONS: printf("moc: EV_OPTIONS\n"); break; case EV_SEND_PLIST: case EV_PLIST_ADD: case EV_PLIST_CLEAR: case EV_PLIST_DEL: case EV_PLIST_MOVE: printf("moc: EV_PLAYLIST\n"); case EV_TAGS: last_cmd = EV_TAGS; printf("moc: EV_TAGS\n"); break; case EV_STATUS_MSG: printf("moc: EV_STATUS_MSG\n"); printf("moc: status update: %s\n", get_str(srv_sock)); break; case EV_MIXER_CHANGE: printf("moc: EV_MIXER_CHANGE\n"); break; case EV_FILE_TAGS: printf("moc: EV_FILE_TAGS\n"); if (!(data->filename = get_str(srv_sock))) { fprintf(stderr, "Error while receiving filename\n"); } if (!(data->title = get_str(srv_sock))) { fprintf(stderr, "Error while receiving title\n"); } if (!(data->artist = get_str(srv_sock))) { fprintf(stderr, "Error while receiving artist\n"); } if (!(data->album = get_str(srv_sock))) { fprintf(stderr, "Error while receiving album\n"); } if (!get_int(srv_sock, &data->track)) { fprintf(stderr, "Error while receiving track\n"); } if (!get_int(srv_sock, &data->time)) { fprintf(stderr, "Error while receiving time\n"); } if (!get_int(srv_sock, &data->filled)) { fprintf(stderr, "Error while receiving filled\n"); } break; case EV_AVG_BITRATE: printf("moc: EV_AVG_BITRATE\n"); break; case EV_AUDIO_START: printf("moc: AUDIO START\n"); break; case EV_AUDIO_STOP: printf("moc: AUDIO STOP\n"); break; default: fprintf(stderr, "Unknown event: 0x%02x!\n", event); break; } } } void *moc_loop2(void *input) { struct moc *data = (struct moc*)input; int srv_sock = -1; srv_sock = moc_server_connect(); if(srv_sock == -1) { pthread_mutex_lock(&data->lock); data->status = FAILED_TO_CONNECT; pthread_mutex_unlock(&data->lock); pthread_exit(NULL); } data->status = CONNECTED; while(1) { int state = 0; int ev = 0; while(ev != EV_DATA) { send_int(srv_sock, CMD_GET_STATE); if (!get_int(srv_sock, &ev)) { fprintf(stderr, "Can't get data from the server.\n"); } fprintf(stderr, "EVENT: 0x%02x\n", ev); } if(!get_int(srv_sock, &state)) { fprintf(stderr, "Can't get state from the server!\n"); } fprintf(stderr, "Got state%d\n", state); pthread_mutex_lock(&data->lock); if(state == STATE_PLAY) { data->state = PLAYING; fprintf(stderr, "moc: playing\n"); } if(state == STATE_PAUSE) { data->state = PAUSED; fprintf(stderr, "moc: paused\n"); } if(state == STATE_STOP) { data->state = STOPPED; fprintf(stderr, "moc: stopped\n"); } pthread_mutex_unlock(&data->lock); continue; char *file = get_curr_file(srv_sock); fprintf(stderr, "moc: filename: %s\n", file); send_int(srv_sock, CMD_GET_FILE_TAGS); send_str(srv_sock, file); send_int(srv_sock,TAGS_COMMENTS | TAGS_TIME); // filename first. if (!(data->filename = get_str(srv_sock))) { fprintf(stderr, "Error while receiving filename\n"); } if (!(data->title = get_str(srv_sock))) { fprintf(stderr, "Error while receiving title\n"); } if (!(data->artist = get_str(srv_sock))) { fprintf(stderr, "Error while receiving artist\n"); } if (!(data->album = get_str(srv_sock))) { fprintf(stderr, "Error while receiving album\n"); } if (!get_int(srv_sock, &data->track)) { fprintf(stderr, "Error while receiving track\n"); } if (!get_int(srv_sock, &data->time)) { fprintf(stderr, "Error while receiving time\n"); } if (!get_int(srv_sock, &data->filled)) { fprintf(stderr, "Error while receiving filled\n"); } fprintf(stderr, "moc: end switch\n"); pthread_mutex_unlock(&data->lock); fprintf(stderr, "end lock\n"); } fprintf(stderr, "moc: end of loop\n"); return NULL; } extern struct moc *moc_init() { struct moc *moc = (struct moc*)malloc(sizeof(struct moc)); reset_data(moc); pthread_t thread_id; if (pthread_mutex_init(&moc->lock, NULL) != 0) { printf("\n mutex init has failed\n"); return NULL; } moc->status = INITIALIZING; pthread_create(&thread_id, NULL, moc_loop, (void *)moc); return moc; }