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/asxparser.c | 581 ++++++++++++++++++++++++ common/asxparser.h | 27 ++ common/av_common.c | 178 ++++++++ common/av_common.h | 39 ++ common/av_log.c | 176 ++++++++ common/av_log.h | 2 + common/av_opts.c | 55 +++ common/av_opts.h | 30 ++ common/codecs.c | 147 ++++++ common/codecs.h | 43 ++ common/common.c | 163 +++++++ common/common.h | 81 ++++ common/cpudetect.c | 56 +++ common/cpudetect.h | 40 ++ common/encode.h | 21 + common/encode_lavc.c | 1115 ++++++++++++++++++++++++++++++++++++++++++++++ common/encode_lavc.h | 101 +++++ common/global.h | 12 + common/msg.c | 389 ++++++++++++++++ common/msg.h | 180 ++++++++ common/playlist.c | 241 ++++++++++ common/playlist.h | 82 ++++ common/playlist_parser.c | 566 +++++++++++++++++++++++ common/playlist_parser.h | 29 ++ common/version.c | 26 ++ 25 files changed, 4380 insertions(+) create mode 100644 common/asxparser.c create mode 100644 common/asxparser.h create mode 100644 common/av_common.c create mode 100644 common/av_common.h create mode 100644 common/av_log.c create mode 100644 common/av_log.h create mode 100644 common/av_opts.c create mode 100644 common/av_opts.h create mode 100644 common/codecs.c create mode 100644 common/codecs.h create mode 100644 common/common.c create mode 100644 common/common.h create mode 100644 common/cpudetect.c create mode 100644 common/cpudetect.h create mode 100644 common/encode.h create mode 100644 common/encode_lavc.c create mode 100644 common/encode_lavc.h create mode 100644 common/global.h create mode 100644 common/msg.c create mode 100644 common/msg.h create mode 100644 common/playlist.c create mode 100644 common/playlist.h create mode 100644 common/playlist_parser.c create mode 100644 common/playlist_parser.h create mode 100644 common/version.c (limited to 'common') diff --git a/common/asxparser.c b/common/asxparser.c new file mode 100644 index 0000000000..5b1d5652a3 --- /dev/null +++ b/common/asxparser.c @@ -0,0 +1,581 @@ +/* + * 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. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "playlist.h" +#include "playlist_parser.h" +#include "stream/stream.h" +#include "asxparser.h" +#include "common/msg.h" + + +typedef struct ASX_Parser_t ASX_Parser_t; + +typedef struct { + char* buffer; + int line; +} ASX_LineSave_t; + +struct ASX_Parser_t { + int line; // Curent line + ASX_LineSave_t *ret_stack; + int ret_stack_size; + char* last_body; + int deep; + struct playlist *pl; +}; + +ASX_Parser_t *asx_parser_new(struct playlist *pl); + +void +asx_parser_free(ASX_Parser_t* parser); + +/* + * Return -1 on error, 0 when nothing is found, 1 on sucess + */ +int +asx_get_element(ASX_Parser_t* parser,char** _buffer, + char** _element,char** _body,char*** _attribs); + +int +asx_parse_attribs(ASX_Parser_t* parser,char* buffer,char*** _attribs); + +/////// Attribs utils + +char* +asx_get_attrib(const char* attrib,char** attribs); + +#define asx_free_attribs(a) asx_list_free(&a,free) + +////// List utils + +typedef void (*ASX_FreeFunc)(void* arg); + +void +asx_list_free(void* list_ptr,ASX_FreeFunc free_func); + + +////// List utils + +void +asx_list_free(void* list_ptr,ASX_FreeFunc free_func) { + void** ptr = *(void***)list_ptr; + if(ptr == NULL) return; + if(free_func != NULL) { + for( ; *ptr != NULL ; ptr++) + free_func(*ptr); + } + free(*(void**)list_ptr); + *(void**)list_ptr = NULL; +} + +/////// Attribs utils + +char* +asx_get_attrib(const char* attrib,char** attribs) { + char** ptr; + + if(attrib == NULL || attribs == NULL) return NULL; + for(ptr = attribs; ptr[0] != NULL; ptr += 2){ + if(strcasecmp(ptr[0],attrib) == 0) + return strdup(ptr[1]); + } + return NULL; +} + +#define asx_warning_attrib_required(p,e,a) mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : element %s don't have the required attribute %s",p->line,e,a) +#define asx_warning_body_parse_error(p,e) mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : error while parsing %s body",p->line,e) + +ASX_Parser_t *asx_parser_new(struct playlist *pl) +{ + ASX_Parser_t* parser = calloc(1,sizeof(ASX_Parser_t)); + parser->pl = pl; + return parser; +} + +void +asx_parser_free(ASX_Parser_t* parser) { + if(!parser) return; + free(parser->ret_stack); + free(parser); + +} + +#define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define SPACE " \n\t\r" + +int +asx_parse_attribs(ASX_Parser_t* parser,char* buffer,char*** _attribs) { + char *ptr1, *ptr2, *ptr3; + int n_attrib = 0; + char **attribs = NULL; + char *attrib, *val; + + ptr1 = buffer; + while(1) { + for( ; strchr(SPACE,*ptr1) != NULL; ptr1++) { // Skip space + if(*ptr1 == '\0') break; + } + ptr3 = strchr(ptr1,'='); + if(ptr3 == NULL) break; + for(ptr2 = ptr3-1; strchr(SPACE,*ptr2) != NULL; ptr2--) { + if (ptr2 == ptr1) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : this should never append, back to attribute begin while skipping end space",parser->line); + break; + } + } + attrib = malloc(ptr2-ptr1+2); + strncpy(attrib,ptr1,ptr2-ptr1+1); + attrib[ptr2-ptr1+1] = '\0'; + + ptr1 = strchr(ptr3,'"'); + if(ptr1 == NULL || ptr1[1] == '\0') ptr1 = strchr(ptr3,'\''); + if(ptr1 == NULL || ptr1[1] == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : can't find attribute %s value",parser->line,attrib); + free(attrib); + break; + } + ptr2 = strchr(ptr1+1,ptr1[0]); + if (ptr2 == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : value of attribute %s isn't finished",parser->line,attrib); + free(attrib); + break; + } + ptr1++; + val = malloc(ptr2-ptr1+1); + strncpy(val,ptr1,ptr2-ptr1); + val[ptr2-ptr1] = '\0'; + n_attrib++; + + attribs = realloc(attribs, (2 * n_attrib + 1) * sizeof(char*)); + attribs[n_attrib*2-2] = attrib; + attribs[n_attrib*2-1] = val; + + ptr1 = ptr2+1; + } + + if(n_attrib > 0) + attribs[n_attrib*2] = NULL; + + *_attribs = attribs; + + return n_attrib; +} + +/* + * Return -1 on error, 0 when nothing is found, 1 on sucess + */ +int +asx_get_element(ASX_Parser_t* parser,char** _buffer, + char** _element,char** _body,char*** _attribs) { + char *ptr1,*ptr2, *ptr3, *ptr4; + char *attribs = NULL; + char *element = NULL, *body = NULL, *ret = NULL, *buffer; + int n_attrib = 0; + int body_line = 0,attrib_line,ret_line,in = 0; + int quotes = 0; + + if(_buffer == NULL || _element == NULL || _body == NULL || _attribs == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : asx_get_element called with invalid value",parser->line); + return -1; + } + + *_body = *_element = NULL; + *_attribs = NULL; + buffer = *_buffer; + + if(buffer == NULL) return 0; + + if(parser->ret_stack && /*parser->last_body && */buffer != parser->last_body) { + ASX_LineSave_t* ls = parser->ret_stack; + int i; + for(i = 0 ; i < parser->ret_stack_size ; i++) { + if(buffer == ls[i].buffer) { + parser->line = ls[i].line; + break; + } + + } + if( i < parser->ret_stack_size) { + i++; + if( i < parser->ret_stack_size) + memmove(parser->ret_stack,parser->ret_stack+i, (parser->ret_stack_size - i)*sizeof(ASX_LineSave_t)); + parser->ret_stack_size -= i; + if(parser->ret_stack_size > 0) + parser->ret_stack = realloc(parser->ret_stack,parser->ret_stack_size*sizeof(ASX_LineSave_t)); + else { + free(parser->ret_stack); + parser->ret_stack = NULL; + } + } + } + + ptr1 = buffer; + while(1) { + for( ; ptr1[0] != '<' ; ptr1++) { + if(ptr1[0] == '\0') { + ptr1 = NULL; + break; + } + if(ptr1[0] == '\n') parser->line++; + } + //ptr1 = strchr(ptr1,'<'); + if(!ptr1 || ptr1[1] == '\0') return 0; // Nothing found + + if(strncmp(ptr1,"",3) != 0 ; ptr1++) { + if(ptr1[0] == '\0') { + ptr1 = NULL; + break; + } + if(ptr1[0] == '\n') parser->line++; + } + //ptr1 = strstr(ptr1,"-->"); + if(!ptr1) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : unfinished comment",parser->line); + return -1; + } + } else { + break; + } + } + + // Is this space skip very useful ?? + for(ptr1++; strchr(SPACE,ptr1[0]) != NULL; ptr1++) { // Skip space + if(ptr1[0] == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line); + return -1; + } + if(ptr1[0] == '\n') parser->line++; + } + + for(ptr2 = ptr1; strchr(LETTER,*ptr2) != NULL;ptr2++) { // Go to end of name + if(*ptr2 == '\0'){ + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line); + return -1; + } + if(ptr2[0] == '\n') parser->line++; + } + + element = malloc(ptr2-ptr1+1); + strncpy(element,ptr1,ptr2-ptr1); + element[ptr2-ptr1] = '\0'; + + for( ; strchr(SPACE,*ptr2) != NULL; ptr2++) { // Skip space + if(ptr2[0] == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line); + free(element); + return -1; + } + if(ptr2[0] == '\n') parser->line++; + } + attrib_line = parser->line; + + + + for(ptr3 = ptr2; ptr3[0] != '\0'; ptr3++) { // Go to element end + if(ptr3[0] == '"') quotes ^= 1; + if(!quotes && (ptr3[0] == '>' || strncmp(ptr3,"/>",2) == 0)) + break; + if(ptr3[0] == '\n') parser->line++; + } + if(ptr3[0] == '\0' || ptr3[1] == '\0') { // End of file + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line); + free(element); + return -1; + } + + // Save attribs string + if(ptr3-ptr2 > 0) { + attribs = malloc(ptr3-ptr2+1); + strncpy(attribs,ptr2,ptr3-ptr2); + attribs[ptr3-ptr2] = '\0'; + } + //bs_line = parser->line; + if(ptr3[0] != '/') { // Not Self closed element + ptr3++; + for( ; strchr(SPACE,*ptr3) != NULL; ptr3++) { // Skip space on body begin + if(*ptr3 == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing %s element body",parser->line,element); + free(element); + free(attribs); + return -1; + } + if(ptr3[0] == '\n') parser->line++; + } + ptr4 = ptr3; + body_line = parser->line; + while(1) { // Find closing element + for( ; ptr4[0] != '<' ; ptr4++) { + if(ptr4[0] == '\0') { + ptr4 = NULL; + break; + } + if(ptr4[0] == '\n') parser->line++; + } + if(ptr4 && strncmp(ptr4,"",3) != 0 ; ptr4++) { + if(ptr4[0] == '\0') { + ptr4 = NULL; + break; + } + if(ptr1[0] == '\n') parser->line++; + } + continue; + } + if(ptr4 == NULL || ptr4[1] == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing %s element body",parser->line,element); + free(element); + free(attribs); + return -1; + } + if(ptr4[1] != '/' && strncasecmp(element,ptr4+1,strlen(element)) == 0) { + in++; + ptr4+=2; + continue; + } else if(strncasecmp(element,ptr4+2,strlen(element)) == 0) { // Extract body + if(in > 0) { + in--; + ptr4 += 2+strlen(element); + continue; + } + ret = ptr4+strlen(element)+3; + if(ptr4 != ptr3) { + ptr4--; + for( ; ptr4 != ptr3 && strchr(SPACE,*ptr4) != NULL; ptr4--) ;// Skip space on body end + // if(ptr4[0] == '\0') parser->line--; + //} + ptr4++; + body = malloc(ptr4-ptr3+1); + strncpy(body,ptr3,ptr4-ptr3); + body[ptr4-ptr3] = '\0'; + } + break; + } else { + ptr4 += 2; + } + } + } else { + ret = ptr3 + 2; // 2 is for /> + } + + for( ; ret[0] != '\0' && strchr(SPACE,ret[0]) != NULL; ret++) { // Skip space + if(ret[0] == '\n') parser->line++; + } + + ret_line = parser->line; + + if(attribs) { + parser->line = attrib_line; + n_attrib = asx_parse_attribs(parser,attribs,_attribs); + free(attribs); + if(n_attrib < 0) { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : error while parsing element %s attributes",parser->line,element); + free(element); + free(body); + return -1; + } + } else + *_attribs = NULL; + + *_element = element; + *_body = body; + + parser->last_body = body; + parser->ret_stack_size++; + parser->ret_stack = realloc(parser->ret_stack,parser->ret_stack_size*sizeof(ASX_LineSave_t)); + if(parser->ret_stack_size > 1) + memmove(parser->ret_stack+1,parser->ret_stack,(parser->ret_stack_size-1)*sizeof(ASX_LineSave_t)); + parser->ret_stack[0].buffer = ret; + parser->ret_stack[0].line = ret_line; + parser->line = body ? body_line : ret_line; + + *_buffer = ret; + return 1; + +} + +static void +asx_parse_ref(ASX_Parser_t* parser, char** attribs) { + char *href; + + href = asx_get_attrib("HREF",attribs); + if(href == NULL) { + asx_warning_attrib_required(parser,"REF" ,"HREF" ); + return; + } +#if 0 + // replace http my mmshttp to avoid infinite loops + // disabled since some playlists for e.g. WinAMP use asx as well + // "-user-agent NSPlayer/4.1.0.3856" is a possible workaround + if (strncmp(href, "http://", 7) == 0) { + char *newref = malloc(3 + strlen(href) + 1); + strcpy(newref, "mms"); + strcpy(newref + 3, href); + free(href); + href = newref; + } +#endif + + playlist_add_file(parser->pl, href); + + mp_msg(MSGT_PLAYTREE,MSGL_V,"Adding file %s to element entry\n",href); + + free(href); + +} + +static void asx_parse_entryref(ASX_Parser_t* parser,char* buffer,char** _attribs) { + char *href; + + if(parser->deep > 0) + return; + + href = asx_get_attrib("HREF",_attribs); + if(href == NULL) { + asx_warning_attrib_required(parser,"ENTRYREF" ,"HREF" ); + return; + } + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Recursive playlist %s\n", href); + playlist_add_file(parser->pl, href); + free(href); + //mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n"); +} + +static void asx_parse_entry(ASX_Parser_t* parser,char* buffer,char** _attribs) { + char *element,*body,**attribs; + int r; + + while(buffer && buffer[0] != '\0') { + r = asx_get_element(parser,&buffer,&element,&body,&attribs); + if(r < 0) { + asx_warning_body_parse_error(parser,"ENTRY"); + return; + } else if (r == 0) { // No more element + break; + } + if(strcasecmp(element,"REF") == 0) { + asx_parse_ref(parser,attribs); + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Adding element %s to entry\n",element); + } else + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element); + free(body); + asx_free_attribs(attribs); + } + +} + + +static void asx_parse_repeat(ASX_Parser_t* parser,char* buffer,char** _attribs) { + char *element,*body,**attribs; + int r; + + asx_get_attrib("COUNT",_attribs); + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Ignoring repeated playlist entries\n"); + + while(buffer && buffer[0] != '\0') { + r = asx_get_element(parser,&buffer,&element,&body,&attribs); + if(r < 0) { + asx_warning_body_parse_error(parser,"REPEAT"); + return; + } else if (r == 0) { // No more element + break; + } + if(strcasecmp(element,"ENTRY") == 0) { + asx_parse_entry(parser,body,attribs); + } else if(strcasecmp(element,"ENTRYREF") == 0) { + asx_parse_entryref(parser,body,attribs); + } else if(strcasecmp(element,"REPEAT") == 0) { + asx_parse_repeat(parser,body,attribs); + } else + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element); + free(body); + asx_free_attribs(attribs); + } + +} + + +bool asx_parse(char* buffer, struct playlist *pl) +{ + char *element,*asx_body,**asx_attribs,*body = NULL, **attribs; + int r; + ASX_Parser_t* parser = asx_parser_new(pl); + + parser->line = 1; + parser->deep = 0; + + r = asx_get_element(parser,&buffer,&element,&asx_body,&asx_attribs); + if(r < 0) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : Syntax error ???",parser->line); + asx_parser_free(parser); + return false; + } else if(r == 0) { // No contents + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"empty asx element"); + asx_parser_free(parser); + return false; + } + + if(strcasecmp(element,"ASX") != 0) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"first element isn't ASX, it's %s\n",element); + asx_free_attribs(asx_attribs); + asx_parser_free(parser); + return false; + } + + if(!asx_body) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"ASX element is empty"); + asx_free_attribs(asx_attribs); + asx_parser_free(parser); + return false; + } + + buffer = asx_body; + while(buffer && buffer[0] != '\0') { + r = asx_get_element(parser,&buffer,&element,&body,&attribs); + if(r < 0) { + asx_warning_body_parse_error(parser,"ASX"); + asx_parser_free(parser); + return false; + } else if (r == 0) { // No more element + break; + } + if(strcasecmp(element,"ENTRY") == 0) { + asx_parse_entry(parser,body,attribs); + } else if(strcasecmp(element,"ENTRYREF") == 0) { + asx_parse_entryref(parser,body,attribs); + } else if(strcasecmp(element,"REPEAT") == 0) { + asx_parse_repeat(parser,body,attribs); + } else + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element); + free(body); + asx_free_attribs(attribs); + } + + free(asx_body); + asx_free_attribs(asx_attribs); + asx_parser_free(parser); + return true; +} diff --git a/common/asxparser.h b/common/asxparser.h new file mode 100644 index 0000000000..e49a2cedc0 --- /dev/null +++ b/common/asxparser.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#ifndef MPLAYER_ASXPARSER_H +#define MPLAYER_ASXPARSER_H + +#include + +struct playlist; +bool asx_parse(char* buffer, struct playlist *pl); + +#endif /* MPLAYER_ASXPARSER_H */ diff --git a/common/av_common.c b/common/av_common.c new file mode 100644 index 0000000000..cccb0f755e --- /dev/null +++ b/common/av_common.c @@ -0,0 +1,178 @@ +/* + * 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 . + */ + +#include + +#include +#include + +#include "common/common.h" +#include "common/msg.h" +#include "demux/packet.h" +#include "av_common.h" +#include "codecs.h" + +#include "osdep/numcores.h" + + +// Copy the codec-related fields from st into avctx. This does not set the +// codec itself, only codec related header data provided by libavformat. +// The goal is to initialize a new decoder with the header data provided by +// libavformat, and unlike avcodec_copy_context(), allow the user to create +// a clean AVCodecContext for a manually selected AVCodec. +// This is strictly for decoding only. +void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st) +{ + if (st->extradata_size) { + av_free(avctx->extradata); + avctx->extradata_size = 0; + avctx->extradata = + av_mallocz(st->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (avctx->extradata) { + avctx->extradata_size = st->extradata_size; + memcpy(avctx->extradata, st->extradata, st->extradata_size); + } + } + avctx->codec_tag = st->codec_tag; + avctx->stream_codec_tag = st->stream_codec_tag; + avctx->bit_rate = st->bit_rate; + avctx->width = st->width; + avctx->height = st->height; + avctx->pix_fmt = st->pix_fmt; + avctx->sample_aspect_ratio = st->sample_aspect_ratio; + avctx->chroma_sample_location = st->chroma_sample_location; + avctx->sample_rate = st->sample_rate; + avctx->channels = st->channels; + avctx->block_align = st->block_align; + avctx->channel_layout = st->channel_layout; + avctx->audio_service_type = st->audio_service_type; + avctx->bits_per_coded_sample = st->bits_per_coded_sample; +} + +// We merely pass-through our PTS/DTS as an int64_t; libavcodec won't use it. +union pts { int64_t i; double d; }; + +// Convert the mpv style timestamp (seconds as double) to a libavcodec style +// timestamp (integer units in a given timebase). +// +// If the given timebase is NULL or invalid, pass through the mpv timestamp by +// reinterpret casting them to int64_t. In this case, the timestamps will be +// non-sense for libavcodec, but we expect that it doesn't interpret them, +// and treats them as opaque. +int64_t mp_pts_to_av(double mp_pts, AVRational *tb) +{ + assert(sizeof(int64_t) >= sizeof(double)); + if (tb && tb->num > 0 && tb->den > 0) + return mp_pts == MP_NOPTS_VALUE ? AV_NOPTS_VALUE : mp_pts / av_q2d(*tb); + // The + 0.0 is to squash possible negative zero mp_pts, which would + // happen to end up as AV_NOPTS_VALUE. + return (union pts){.d = mp_pts + 0.0}.i; +} + +// Inverse of mp_pts_to_av(). (The timebases must be exactly the same.) +double mp_pts_from_av(int64_t av_pts, AVRational *tb) +{ + assert(sizeof(int64_t) >= sizeof(double)); + if (tb && tb->num > 0 && tb->den > 0) + return av_pts == AV_NOPTS_VALUE ? MP_NOPTS_VALUE : av_pts * av_q2d(*tb); + // Should libavcodec set the PTS to AV_NOPTS_VALUE, it would end up as + // non-sense (usually negative zero) when unwrapped to double. + return av_pts == AV_NOPTS_VALUE ? MP_NOPTS_VALUE : (union pts){.i = av_pts}.d; +} + +// Set dst from mpkt. Note that dst is not refcountable. +// mpkt can be NULL to generate empty packets (used to flush delayed data). +// Sets pts/dts using mp_pts_to_av(ts, tb). (Be aware of the implications.) +// Set duration field only if tb is set. +void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb) +{ + av_init_packet(dst); + dst->data = mpkt ? mpkt->buffer : NULL; + dst->size = mpkt ? mpkt->len : 0; + /* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info + * from demuxer. */ + if (mpkt && mpkt->keyframe) + dst->flags |= AV_PKT_FLAG_KEY; + if (mpkt && mpkt->avpacket) { + dst->side_data = mpkt->avpacket->side_data; + dst->side_data_elems = mpkt->avpacket->side_data_elems; + } + if (mpkt && tb && tb->num > 0 && tb->den > 0) + dst->duration = mpkt->duration / av_q2d(*tb); + dst->pts = mp_pts_to_av(mpkt ? mpkt->pts : MP_NOPTS_VALUE, tb); + dst->dts = mp_pts_to_av(mpkt ? mpkt->dts : MP_NOPTS_VALUE, tb); +} + +void mp_set_avcodec_threads(AVCodecContext *avctx, int threads) +{ + if (threads == 0) { + threads = default_thread_count(); + if (threads < 1) { + mp_msg(MSGT_GLOBAL, MSGL_WARN, "Could not determine " + "thread count to use, defaulting to 1.\n"); + threads = 1; + } + // Apparently some libavcodec versions have or had trouble with more + // than 16 threads, and/or print a warning when using > 16. + threads = MPMIN(threads, 16); + } + avctx->thread_count = threads; +} + +void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type) +{ + AVCodec *cur = NULL; + for (;;) { + cur = av_codec_next(cur); + if (!cur) + break; + if (av_codec_is_decoder(cur) && cur->type == type) { + mp_add_decoder(list, "lavc", mp_codec_from_av_codec_id(cur->id), + cur->name, cur->long_name); + } + } +} + +int mp_codec_to_av_codec_id(const char *codec) +{ + int id = AV_CODEC_ID_NONE; + if (codec) { + const AVCodecDescriptor *desc = avcodec_descriptor_get_by_name(codec); + if (desc) + id = desc->id; + if (id == AV_CODEC_ID_NONE) { + AVCodec *avcodec = avcodec_find_decoder_by_name(codec); + if (avcodec) + id = avcodec->id; + } + } + return id; +} + +const char *mp_codec_from_av_codec_id(int codec_id) +{ + const char *name = NULL; + const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id); + if (desc) + name = desc->name; + if (!name) { + AVCodec *avcodec = avcodec_find_decoder(codec_id); + if (avcodec) + name = avcodec->name; + } + return name; +} diff --git a/common/av_common.h b/common/av_common.h new file mode 100644 index 0000000000..7bf2d64d9e --- /dev/null +++ b/common/av_common.h @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +#ifndef MP_AVCOMMON_H +#define MP_AVCOMMON_H + +#include + +#include +#include +#include + +struct mp_decoder_list; +struct demux_packet; + +void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st); +void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb); +int64_t mp_pts_to_av(double mp_pts, AVRational *tb); +double mp_pts_from_av(int64_t av_pts, AVRational *tb); +void mp_set_avcodec_threads(AVCodecContext *avctx, int threads); +void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type); +int mp_codec_to_av_codec_id(const char *codec); +const char *mp_codec_from_av_codec_id(int codec_id); + +#endif diff --git a/common/av_log.c b/common/av_log.c new file mode 100644 index 0000000000..ba47500572 --- /dev/null +++ b/common/av_log.c @@ -0,0 +1,176 @@ +/* + * av_log to mp_msg converter + * Copyright (C) 2006 Michael Niedermayer + * Copyright (C) 2009 Uoti Urpala + * + * 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. + */ + +#include +#include + +#include "av_log.h" +#include "config.h" +#include "common/msg.h" +#include +#include + +#include +#include +#include + +#if HAVE_LIBAVDEVICE +#include +#endif + +#if HAVE_LIBAVFILTER +#include +#endif + +#if HAVE_LIBAVRESAMPLE +#include +#endif +#if HAVE_LIBSWRESAMPLE +#include +#endif + +static int av_log_level_to_mp_level(int av_level) +{ + if (av_level > AV_LOG_VERBOSE) + return MSGL_DBG2; + if (av_level > AV_LOG_INFO) + return MSGL_V; + if (av_level > AV_LOG_WARNING) + return MSGL_V; + if (av_level > AV_LOG_ERROR) + return MSGL_WARN; + if (av_level > AV_LOG_FATAL) + return MSGL_ERR; + return MSGL_FATAL; +} + +static int extract_msg_type_from_ctx(void *ptr) +{ + if (!ptr) + return MSGT_FIXME; + + AVClass *avc = *(AVClass **)ptr; + if (!avc) { + mp_msg(MSGT_FIXME, MSGL_WARN, + "av_log callback called with bad parameters (NULL AVClass).\n" + "This is a bug in one of Libav/FFmpeg libraries used.\n"); + return MSGT_FIXME; + } + + if (!strcmp(avc->class_name, "AVCodecContext")) { + AVCodecContext *s = ptr; + if (s->codec) { + if (s->codec->type == AVMEDIA_TYPE_AUDIO) { + if (s->codec->decode) + return MSGT_DECAUDIO; + } else if (s->codec->type == AVMEDIA_TYPE_VIDEO) { + if (s->codec->decode) + return MSGT_DECVIDEO; + } + // FIXME subtitles, encoders + // What msgt for them? There is nothing appropriate... + } + return MSGT_FIXME; + } + + if (!strcmp(avc->class_name, "AVFormatContext")) { + AVFormatContext *s = ptr; + if (s->iformat) + return MSGT_DEMUXER; + else if (s->oformat) + return MSGT_MUXER; + return MSGT_FIXME; + } + + return MSGT_FIXME; +} + +#if LIBAVCODEC_VERSION_MICRO >= 100 +#define LIB_PREFIX "ffmpeg" +#else +#define LIB_PREFIX "libav" +#endif + +static bool print_prefix = true; + +static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt, + va_list vl) +{ + AVClass *avc = ptr ? *(AVClass **)ptr : NULL; + int mp_level = av_log_level_to_mp_level(level); + int type = extract_msg_type_from_ctx(ptr); + + if (!mp_msg_test(type, mp_level)) + return; + + if (print_prefix) { + mp_msg(type, mp_level, "[%s/%s] ", LIB_PREFIX, + avc ? avc->item_name(ptr) : "?"); + } + print_prefix = fmt[strlen(fmt) - 1] == '\n'; + + mp_msg_va(type, mp_level, fmt, vl); +} + +void init_libav(void) +{ + av_log_set_callback(mp_msg_av_log_callback); + avcodec_register_all(); + av_register_all(); + avformat_network_init(); + +#if HAVE_LIBAVFILTER + avfilter_register_all(); +#endif +#if HAVE_LIBAVDEVICE + avdevice_register_all(); +#endif +} + +#define V(x) (x)>>16, (x)>>8 & 255, (x) & 255 +static void print_version(int v, char *name, unsigned buildv, unsigned runv) +{ + mp_msg(MSGT_CPLAYER, v, " %-15s %d.%d.%d", name, V(buildv)); + if (buildv != runv) + mp_msg(MSGT_CPLAYER, v, " (runtime %d.%d.%d)", V(runv)); + mp_msg(MSGT_CPLAYER, v, "\n"); +} +#undef V + +void print_libav_versions(int v) +{ + mp_msg(MSGT_CPLAYER, v, "%s library versions:\n", LIB_PREFIX); + + print_version(v, "libavutil", LIBAVUTIL_VERSION_INT, avutil_version()); + print_version(v, "libavcodec", LIBAVCODEC_VERSION_INT, avcodec_version()); + print_version(v, "libavformat", LIBAVFORMAT_VERSION_INT, avformat_version()); + print_version(v, "libswscale", LIBSWSCALE_VERSION_INT, swscale_version()); +#if HAVE_LIBAVFILTER + print_version(v, "libavfilter", LIBAVFILTER_VERSION_INT, avfilter_version()); +#endif +#if HAVE_LIBAVRESAMPLE + print_version(v, "libavresample", LIBAVRESAMPLE_VERSION_INT, avresample_version()); +#endif +#if HAVE_LIBSWRESAMPLE + print_version(v, "libswresample", LIBSWRESAMPLE_VERSION_INT, swresample_version()); +#endif +} diff --git a/common/av_log.h b/common/av_log.h new file mode 100644 index 0000000000..d5c57b0aeb --- /dev/null +++ b/common/av_log.h @@ -0,0 +1,2 @@ +void init_libav(void); +void print_libav_versions(int v); diff --git a/common/av_opts.c b/common/av_opts.c new file mode 100644 index 0000000000..777a1eec5a --- /dev/null +++ b/common/av_opts.c @@ -0,0 +1,55 @@ +/* + * AVOption parsing helper + * Copyright (C) 2008 Michael Niedermayer + * + * 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. + */ + +#include +#include + +#include + +#include "av_opts.h" + +int parse_avopts(void *v, char *str){ + char *start; + + if (!str) + return 0; + + start= str= strdup(str); + + while(str && *str){ + char *next_opt, *arg; + + next_opt= strchr(str, ','); + if(next_opt) *next_opt++= 0; + + arg = strchr(str, '='); + if(arg) *arg++= 0; + + if (av_opt_set(v, str, arg, AV_OPT_SEARCH_CHILDREN) < 0) { + free(start); + return -1; + } + str= next_opt; + } + + free(start); + return 0; +} diff --git a/common/av_opts.h b/common/av_opts.h new file mode 100644 index 0000000000..640443a352 --- /dev/null +++ b/common/av_opts.h @@ -0,0 +1,30 @@ +/* + * AVOption parsing helper + * Copyright (C) 2008 Michael Niedermayer + * + * 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. + */ + +#ifndef MPLAYER_AV_OPTS_H +#define MPLAYER_AV_OPTS_H + +/** + * Parses str and sets AVOptions in v accordingly. + */ +int parse_avopts(void *v, char *str); + +#endif /* MPLAYER_AV_OPTS_H */ diff --git a/common/codecs.c b/common/codecs.c new file mode 100644 index 0000000000..b7639a4576 --- /dev/null +++ b/common/codecs.c @@ -0,0 +1,147 @@ +/* + * 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 . + */ + +#include +#include "talloc.h" +#include "bstr/bstr.h" +#include "common/msg.h" +#include "codecs.h" + +void mp_add_decoder(struct mp_decoder_list *list, const char *family, + const char *codec, const char *decoder, const char *desc) +{ + struct mp_decoder_entry entry = { + .family = talloc_strdup(list, family), + .codec = talloc_strdup(list, codec), + .decoder = talloc_strdup(list, decoder), + .desc = talloc_strdup(list, desc), + }; + MP_TARRAY_APPEND(list, list->entries, list->num_entries, entry); +} + +static void mp_add_decoder_entry(struct mp_decoder_list *list, + struct mp_decoder_entry *entry) +{ + mp_add_decoder(list, entry->family, entry->codec, entry->decoder, + entry->desc); +} + +static struct mp_decoder_entry *find_decoder(struct mp_decoder_list *list, + bstr family, bstr decoder) +{ + for (int n = 0; n < list->num_entries; n++) { + struct mp_decoder_entry *cur = &list->entries[n]; + if (bstr_equals0(decoder, cur->decoder) && + bstr_equals0(family, cur->family)) + return cur; + } + return NULL; +} + +// Add entry, but only if it's not yet on the list, and if the codec matches. +// If codec == NULL, don't compare codecs. +static void add_new(struct mp_decoder_list *to, struct mp_decoder_entry *entry, + const char *codec) +{ + if (!entry || (codec && strcmp(entry->codec, codec) != 0)) + return; + if (!find_decoder(to, bstr0(entry->family), bstr0(entry->decoder))) + mp_add_decoder_entry(to, entry); +} + +// Select a decoder from the given list for the given codec. The selection +// can be influenced by the selection string, which can specify a priority +// list of preferred decoders. +// This returns a list of decoders to try, with the preferred decoders first. +// The selection string corresponds to --vd/--ad directly, and has the +// following syntax: +// selection = [ ("," )*] +// entry = ":" // prefer decoder +// entry = ":*" // prefer all decoders +// entry = "+" ":" // force a decoder +// entry = "-" ":" // exclude a decoder +// entry = "-" // don't add fallback decoders +// Forcing a decoder means it's added even if the codec mismatches. +struct mp_decoder_list *mp_select_decoders(struct mp_decoder_list *all, + const char *codec, + const char *selection) +{ + struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list); + struct mp_decoder_list *remove = talloc_zero(NULL, struct mp_decoder_list); + if (!codec) + codec = "unknown"; + bool stop = false; + bstr sel = bstr0(selection); + while (sel.len) { + bstr entry; + bstr_split_tok(sel, ",", &entry, &sel); + if (bstr_equals0(entry, "-")) { + stop = true; + break; + } + bool force = bstr_eatstart0(&entry, "+"); + bool exclude = !force && bstr_eatstart0(&entry, "-"); + struct mp_decoder_list *dest = exclude ? remove : list; + bstr family, decoder; + if (!bstr_split_tok(entry, ":", &family, &decoder)) { + mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Decoders must be specified as " + "'family:decoder' for the --ad/--vd options.\n"); + break; + } + if (bstr_equals0(decoder, "*")) { + for (int n = 0; n < all->num_entries; n++) { + struct mp_decoder_entry *cur = &all->entries[n]; + if (bstr_equals0(family, cur->family)) + add_new(dest, cur, codec); + } + } else { + add_new(dest, find_decoder(all, family, decoder), + force ? NULL : codec); + } + } + if (!stop) { + // Add the remaining codecs which haven't been added yet + for (int n = 0; n < all->num_entries; n++) + add_new(list, &all->entries[n], codec); + } + for (int n = 0; n < remove->num_entries; n++) { + struct mp_decoder_entry *ex = &remove->entries[n]; + struct mp_decoder_entry *del = + find_decoder(list, bstr0(ex->family), bstr0(ex->decoder)); + if (del) { + int index = del - &list->entries[0]; + MP_TARRAY_REMOVE_AT(list->entries, list->num_entries, index); + } + } + talloc_free(remove); + return list; +} + +void mp_print_decoders(int msgt, int msgl, const char *header, + struct mp_decoder_list *list) +{ + mp_msg(msgt, msgl, "%s\n", header); + for (int n = 0; n < list->num_entries; n++) { + struct mp_decoder_entry *entry = &list->entries[n]; + mp_msg(msgt, msgl, " %s:%s", entry->family, entry->decoder); + if (strcmp(entry->decoder, entry->codec) != 0) + mp_msg(msgt, msgl, " (%s)", entry->codec); + mp_msg(msgt, msgl, " - %s\n", entry->desc); + } + if (list->num_entries == 0) + mp_msg(msgt, msgl, " (no decoders)\n"); +} diff --git a/common/codecs.h b/common/codecs.h new file mode 100644 index 0000000000..21ff284617 --- /dev/null +++ b/common/codecs.h @@ -0,0 +1,43 @@ +/* + * 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 . + */ + +#ifndef MP_CODECS_H +#define MP_CODECS_H + +struct mp_decoder_entry { + const char *family; // decoder module (e.g. ad_lavc => "lavc") + const char *codec; // name of the codec (e.g. "mp3") + const char *decoder; // decoder name (e.g. "mp3float") + const char *desc; // human readable description +}; + +struct mp_decoder_list { + struct mp_decoder_entry *entries; + int num_entries; +}; + +void mp_add_decoder(struct mp_decoder_list *list, const char *family, + const char *codec, const char *decoder, const char *desc); + +struct mp_decoder_list *mp_select_decoders(struct mp_decoder_list *all, + const char *codec, + const char *selection); + +void mp_print_decoders(int msgt, int msgl, const char *header, + struct mp_decoder_list *list); + +#endif diff --git a/common/common.c b/common/common.c new file mode 100644 index 0000000000..365a369425 --- /dev/null +++ b/common/common.c @@ -0,0 +1,163 @@ +/* + * 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. + */ + +#include + +#include "talloc.h" +#include "bstr/bstr.h" +#include "common/common.h" + +#define appendf(ptr, ...) \ + do {(*(ptr)) = talloc_asprintf_append_buffer(*(ptr), __VA_ARGS__);} while(0) + +// Return a talloc'ed string formatted according to the format string in fmt. +// On error, return NULL. +// Valid formats: +// %H, %h: hour (%H is padded with 0 to two digits) +// %M: minutes from 00-59 (hours are subtracted) +// %m: total minutes (includes hours, unlike %M) +// %S: seconds from 00-59 (minutes and hours are subtracted) +// %s: total seconds (includes hours and minutes) +// %f: like %s, but as float +// %T: milliseconds (000-999) +char *mp_format_time_fmt(const char *fmt, double time) +{ + if (time == MP_NOPTS_VALUE) + return talloc_strdup(NULL, "unknown"); + char *sign = time < 0 ? "-" : ""; + time = time < 0 ? -time : time; + long long int itime = time; + long long int h, m, tm, s; + int ms; + s = itime; + tm = s / 60; + h = s / 3600; + s -= h * 3600; + m = s / 60; + s -= m * 60; + ms = (time - itime) * 1000; + char *res = talloc_strdup(NULL, ""); + while (*fmt) { + if (fmt[0] == '%') { + fmt++; + switch (fmt[0]) { + case 'h': appendf(&res, "%s%lld", sign, h); break; + case 'H': appendf(&res, "%s%02lld", sign, h); break; + case 'm': appendf(&res, "%s%lld", sign, tm); break; + case 'M': appendf(&res, "%02lld", m); break; + case 's': appendf(&res, "%s%lld", sign, itime); break; + case 'S': appendf(&res, "%02lld", s); break; + case 'T': appendf(&res, "%03d", ms); break; + case '%': appendf(&res, "%s", "%"); break; + default: goto error; + } + fmt++; + } else { + appendf(&res, "%c", *fmt); + fmt++; + } + } + return res; +error: + talloc_free(res); + return NULL; +} + +char *mp_format_time(double time, bool fractions) +{ + return mp_format_time_fmt(fractions ? "%H:%M:%S.%T" : "%H:%M:%S", time); +} + +// Set rc to the union of rc and rc2 +void mp_rect_union(struct mp_rect *rc, const struct mp_rect *rc2) +{ + rc->x0 = FFMIN(rc->x0, rc2->x0); + rc->y0 = FFMIN(rc->y0, rc2->y0); + rc->x1 = FFMAX(rc->x1, rc2->x1); + rc->y1 = FFMAX(rc->y1, rc2->y1); +} + +// Set rc to the intersection of rc and src. +// Return false if the result is empty. +bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2) +{ + rc->x0 = FFMAX(rc->x0, rc2->x0); + rc->y0 = FFMAX(rc->y0, rc2->y0); + rc->x1 = FFMIN(rc->x1, rc2->x1); + rc->y1 = FFMIN(rc->y1, rc2->y1); + + return rc->x1 > rc->x0 && rc->y1 > rc->y0; +} + +// Encode the unicode codepoint as UTF-8, and append to the end of the +// talloc'ed buffer. +char *mp_append_utf8_buffer(char *buffer, uint32_t codepoint) +{ + char data[8]; + uint8_t tmp; + char *output = data; + PUT_UTF8(codepoint, tmp, *output++ = tmp;); + return talloc_strndup_append_buffer(buffer, data, output - data); +} + +// Parse a C-style escape beginning at code, and append the result to *str +// using talloc. The input string (*code) must point to the first character +// after the initial '\', and after parsing *code is set to the first character +// after the current escape. +// On error, false is returned, and all input remains unchanged. +bool mp_parse_escape(bstr *code, char **str) +{ + if (code->len < 1) + return false; + char replace = 0; + switch (code->start[0]) { + case '"': replace = '"'; break; + case '\\': replace = '\\'; break; + case 'b': replace = '\b'; break; + case 'f': replace = '\f'; break; + case 'n': replace = '\n'; break; + case 'r': replace = '\r'; break; + case 't': replace = '\t'; break; + case 'e': replace = '\x1b'; break; + case '\'': replace = '\''; break; + } + if (replace) { + *str = talloc_strndup_append_buffer(*str, &replace, 1); + *code = bstr_cut(*code, 1); + return true; + } + if (code->start[0] == 'x' && code->len >= 3) { + bstr num = bstr_splice(*code, 1, 3); + char c = bstrtoll(num, &num, 16); + if (!num.len) + return false; + *str = talloc_strndup_append_buffer(*str, &c, 1); + *code = bstr_cut(*code, 3); + return true; + } + if (code->start[0] == 'u' && code->len >= 5) { + bstr num = bstr_splice(*code, 1, 5); + int c = bstrtoll(num, &num, 16); + if (num.len) + return false; + *str = mp_append_utf8_buffer(*str, c); + *code = bstr_cut(*code, 5); + return true; + } + return false; +} diff --git a/common/common.h b/common/common.h new file mode 100644 index 0000000000..9e751ee851 --- /dev/null +++ b/common/common.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef MPLAYER_MPCOMMON_H +#define MPLAYER_MPCOMMON_H + +#include +#include +#include + +#include "compat/compiler.h" +#include "talloc.h" + +// both int64_t and double should be able to represent this exactly +#define MP_NOPTS_VALUE (-1LL<<63) + +#define MP_CONCAT_(a, b) a ## b +#define MP_CONCAT(a, b) MP_CONCAT_(a, b) + +#define ROUND(x) ((int)((x) < 0 ? (x) - 0.5 : (x) + 0.5)) + +#define MPMAX(a, b) ((a) > (b) ? (a) : (b)) +#define MPMIN(a, b) ((a) > (b) ? (b) : (a)) +#define MPCLAMP(a, min, max) (((a) < (min)) ? (min) : (((a) > (max)) ? (max) : (a))) +#define MPSWAP(type, a, b) \ + do { type SWAP_tmp = b; b = a; a = SWAP_tmp; } while (0) +#define MP_ARRAY_SIZE(s) (sizeof(s) / sizeof((s)[0])) + +// align must be a power of two (align >= 1), x >= 0 +#define MP_ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1)) +#define MP_ALIGN_DOWN(x, align) ((x) & ~((align) - 1)) + +#define CONTROL_OK 1 +#define CONTROL_TRUE 1 +#define CONTROL_FALSE 0 +#define CONTROL_UNKNOWN -1 +#define CONTROL_ERROR -2 +#define CONTROL_NA -3 + +enum stream_type { + STREAM_VIDEO, + STREAM_AUDIO, + STREAM_SUB, + STREAM_TYPE_COUNT, +}; + +extern const char *mplayer_version; +extern const char *mplayer_builddate; + +char *mp_format_time(double time, bool fractions); +char *mp_format_time_fmt(const char *fmt, double time); + +struct mp_rect { + int x0, y0; + int x1, y1; +}; + +void mp_rect_union(struct mp_rect *rc, const struct mp_rect *src); +bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2); + +char *mp_append_utf8_buffer(char *buffer, uint32_t codepoint); + +struct bstr; +bool mp_parse_escape(struct bstr *code, char **str); + +#endif /* MPLAYER_MPCOMMON_H */ diff --git a/common/cpudetect.c b/common/cpudetect.c new file mode 100644 index 0000000000..ea28cf4dd4 --- /dev/null +++ b/common/cpudetect.c @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#include +#include +#include + +#include +#include "compat/libav.h" + +#include "config.h" +#include "common/cpudetect.h" +#include "common/msg.h" + +CpuCaps gCpuCaps; + +static void dump_flag(const char *name, bool val) +{ + mp_msg(MSGT_CPUDETECT, MSGL_V, "CPU: %s: %s\n", name, + val ? "enabled" : "disabled"); +} + +void GetCpuCaps(CpuCaps *c) +{ + memset(c, 0, sizeof(*c)); + int flags = av_get_cpu_flags(); +#if ARCH_X86 + c->hasMMX = flags & AV_CPU_FLAG_MMX; + c->hasMMX2 = flags & AV_CPU_FLAG_MMX2; + c->hasSSE = flags & AV_CPU_FLAG_SSE; + c->hasSSE2 = (flags & AV_CPU_FLAG_SSE2) && !(flags & AV_CPU_FLAG_SSE2SLOW); + c->hasSSE3 = (flags & AV_CPU_FLAG_SSE3) && !(flags & AV_CPU_FLAG_SSE3SLOW); + c->hasSSSE3 = flags & AV_CPU_FLAG_SSSE3; +#endif + dump_flag("MMX", c->hasMMX); + dump_flag("MMX2", c->hasMMX2); + dump_flag("SSE", c->hasSSE); + dump_flag("SSE2", c->hasSSE2); + dump_flag("SSE3", c->hasSSE3); + dump_flag("SSSE3", c->hasSSSE3); +} diff --git a/common/cpudetect.h b/common/cpudetect.h new file mode 100644 index 0000000000..d3d9206c65 --- /dev/null +++ b/common/cpudetect.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef MPLAYER_CPUDETECT_H +#define MPLAYER_CPUDETECT_H + +#include +#include "config.h" + +#include "compat/x86_cpu.h" + +typedef struct cpucaps_s { + bool hasMMX; + bool hasMMX2; + bool hasSSE; + bool hasSSE2; + bool hasSSE3; + bool hasSSSE3; +} CpuCaps; + +extern CpuCaps gCpuCaps; + +void GetCpuCaps(CpuCaps *caps); + +#endif /* MPLAYER_CPUDETECT_H */ diff --git a/common/encode.h b/common/encode.h new file mode 100644 index 0000000000..fec14045ed --- /dev/null +++ b/common/encode.h @@ -0,0 +1,21 @@ +#ifndef MPLAYER_ENCODE_H +#define MPLAYER_ENCODE_H + +#include + +struct MPOpts; +struct encode_lavc_context; +struct encode_output_conf; + +// interface for mplayer.c +struct encode_lavc_context *encode_lavc_init(struct encode_output_conf *options); +void encode_lavc_finish(struct encode_lavc_context *ctx); +void encode_lavc_free(struct encode_lavc_context *ctx); +void encode_lavc_discontinuity(struct encode_lavc_context *ctx); +bool encode_lavc_showhelp(struct MPOpts *opts); +int encode_lavc_getstatus(struct encode_lavc_context *ctx, char *buf, int bufsize, float relative_position); +void encode_lavc_expect_stream(struct encode_lavc_context *ctx, int mt); +void encode_lavc_set_video_fps(struct encode_lavc_context *ctx, float fps); +bool encode_lavc_didfail(struct encode_lavc_context *ctx); // check if encoding failed + +#endif diff --git a/common/encode_lavc.c b/common/encode_lavc.c new file mode 100644 index 0000000000..97a45ccbbe --- /dev/null +++ b/common/encode_lavc.c @@ -0,0 +1,1115 @@ +/* + * muxing using libavformat + * Copyright (C) 2010 Nicolas George + * Copyright (C) 2011-2012 Rudolf Polzer + * + * This file is part of mpv. + * + * 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. + */ + +#include + +#include "encode_lavc.h" +#include "common/msg.h" +#include "video/vfcap.h" +#include "options/options.h" +#include "osdep/timer.h" +#include "video/out/vo.h" +#include "talloc.h" +#include "stream/stream.h" + +static int set_to_avdictionary(AVDictionary **dictp, const char *key, + const char *val) +{ + char keybuf[1024]; + char valuebuf[1024]; + + if (key == NULL) { + // we need to split at equals sign + const char *equals = strchr(val, '='); + if (!equals || equals - val >= sizeof(keybuf)) { + mp_msg(MSGT_ENCODE, MSGL_WARN, + "encode-lavc: option '%s' does not contain an equals sign\n", + val); + return 0; + } + memcpy(keybuf, val, equals - val); + keybuf[equals - val] = 0; + key = keybuf; + val = equals + 1; + } + + // hack: support "qscale" key as virtual "global_quality" key that multiplies by QP2LAMBDA + if (!strcmp(key, "qscale")) { + key = "global_quality"; + snprintf(valuebuf, sizeof(valuebuf), + "%.1s(%s)*QP2LAMBDA", + (val[0] == '+' || val[0] == '-') ? val : "", + (val[0] == '+' || val[0] == '-') ? val + 1 : val); + valuebuf[sizeof(valuebuf) - 1] = 0; + val = valuebuf; + } + + mp_msg(MSGT_ENCODE, MSGL_V, + "encode-lavc: setting value '%s' for key '%s'\n", + val, + key); + + if (av_dict_set(dictp, key, *val ? val : NULL, + (val[0] == '+' || val[0] == '-') ? AV_DICT_APPEND : 0) >= 0) + return 1; + + return 0; +} + +static bool value_has_flag(const char *value, const char *flag) +{ + bool state = true; + bool ret = false; + while (*value) { + size_t l = strcspn(value, "+-"); + if (l == 0) { + state = (*value == '+'); + ++value; + } else { + if (l == strlen(flag)) + if (!memcmp(value, flag, l)) + ret = state; + value += l; + } + } + return ret; +} + +#define CHECK_FAIL(ctx, val) \ + if (ctx && (ctx->failed || ctx->finished)) { \ + mp_msg(MSGT_ENCODE, MSGL_ERR, \ + "Called a function on a %s encoding context. Bailing out.\n", \ + ctx->failed ? "failed" : "finished"); \ + return val; \ + } + +int encode_lavc_available(struct encode_lavc_context *ctx) +{ + CHECK_FAIL(ctx, 0); + return ctx && ctx->avc; +} + +int encode_lavc_oformat_flags(struct encode_lavc_context *ctx) +{ + CHECK_FAIL(ctx, 0); + return ctx->avc ? ctx->avc->oformat->flags : 0; +} + +struct encode_lavc_context *encode_lavc_init(struct encode_output_conf *options) +{ + struct encode_lavc_context *ctx; + const char *filename = options->file; + + // STUPID STUPID STUPID STUPID avio + // does not support "-" as file name to mean stdin/stdout + // ffmpeg.c works around this too, the same way + if (!strcmp(filename, "-")) + filename = "pipe:1"; + + if (filename && ( + !strcmp(filename, "/dev/stdout") || + !strcmp(filename, "pipe:") || + !strcmp(filename, "pipe:1"))) + mp_msg_stdout_in_use = 1; + + ctx = talloc_zero(NULL, struct encode_lavc_context); + encode_lavc_discontinuity(ctx); + ctx->options = options; + + ctx->avc = avformat_alloc_context(); + + if (ctx->options->format) { + char *tok; + const char *in = ctx->options->format; + while (*in) { + tok = av_get_token(&in, ","); + ctx->avc->oformat = av_guess_format(tok, filename, NULL); + av_free(tok); + if (ctx->avc->oformat) + break; + if (*in) + ++in; + } + } else + ctx->avc->oformat = av_guess_format(NULL, filename, NULL); + + if (!ctx->avc->oformat) { + encode_lavc_fail(ctx, "encode-lavc: format not found\n"); + return NULL; + } + + av_strlcpy(ctx->avc->filename, filename, + sizeof(ctx->avc->filename)); + + ctx->foptions = NULL; + if (ctx->options->fopts) { + char **p; + for (p = ctx->options->fopts; *p; ++p) { + if (!set_to_avdictionary(&ctx->foptions, NULL, *p)) + mp_msg(MSGT_ENCODE, MSGL_WARN, + "encode-lavc: could not set option %s\n", *p); + } + } + + if (ctx->options->vcodec) { + char *tok; + const char *in = ctx->options->vcodec; + while (*in) { + tok = av_get_token(&in, ","); + ctx->vc = avcodec_find_encoder_by_name(tok); + av_free(tok); + if (ctx->vc && ctx->vc->type != AVMEDIA_TYPE_VIDEO) + ctx->vc = NULL; + if (ctx->vc) + break; + if (*in) + ++in; + } + } else + ctx->vc = avcodec_find_encoder(av_guess_codec(ctx->avc->oformat, NULL, + ctx->avc->filename, NULL, + AVMEDIA_TYPE_VIDEO)); + + if (ctx->options->acodec) { + char *tok; + const char *in = ctx->options->acodec; + while (*in) { + tok = av_get_token(&in, ","); + ctx->ac = avcodec_find_encoder_by_name(tok); + av_free(tok); + if (ctx->ac && ctx->ac->type != AVMEDIA_TYPE_AUDIO) + ctx->ac = NULL; + if (ctx->ac) + break; + if (*in) + ++in; + } + } else + ctx->ac = avcodec_find_encoder(av_guess_codec(ctx->avc->oformat, NULL, + ctx->avc->filename, NULL, + AVMEDIA_TYPE_AUDIO)); + + if (!ctx->vc && !ctx->ac) { + encode_lavc_fail( + ctx, "encode-lavc: neither audio nor video codec was found\n"); + return NULL; + } + + /* taken from ffmpeg unchanged + * TODO turn this into an option if anyone needs this */ + + ctx->avc->max_delay = 0.7 * AV_TIME_BASE; + + ctx->abytes = 0; + ctx->vbytes = 0; + ctx->frames = 0; + + if (options->video_first) + ctx->video_first = true; + if (options->audio_first) + ctx->audio_first = true; + + return ctx; +} + +int encode_lavc_start(struct encode_lavc_context *ctx) +{ + AVDictionaryEntry *de; + unsigned i; + + if (ctx->header_written < 0)