/* * This file is part of mpv. * * mpv 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. * * mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>. */#include<strings.h>#include"common/common.h"#include"options/options.h"#include"common/msg.h"#include"common/playlist.h"#include"options/path.h"#include"stream/stream.h"#include"demux.h"#define PROBE_SIZE (8 * 1024)staticboolcheck_mimetype(structstream*s,constchar*const*list){if(s->mime_type){for(intn=0;list&&list[n];n++){if(strcmp(s->mime_type,list[n])==0)returntrue;}}returnfalse;}structpl_parser{structmp_log*log;structstream*s;charbuffer[8*1024];intutf16;structplaylist*pl;boolerror;boolprobing;boolforce;enumdemux_checkcheck_level;structstream*real_stream;};staticchar*pl_get_line0(structpl_parser*p){char*res=stream_read_line(p->s,p->buffer,sizeof(p->buffer),p->utf16);if(res){intlen=strlen(res);if(len>0&&res[len-1]=='\n')res[len-1]='\0';}else{p->error|=!p->s->eof;}returnres;}staticbstrpl_get_line(structpl_parser*p){returnbstr0(pl_get_line0(p));}staticvoidpl_add(structpl_parser*p,bstrentry){char*s=bstrto0(NULL,entry);playlist_add_file(p->pl,s);talloc_free(s);}staticboolpl_eof(structpl_parser*p){returnp->error||p->s->eof;}staticboolmaybe_text(bstrd){for(intn=0;n<d.len;n++){unsignedcharc=d.start[n];if(c<32&&c!='\n'&&c!='\r'&&c!='\t')returnfalse;}returntrue;}staticintparse_m3u(structpl_parser*p){bstrline=bstr_strip(pl_get_line(p));if(p->probing&&!bstr_equals0(line,"#EXTM3U")){// Last resort: if the file extension is m3u, it might be headerless.if(p->check_level==DEMUX_CHECK_UNSAFE){char*ext=mp_splitext(p->real_stream->url,NULL);bstrdata=stream_peek(p->real_stream,PROBE_SIZE);if(ext&&!strcmp(ext,"m3u")&&data.len>10&&maybe_text(data))gotook;}return-1;}ok:if(p->probing)return0;while(line.len||!pl_eof(p)){if(line.len>0&&!bstr_startswith0(line,"#"))pl_add(p,line);line=bstr_strip(pl_get_line(p));}return0;}staticintparse_ref_init(structpl_parser*p){bstrline=bstr_strip(pl_get_line(p));if(!bstr_equals0(line,"[Reference]"))return-1;// ASF http streaming redirection - this is needed because ffmpeg http://// and mmsh:// can not automatically switch automatically between each// others. Both protocols use http - MMSH requires special http headers// to "activate" it, and will in other cases return this playlist.staticconstchar*constmmsh_types[]={"audio/x-ms-wax","audio/x-ms-wma","video/x-ms-asf","video/x-ms-afs","video/x-ms-wmv","video/x-ms-wma","application/x-mms-framed","application/vnd.ms.wms-hdr.asfv1",NULL};bstrburl=bstr0(p->s->url);if(bstr_eatstart0(&burl,"http://")&&check_mimetype(p->s,mmsh_types)){MP_INFO(p,"Redirecting to mmsh://\n");playlist_add_file(p->pl,talloc_asprintf(p,"mmsh://%.*s",BSTR_P(burl)));return0;}while(!pl_eof(p)){line=bstr_strip(pl_get_line(p));if(bstr_case_startswith(line,bstr0("Ref"))){bstr_split_tok(line,"=",&(bstr){0},&line);if(line.len)pl_add(p,line);}}return0;}staticintparse_mov_rtsptext(structpl_parser*p){bstrline=pl_get_line(p);if(!bstr_eatstart(&line,bstr0("RTSPtext")))return-1;if(p->probing)return0;line=bstr_strip(line);do{if(bstr_case_startswith(line,bstr0("rtsp://"))){pl_add(p,line);return0;}}while(!pl_eof(p)&&(line=bstr_strip(pl_get_line(p))).len);return-1;}staticintparse_pls(structpl_parser*p){bstrline={0};while(!line.len&&!pl_eof(p))line=bstr_strip(pl_get_line(p));if(bstrcasecmp0(line,"[playlist]")!=0)return-1;if(p->probing)return0;while(!pl_eof(p)){line=bstr_strip(pl_get_line(p));bstrkey,value;if(bstr_split_tok(line,"=",&key,&value)&&bstr_case_startswith(key,bstr0("File"))){value=bstr_strip(value);if(bstr_startswith0(value,"\"")&&bstr_endswith0(value,"\""))value=bstr_splice(value,1,-1);pl_add(p,value);}}return0;}staticintparse_txt(structpl_parser*p){if(!p->force)return-1;if(p->probing)return0;MP_WARN(p,"Reading plaintext playlist.\n");while(!pl_eof(p)){bstrline=bstr_strip(pl_get_line(p));if(line.len==0)continue;pl_add(p,line);}return0;}#define MIME_TYPES(...) \ .mime_types = (const char*const[]){__VA_ARGS__, NULL}structpl_format{constchar*name;int(*parse)(structpl_parser*p);constchar*const*mime_types;};staticconststructpl_formatformats[]={{"m3u",parse_m3u,MIME_TYPES("audio/mpegurl","audio/x-mpegurl","application/x-mpegurl")},{"ini",parse_ref_init},{"mov",parse_mov_rtsptext},{"pls",parse_pls,MIME_TYPES("audio/x-scpls")},{"txt",parse_txt},};staticconststructpl_format*probe_pl(structpl_parser*p){int64_tstart=stream_tell(p->s);for(intn=0;n<MP_ARRAY_SIZE(formats);n++){conststructpl_format*fmt=&formats[n];stream_seek(p->s,start);if(check_mimetype(p->s,fmt->mime_types)){MP_VERBOSE(p,"forcing format by mime-type.\n");p->force=true;returnfmt;}if(fmt->parse(p)>=0)returnfmt;}returnNULL;}staticintopen_file(structdemuxer*demuxer,enumdemux_checkcheck){boolforce=check<DEMUX_CHECK_UNSAFE||check==DEMUX_CHECK_REQUEST;structpl_parser*p=talloc_zero(NULL,structpl_parser);p->log=demuxer->log;p->pl=talloc_zero(p,structplaylist);p->real_stream=demuxer->stream;bstrprobe_buf=stream_peek(demuxer->stream,PROBE_SIZE);p->s=open_memory_stream(probe_buf.start,probe_buf.len);p->s->mime_type=demuxer->stream->mime_type;p->utf16=stream_skip_bom(p->s);p->force=force;p->check_level=check;p->probing=true;conststructpl_format*fmt=probe_pl(p);free_stream(p->s);playlist_clear(p->pl);if(!fmt){talloc_free(p);return-1;}p->probing=false;p->error=false;p->s=demuxer->stream;p->utf16=stream_skip_bom(p->s);boolok=fmt->parse(p)>=0&&!p->error;if(ok)playlist_add_base_path(p->pl,mp_dirname(demuxer->filename));demuxer->playlist=talloc_steal(demuxer,p->pl);demuxer->filetype=fmt->name;demuxer->fully_read=true;talloc_free(p);returnok?0:-1;}conststructdemuxer_descdemuxer_desc_playlist={.name="playlist",.desc="Playlist file",.open=open_file,};