📄 command.c
字号:
/* * command.c */#include <unistd.h>#include <stdio.h>#include <string.h>#include <stdbool.h>#include <stdint.h>#include <fcntl.h>#include "command.h"#include "assoc.h"#include "fs.h"#include "stream.h"#include "utils.h"/* max number of args that can be passed to a command (arbitrary) */#define ARGV_MAX 10/* the commands */bool cmd_assoc(struct listen_data *, FILE *, int, char **);bool cmd_astream(struct listen_data *, FILE *, int, char **);bool cmd_avstream(struct listen_data *, FILE *, int, char **);bool cmd_check(struct listen_data *, FILE *, int, char **);bool cmd_file(struct listen_data *, FILE *, int, char **);bool cmd_help(struct listen_data *, FILE *, int, char **);bool cmd_quit(struct listen_data *, FILE *, int, char **);bool cmd_vstream(struct listen_data *, FILE *, int, char **);static struct{ char *name; char *args; bool (*proc)(struct listen_data *, FILE *, int, char **); char *help;} command[] ={ { "assoc", "", cmd_assoc, "List component tag to PID mappings" }, { "astream", "<ComponentTag>", cmd_astream, "Stream the given audio component tag" }, { "avstream", "<AudioTag> <VideoTag>", cmd_avstream, "Stream the given audio and video component tags" }, { "check", "<ContentReference>", cmd_check, "Check if the given file exists on the carousel" }, { "exit", "", cmd_quit, "Close the connection" }, { "file", "<ContentReference>", cmd_file, "Retrieve the given file from the carousel" }, { "help", "", cmd_help, "List available commands" }, { "quit", "", cmd_quit, "Close the connection" }, { "vstream", "<ComponentTag>", cmd_vstream, "Stream the given video component tag" }, { NULL, NULL, NULL, NULL }};/* send an OK/error code etc response down client_sock */#define SEND_RESPONSE(RC, MESSAGE) fputs(#RC " " MESSAGE "\n", client)/* internal routines */char *external_filename(struct listen_data *, char *);char *canonical_filename(char *);/* * process the given command * return true if we should close the connection */boolprocess_command(struct listen_data *listen_data, FILE *client, char *cmd){ int argc; char *argv[ARGV_MAX]; char term; unsigned int cmd_len; int i; /* chop it into words, complicated by quoting */ argc = 0; while(argc < ARGV_MAX && *cmd != '\0') { argv[argc++] = cmd; /* do we need to find the next space, or the next quote */ if(*cmd == '\'' || *cmd == '"') { term = *cmd; /* remove/skip the opening quote */ argv[argc-1] ++; cmd ++; } else { /* stop at the next space */ term = ' '; } /* find the next terminating character */ while(*cmd != term && *cmd != '\0') cmd ++; /* if this is not the end, skip to the start of the next word */ if(*cmd != '\0') { /* terminate the last word */ *cmd = '\0'; /* move onto the next non-space character */ cmd = skip_ws(cmd + 1); } } cmd_len = strlen(argv[0]); for(i=0; command[i].name != NULL; i++) { if(strncmp(argv[0], command[i].name, cmd_len) == 0) return (command[i].proc)(listen_data, client, argc, argv); } SEND_RESPONSE(500, "Unrecognised command"); return false;}/* * the commands * listen_data is global data needed by listener commands * client is where any response data should go * argc is the number of arguments passed to it * argv[0] is the command name (or the abreviation the user used) * return true if we should close the connection */#define CHECK_USAGE(ARGC, SYNTAX) \if(argc != ARGC) \{ \ SEND_RESPONSE(500, "Syntax: " SYNTAX); \ return false; \}/* * assoc * show the association/component tag to PID mappings * also shows the default audio and video PIDs * (just for debugging) */boolcmd_assoc(struct listen_data *listen_data, FILE *client, int argc, char *argv[]){ struct carousel *car = listen_data->carousel; unsigned int i; SEND_RESPONSE(200, "OK"); /* if this is ever used by rb-browser, you will need to send a length first */ fprintf(client, "Tag\tPID\tType\n"); fprintf(client, "===\t===\t====\n"); /* default audio and video PIDs */ fprintf(client, "(audio)\t%u\t%u\n", car->audio_pid, car->audio_type); fprintf(client, "(video)\t%u\t%u\n", car->video_pid, car->video_type); /* component tag mappings */ for(i=0; i<car->assoc.nassocs; i++) fprintf(client, "%u\t%u\t%u\n", car->assoc.sids[i], car->assoc.pids[i], car->assoc.types[i]); return false;}/* * astream <tag> * send the given audio stream down the connection * the tag should be an association/component_tag number as found in the PMT * the tag is converted to a PID and that PID is sent as a MPEG Transport Stream down the connection * if tag is -1, the default audio stream for the current service_id is sent */boolcmd_astream(struct listen_data *listen_data, FILE *client, int argc, char *argv[]){ struct carousel *car = listen_data->carousel; int tag; uint16_t pid; uint8_t type; int audio_fd; int ts_fd; char hdr[64]; CHECK_USAGE(2, "astream <ComponentTag>"); tag = strtol(argv[1], NULL, 0); /* map the tag to a PID and stream type, or use the default */ if(tag == -1) { /* check we have a default stream */ if(car->audio_pid == 0) { SEND_RESPONSE(500, "Unable to find audio PID"); return false; } pid = car->audio_pid; type = car->audio_type; } else { pid = stream2pid(&car->assoc, tag); type = stream2type(&car->assoc, tag); } /* add the PID to the demux device */ if((audio_fd = add_demux_filter(car->demux_device, pid, DMX_PES_AUDIO)) < 0) { SEND_RESPONSE(500, "Unable to open audio PID"); return false; } /* we can now read a transport stream from the dvr device */ if((ts_fd = open(car->dvr_device, O_RDONLY)) < 0) { SEND_RESPONSE(500, "Unable to open DVB device"); close(audio_fd); return false; } /* send the OK code */ SEND_RESPONSE(200, "OK"); /* tell the client what PID and stream type the component tag resolved to */ snprintf(hdr, sizeof(hdr), "AudioPID %u AudioType %u\n", pid, type); fputs(hdr, client); /* shovel the transport stream to client until the client closes or we get an error */ stream_ts(ts_fd, client); /* clean up */ close(ts_fd); close(audio_fd); /* close the connection */ return true;}/* * vstream <tag> * send the given video stream down the connection * the tag should be an association/component_tag number as found in the PMT * the tag is converted to a PID and that PID is sent as a MPEG Transport Stream down the connection * if tag is -1, the default video stream for the current service_id is sent */boolcmd_vstream(struct listen_data *listen_data, FILE *client, int argc, char *argv[]){ struct carousel *car = listen_data->carousel; int tag; uint16_t pid; uint8_t type; int video_fd; int ts_fd; char hdr[64]; CHECK_USAGE(2, "vstream <ComponentTag>"); tag = strtol(argv[1], NULL, 0); /* map the tag to a PID and stream type, or use the default */ if(tag == -1) { /* check we have a default stream */ if(car->video_pid == 0) { SEND_RESPONSE(500, "Unable to find video PID"); return false; } pid = car->video_pid; type = car->video_type; } else { pid = stream2pid(&car->assoc, tag); type = stream2type(&car->assoc, tag); } /* add the PID to the demux device */ if((video_fd = add_demux_filter(car->demux_device, pid, DMX_PES_VIDEO)) < 0) { SEND_RESPONSE(500, "Unable to open video PID"); return false; } /* we can now read a transport stream from the dvr device */ if((ts_fd = open(car->dvr_device, O_RDONLY)) < 0) { SEND_RESPONSE(500, "Unable to open DVB device"); close(video_fd); return false; } /* send the OK code */ SEND_RESPONSE(200, "OK"); /* tell the client what PID and stream type the component tag resolved to */ snprintf(hdr, sizeof(hdr), "VideoPID %u VideoType %u\n", pid, type); fputs(hdr, client); /* shovel the transport stream down client_sock until the client closes it or we get an error */ stream_ts(ts_fd, client); /* clean up */ close(ts_fd); close(video_fd); /* close the connection */ return true;}/* * avstream <audio_tag> <video_tag> * send the given audio and video streams down the connection * the tags should be association/component_tag numbers as found in the PMT * the tags are converted to PIDs and those PIDs are sent as a MPEG Transport Stream down the connection * if a tag is -1, the default audio or video stream for the current service_id is sent */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -