From 0112143fdaae0a6264d9e02355e9dc0ca4f7741c Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Dec 2013 02:39:45 +0100 Subject: Split mpvcore/ into common/, misc/, bstr/ --- common/playlist_parser.c | 566 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 566 insertions(+) create mode 100644 common/playlist_parser.c (limited to 'common/playlist_parser.c') diff --git a/common/playlist_parser.c b/common/playlist_parser.c new file mode 100644 index 0000000000..af249f4aa5 --- /dev/null +++ b/common/playlist_parser.c @@ -0,0 +1,566 @@ +/* + * This file is part of MPlayer. + * + * 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 of the License, 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * Warning: this is outdated, crappy code. It is used only for --playlist. + * New or cleaned up code should be added to demux_playlist.c instead. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "talloc.h" +#include "asxparser.h" +#include "playlist.h" +#include "playlist_parser.h" +#include "stream/stream.h" +#include "demux/demux.h" +#include "common/msg.h" +#include "options/path.h" + + +#define BUF_STEP 1024 + +#define WHITES " \n\r\t" + +typedef struct play_tree_parser { + struct stream *stream; + char *buffer,*iter,*line; + int buffer_size , buffer_end; + int keep; + struct playlist *pl; +} play_tree_parser_t; + +static void +strstrip(char* str) { + char* i; + + if (str==NULL) + return; + for(i = str ; i[0] != '\0' && strchr(WHITES,i[0]) != NULL; i++) + /* NOTHING */; + if(i[0] != '\0') { + memmove(str,i,strlen(i) + 1); + for(i = str + strlen(str) - 1 ; strchr(WHITES,i[0]) != NULL; i--) + /* NOTHING */; + i[1] = '\0'; + } else + str[0] = '\0'; +} + +static char* +play_tree_parser_get_line(play_tree_parser_t* p) { + char *end,*line_end; + int r,resize = 0; + + if(p->buffer == NULL) { + p->buffer = malloc(BUF_STEP); + p->buffer_size = BUF_STEP; + p->buffer[0] = 0; + p->iter = p->buffer; + } + + if(p->stream->eof && (p->buffer_end == 0 || p->iter[0] == '\0')) + return NULL; + + assert(p->buffer_end < p->buffer_size); + assert(!p->buffer[p->buffer_end]); + while(1) { + + if(resize) { + char *tmp; + r = p->iter - p->buffer; + end = p->buffer + p->buffer_end; + if (p->buffer_size > INT_MAX - BUF_STEP) + break; + tmp = realloc(p->buffer, p->buffer_size + BUF_STEP); + if (!tmp) + break; + p->buffer = tmp; + p->iter = p->buffer + r; + p->buffer_size += BUF_STEP; + resize = 0; + } + + if(p->buffer_size - p->buffer_end > 1 && ! p->stream->eof) { + r = stream_read(p->stream,p->buffer + p->buffer_end,p->buffer_size - p->buffer_end - 1); + if(r > 0) { + p->buffer_end += r; + assert(p->buffer_end < p->buffer_size); + p->buffer[p->buffer_end] = '\0'; + while(strlen(p->buffer + p->buffer_end - r) != r) + p->buffer[p->buffer_end - r + strlen(p->buffer + p->buffer_end - r)] = '\n'; + } + assert(!p->buffer[p->buffer_end]); + } + + end = strchr(p->iter,'\n'); + if(!end) { + if(p->stream->eof) { + end = p->buffer + p->buffer_end; + break; + } + resize = 1; + continue; + } + break; + } + + line_end = (end > p->iter && *(end-1) == '\r') ? end-1 : end; + if(line_end - p->iter >= 0) + p->line = realloc(p->line, line_end - p->iter + 1); + else + return NULL; + if(line_end - p->iter > 0) + strncpy(p->line,p->iter,line_end - p->iter); + p->line[line_end - p->iter] = '\0'; + if(end[0] != '\0') + end++; + + if(!p->keep) { + if(end[0] != '\0') { + p->buffer_end -= end-p->iter; + memmove(p->buffer,end,p->buffer_end); + } else + p->buffer_end = 0; + p->buffer[p->buffer_end] = '\0'; + p->iter = p->buffer; + } else + p->iter = end; + + return p->line; +} + +static void +play_tree_parser_reset(play_tree_parser_t* p) { + p->iter = p->buffer; +} + +static void +play_tree_parser_stop_keeping(play_tree_parser_t* p) { + p->keep = 0; + if(p->iter && p->iter != p->buffer) { + p->buffer_end -= p->iter -p->buffer; + if(p->buffer_end) + memmove(p->buffer,p->iter,p->buffer_end); + p->buffer[p->buffer_end] = 0; + p->iter = p->buffer; + } +} + + +static bool parse_asx(play_tree_parser_t* p) { + int comments = 0,get_line = 1; + char* line = NULL; + + mp_msg(MSGT_PLAYTREE,MSGL_V,"Trying asx...\n"); + + while(1) { + if(get_line) { + line = play_tree_parser_get_line(p); + if(!line) + return false; + strstrip(line); + if(line[0] == '\0') + continue; + } + if(!comments) { + if(line[0] != '<') { + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"First char isn't '<' but '%c'\n",line[0]); + mp_msg(MSGT_PLAYTREE,MSGL_DBG3,"Buffer = [%s]\n",p->buffer); + return false; + } else if(strncmp(line,"",4) == 0) { // End of comments + comments = 0; + line = c+4; + if(line[0] != '\0') // There is some more data on this line : keep it + get_line = 0; + + } else { + line = c+1; // Jump the - + if(line[0] != '\0') // Some more data + get_line = 0; + else // End of line + get_line = 1; + } + } else // No - on this line (or rest of line) : get next one + get_line = 1; + } + } + + mp_msg(MSGT_PLAYTREE,MSGL_V,"Detected asx format\n"); + + // We have an asx : load it in memory and parse + + while((line = play_tree_parser_get_line(p)) != NULL) + /* NOTHING */; + + mp_msg(MSGT_PLAYTREE,MSGL_DBG3,"Parsing asx file: [%s]\n",p->buffer); + return asx_parse(p->buffer,p->pl); +} + +static bool parse_smil(play_tree_parser_t* p) { + int entrymode=0; + char* line,source[512],*pos,*s_start,*s_end,*src_line; + int is_rmsmil = 0; + unsigned int npkt, ttlpkt; + + mp_msg(MSGT_PLAYTREE,MSGL_V,"Trying smil playlist...\n"); + + // Check if smil + while((line = play_tree_parser_get_line(p)) != NULL) { + strstrip(line); + if(line[0] == '\0') // Ignore empties + continue; + if (strncasecmp(line," ttlpkt) { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"smil-over-realrtsp: bad packet counters (npkk = %u, ttlpkt = %u), assuming single packet.\n", + npkt, ttlpkt); + npkt = ttlpkt = 1; + } + } + + //Get entries from smil + src_line = line; + line = NULL; + do { + strstrip(src_line); + free(line); + line = NULL; + /* If we're parsing smil over realrtsp and this is not the last packet and + * this is the last line in the packet (terminating with ") ) we must get + * the next line, strip the header, and concatenate it to the current line. + */ + if (is_rmsmil && npkt != ttlpkt && strstr(src_line,"\")")) { + char *payload; + + line = strdup(src_line); + if(!(src_line = play_tree_parser_get_line(p))) { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"smil-over-realrtsp: can't get line from packet %u/%u.\n", npkt, ttlpkt); + break; + } + strstrip(src_line); + // Skip header, packet starts after " + if(!(payload = strchr(src_line,'\"'))) { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"smil-over-realrtsp: can't find start of packet, using complete line.\n"); + payload = src_line; + } else + payload++; + // Skip ") at the end of the last line from the current packet + line[strlen(line)-2] = 0; + line = realloc(line, strlen(line)+strlen(payload)+1); + strcat (line, payload); + npkt++; + } else + line = strdup(src_line); + /* Unescape \" to " for smil-over-rtsp */ + if (is_rmsmil && line[0] != '\0') { + int i, j; + + for (i = 0; i < strlen(line); i++) + if (line[i] == '\\' && line[i+1] == '"') + for (j = i; line[j]; j++) + line[j] = line[j+1]; + } + pos = line; + while (pos) { + if (!entrymode) { // all entries filled so far + while ((pos=strchr(pos, '<'))) { + if (strncasecmp(pos," 511) { + mp_msg(MSGT_PLAYTREE,MSGL_V,"Cannot store such a large source %s\n",line); + break; + } + strncpy(source,s_start,s_end-s_start); + source[(s_end-s_start)]='\0'; // Null terminate + playlist_add_file(p->pl, source); + pos = s_end; + } + } + } + } while((src_line = play_tree_parser_get_line(p)) != NULL); + + free(line); + return true; +} + +static bool parse_textplain(play_tree_parser_t* p) { + char* line; + + mp_msg(MSGT_PLAYTREE,MSGL_V,"Trying plaintext playlist...\n"); + play_tree_parser_stop_keeping(p); + + while((line = play_tree_parser_get_line(p)) != NULL) { + strstrip(line); + if(line[0] == '\0' || line[0] == '#' || (line[0] == '/' && line[1] == '/')) + continue; + + playlist_add_file(p->pl,line); + } + + return true; +} + +/** + * \brief decode the base64 used in nsc files + * \param in input string, 0-terminated + * \param buf output buffer, must point to memory suitable for realloc, + * will be NULL on failure. + * \return decoded length in bytes + */ +static int decode_nsc_base64(char *in, char **buf) { + int i, j, n; + if (in[0] != '0' || in[1] != '2') + goto err_out; + in += 2; // skip prefix + if (strlen(in) < 16) // error out if nothing to decode + goto err_out; + in += 12; // skip encoded string length + n = strlen(in) / 4; + *buf = realloc(*buf, n * 3); + for (i = 0; i < n; i++) { + uint8_t c[4]; + for (j = 0; j < 4; j++) { + c[j] = in[4 * i + j]; + if (c[j] >= '0' && c[j] <= '9') c[j] += 0 - '0'; + else if (c[j] >= 'A' && c[j] <= 'Z') c[j] += 10 - 'A'; + else if (c[j] >= 'a' && c[j] <= 'z') c[j] += 36 - 'a'; + else if (c[j] == '{') c[j] = 62; + else if (c[j] == '}') c[j] = 63; + else { + mp_msg(MSGT_PLAYTREE, MSGL_ERR, "Invalid character %c (0x%02"PRIx8")\n", c[j], c[j]); + goto err_out; + } + } + (*buf)[3 * i] = (c[0] << 2) | (c[1] >> 4); + (*buf)[3 * i + 1] = (c[1] << 4) | (c[2] >> 2); + (*buf)[3 * i + 2] = (c[2] << 6) | c[3]; + } + return 3 * n; +err_out: + free(*buf); + *buf = NULL; + return 0; +} + +/** + * \brief "converts" utf16 to ascii by just discarding every second byte + * \param buf buffer to convert + * \param len lenght of buffer, must be > 0 + */ +static void utf16_to_ascii(char *buf, int len) { + int i; + if (len <= 0) return; + for (i = 0; i < len / 2; i++) + buf[i] = buf[i * 2]; + buf[i] = 0; // just in case +} + +static bool parse_nsc(play_tree_parser_t* p) { + char *line, *addr = NULL, *url, *unicast_url = NULL; + int port = 0; + + mp_msg(MSGT_PLAYTREE,MSGL_V,"Trying nsc playlist...\n"); + while((line = play_tree_parser_get_line(p)) != NULL) { + strstrip(line); + if(!line[0]) // Ignore empties + continue; + if (strncasecmp(line,"[Address]", 9) == 0) + break; // nsc header found + else + return false; + } + mp_msg(MSGT_PLAYTREE,MSGL_V,"Detected nsc playlist format\n"); + play_tree_parser_stop_keeping(p); + while ((line = play_tree_parser_get_line(p)) != NULL) { + strstrip(line); + if (!line[0]) + continue; + if (strncasecmp(line, "Unicast URL=", 12) == 0) { + int len = decode_nsc_base64(&line[12], &unicast_url); + if (len <= 0) + mp_msg(MSGT_PLAYTREE, MSGL_WARN, "[nsc] Unsupported Unicast URL encoding\n"); + else + utf16_to_ascii(unicast_url, len); + } else if (strncasecmp(line, "IP Address=", 11) == 0) { + int len = decode_nsc_base64(&line[11], &addr); + if (len <= 0) + mp_msg(MSGT_PLAYTREE, MSGL_WARN, "[nsc] Unsupported IP Address encoding\n"); + else + utf16_to_ascii(addr, len); + } else if (strncasecmp(line, "IP Port=", 8) == 0) { + port = strtol(&line[8], NULL, 0); + } + } + + bool success = false; + + if (unicast_url) + url = strdup(unicast_url); + else if (addr && port) { + url = malloc(strlen(addr) + 7 + 20 + 1); + sprintf(url, "http://%s:%i", addr, port); + } else + goto err_out; + + playlist_add_file(p->pl, url); + free(url); + success = true; +err_out: + free(addr); + free(unicast_url); + return success; +} + +static struct playlist *do_parse(struct stream* stream, bool forced); + +struct playlist *playlist_parse_file(const char *file, struct MPOpts *opts) +{ + stream_t *stream = stream_open(file, opts); + if(!stream) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR, + "Error while opening playlist file %s: %s\n", + file, strerror(errno)); + return false; + } + + mp_msg(MSGT_PLAYTREE, MSGL_V, + "Parsing playlist file %s...\n", file); + + struct playlist *ret = do_parse(stream, true); + free_stream(stream); + + if (ret) + playlist_add_base_path(ret, mp_dirname(file)); + + return ret; + +} + +typedef bool (*parser_fn)(play_tree_parser_t *); +static const parser_fn pl_parsers[] = { + parse_asx, + parse_smil, + parse_nsc, + parse_textplain +}; + + +static struct playlist *do_parse(struct stream* stream, bool forced) +{ + play_tree_parser_t p = { + .stream = stream, + .pl = talloc_zero(NULL, struct playlist), + .keep = 1, + }; + + bool success = false; + struct demuxer *pl_demux = demux_open(stream, "playlist", NULL, stream->opts); + if (pl_demux && pl_demux->playlist) { + playlist_transfer_entries(p.pl, pl_demux->playlist); + success = true; + } + free_demuxer(pl_demux); + if (!success && play_tree_parser_get_line(&p) != NULL) { + for (int n = 0; n < sizeof(pl_parsers) / sizeof(pl_parsers[0]); n++) { + play_tree_parser_reset(&p); + if (pl_parsers[n] == parse_textplain && !forced) + break; + if (pl_parsers[n](&p)) { + success = true; + break; + } + } + } + + if(success) + mp_msg(MSGT_PLAYTREE,MSGL_V,"Playlist successfully parsed\n"); + else { + mp_msg(MSGT_PLAYTREE,((forced==1)?MSGL_ERR:MSGL_V),"Error while parsing playlist\n"); + talloc_free(p.pl); + p.pl = NULL; + } + + if (p.pl && !p.pl->first) + mp_msg(MSGT_PLAYTREE,((forced==1)?MSGL_WARN:MSGL_V),"Warning: empty playlist\n"); + + return p.pl; +} -- cgit v1.2.3