view m_property.c @ 24589:9118be6575da

demux_audio.c: Fix timestamp handling The code calculated the pts values of audio packets by adding the length of the current packet to the pts of the previous one. The length of the previous packet should be added instead. This broke WAV timestamps near the end of the stream where a short packet occurs. Change the code to store the pts of the next packet instead of the last one. This fixes the WAV timestamps and allows some simplifications. MP3 timestamps are not affected as packets are always treated as constant decoded length, and FLAC timestamps still have worse problems (FLAC is treated as as if it was constant bitrate even though it isn't). Also store the timestamps as double instead of float.
author uau
date Mon, 24 Sep 2007 21:49:56 +0000
parents ae4237717601
children 3086b6d39052
line wrap: on
line source


/// \file
/// \ingroup Properties

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>

#include "m_option.h"
#include "m_property.h"
#include "mp_msg.h"
#include "help_mp.h"

#define ROUND(x) ((int)((x)<0 ? (x)-0.5 : (x)+0.5))

static int do_action(m_option_t* prop_list, const char* name,
                     int action, void* arg, void *ctx) {
    const char* sep;
    m_option_t* prop;
    m_property_action_t ka;
    int r;
    if((sep = strchr(name,'/')) && sep[1]) {
        int len = sep-name;
        char base[len+1];
        memcpy(base,name,len);
        base[len] = 0;
        prop = m_option_list_find(prop_list, base);
        ka.key = sep+1;
        ka.action = action;
        ka.arg = arg;
        action = M_PROPERTY_KEY_ACTION;
        arg = &ka;
    } else
        prop = m_option_list_find(prop_list, name);
    if(!prop) return M_PROPERTY_UNKNOWN;
    r = ((m_property_ctrl_f)prop->p)(prop,action,arg,ctx);
    if(action == M_PROPERTY_GET_TYPE && r < 0) {
        if(!arg) return M_PROPERTY_ERROR;
        *(m_option_t**)arg = prop;
        return M_PROPERTY_OK;
    }
    return r;
}

int m_property_do(m_option_t* prop_list, const char* name,
                  int action, void* arg, void *ctx) {
    m_option_t* opt;
    void* val;
    char* str;
    int r;

    switch(action) {
    case M_PROPERTY_PRINT:
        if((r = do_action(prop_list,name,M_PROPERTY_PRINT,arg,ctx)) >= 0)
            return r;
        // fallback on the default print for this type
    case M_PROPERTY_TO_STRING:
        if((r = do_action(prop_list,name,M_PROPERTY_TO_STRING,arg,ctx)) !=
           M_PROPERTY_NOT_IMPLEMENTED)
            return r;
        // fallback on the options API. Get the type, value and print.
        if((r = do_action(prop_list,name,M_PROPERTY_GET_TYPE,&opt,ctx)) <= 0)
            return r;
        val = calloc(1,opt->type->size);
        if((r = do_action(prop_list,name,M_PROPERTY_GET,val,ctx)) <= 0) {
            free(val);
            return r;
        }
        if(!arg) return M_PROPERTY_ERROR;
        str = m_option_print(opt,val);
        free(val);
        *(char**)arg = str == (char*)-1 ? NULL : str;
        return str != (char*)-1;
    case M_PROPERTY_PARSE:
        // try the property own parsing func
        if((r = do_action(prop_list,name,M_PROPERTY_PARSE,arg,ctx)) !=
           M_PROPERTY_NOT_IMPLEMENTED)
            return r;
        // fallback on the options API, get the type and parse.
        if((r = do_action(prop_list,name,M_PROPERTY_GET_TYPE,&opt,ctx)) <= 0)
            return r;
        if(!arg) return M_PROPERTY_ERROR;
        val = calloc(1,opt->type->size);
        if((r = m_option_parse(opt,opt->name,arg,val,M_CONFIG_FILE)) <= 0) {
            free(val);
            return r;
        }
        r = do_action(prop_list,name,M_PROPERTY_SET,val,ctx);
        m_option_free(opt,val);
        free(val);
        return r;
    }
    return do_action(prop_list,name,action,arg,ctx);
}

char* m_properties_expand_string(m_option_t* prop_list,char* str, void *ctx) {
    int l,fr=0,pos=0,size=strlen(str)+512;
    char *p = NULL,*e,*ret = malloc(size), num_val;
    int skip = 0, lvl = 0, skip_lvl = 0;
    
    while(str[0]) {
        if(str[0] == '\\') {
            int sl = 1;
            switch(str[1]) {
            case 'e':
                p = "\x1b", l = 1; break;
            case 'n':
                p = "\n", l = 1; break;
            case 'r':
                p = "\r", l = 1; break;
            case 't':
                p = "\t", l = 1; break;
            case 'x': 
                if(str[2]) {
                    char num[3] = { str[2], str[3], 0 };
                    char* end = num;
                    num_val = strtol(num,&end,16);
                    sl = end-num;
                    l = 1;
                    p = &num_val;
                } else
                    l = 0;
                break;
            default:
                p = str+1, l = 1;
            }
            str+=1+sl;
        } else if(lvl > 0 && str[0] == ')') {
            if(skip && lvl <= skip_lvl) skip = 0;
            lvl--, str++, l = 0;
        } else if(str[0] == '$' && str[1] == '{' && (e = strchr(str+2,'}'))) {
            int pl = e-str-2;
            char pname[pl+1];
            memcpy(pname,str+2,pl);
            pname[pl] = 0;
            if(m_property_do(prop_list, pname,
                             M_PROPERTY_PRINT, &p, ctx) >= 0 && p)
                l = strlen(p), fr = 1;
            else
                l = 0;
            str = e+1;
        } else if(str[0] == '?' && str[1] == '(' && (e = strchr(str+2,':'))) {
            int pl = e-str-2;
            char pname[pl+1];
            lvl++;
            if(!skip) {            
                memcpy(pname,str+2,pl);
                pname[pl] = 0;
                if(m_property_do(prop_list,pname,M_PROPERTY_GET,NULL,ctx) < 0)
                    skip = 1, skip_lvl = lvl;
            }
            str = e+1, l = 0;
        } else
            p = str, l = 1, str++;
        
        if(skip || l <= 0) continue;
        
        if(pos+l+1 > size) {
            size = pos+l+512;
            ret = realloc(ret,size);
        }
        memcpy(ret+pos,p,l);
        pos += l;
        if(fr) free(p), fr = 0;
    }
    
    ret[pos] = 0;
    return ret;
}

void m_properties_print_help_list(m_option_t* list) {
    char min[50],max[50];
    int i,count = 0;
    
    mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_PropertyListHeader);
    for(i = 0 ; list[i].name ; i++) {
        m_option_t* opt = &list[i];
        if(opt->flags & M_OPT_MIN)
            sprintf(min,"%-8.0f",opt->min);
        else
            strcpy(min,"No");
        if(opt->flags & M_OPT_MAX)
            sprintf(max,"%-8.0f",opt->max);
        else
            strcpy(max,"No");
        mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-20.20s %-15.15s %-10.10s %-10.10s\n",
               opt->name,
               opt->type->name,
               min,
               max);
        count++;
    }
    mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_TotalProperties, count);
}

// Some generic property implementations

int m_property_int_ro(m_option_t* prop,int action,
                      void* arg,int var) {
    switch(action) {
    case M_PROPERTY_GET:
        if(!arg) return 0;
        *(int*)arg = var;
        return 1;
    }
    return M_PROPERTY_NOT_IMPLEMENTED;
}

int m_property_int_range(m_option_t* prop,int action,
                         void* arg,int* var) {
    switch(action) {
    case M_PROPERTY_SET:
        if(!arg) return 0;
        M_PROPERTY_CLAMP(prop,*(int*)arg);
        *var = *(int*)arg;
        return 1;
    case M_PROPERTY_STEP_UP:
    case M_PROPERTY_STEP_DOWN:
        *var += (arg ? *(int*)arg : 1) *
            (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
        M_PROPERTY_CLAMP(prop,*var);
        return 1;
    }
    return m_property_int_ro(prop,action,arg,*var);
}

int m_property_choice(m_option_t* prop,int action,
                      void* arg,int* var) {
    switch(action) {
    case M_PROPERTY_STEP_UP:
    case M_PROPERTY_STEP_DOWN:
        *var += action == M_PROPERTY_STEP_UP ? 1 : prop->max;
        *var %= (int)prop->max+1;
        return 1;
    }
    return m_property_int_range(prop,action,arg,var);
}

int m_property_flag(m_option_t* prop,int action,
                    void* arg,int* var) {
    switch(action) {
    case M_PROPERTY_STEP_UP:
    case M_PROPERTY_STEP_DOWN:
        *var = *var == prop->min ? prop->max : prop->min;
        return 1;
    case M_PROPERTY_PRINT:
        if(!arg) return 0;
        *(char**)arg = strdup((*var > prop->min) ? MSGTR_Enabled : MSGTR_Disabled);
        return 1;
    }
    return m_property_int_range(prop,action,arg,var);
}

int m_property_float_ro(m_option_t* prop,int action,
                        void* arg,float var) {
    switch(action) {
    case M_PROPERTY_GET:
        if(!arg) return 0;
        *(float*)arg = var;
        return 1;
    case M_PROPERTY_PRINT:
        if(!arg) return 0;
        *(char**)arg = malloc(20);
        sprintf(*(char**)arg,"%.2f",var);
        return 1;
    }
    return M_PROPERTY_NOT_IMPLEMENTED;
}

int m_property_float_range(m_option_t* prop,int action,
                           void* arg,float* var) {
    switch(action) {
    case M_PROPERTY_SET:
        if(!arg) return 0;
        M_PROPERTY_CLAMP(prop,*(float*)arg);
        *var = *(float*)arg;
        return 1;
    case M_PROPERTY_STEP_UP:
    case M_PROPERTY_STEP_DOWN:
        *var += (arg ? *(float*)arg : 0.1) *
            (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
        M_PROPERTY_CLAMP(prop,*var);
        return 1;
    }
    return m_property_float_ro(prop,action,arg,*var);
}

int m_property_delay(m_option_t* prop,int action,
                     void* arg,float* var) {
    switch(action) {
    case M_PROPERTY_PRINT:
        if(!arg) return 0;
        *(char**)arg = malloc(20);
        sprintf(*(char**)arg,"%d ms",ROUND((*var)*1000));
        return 1;
    default:
        return m_property_float_range(prop,action,arg,var);
    }
}

int m_property_double_ro(m_option_t* prop,int action,
                         void* arg,double var) {
    switch(action) {
    case M_PROPERTY_GET:
        if(!arg) return 0;
        *(double*)arg = var;
        return 1;
    case M_PROPERTY_PRINT:
        if(!arg) return 0;
        *(char**)arg = malloc(20);
        sprintf(*(char**)arg,"%.2f",var);
        return 1;
    }
    return M_PROPERTY_NOT_IMPLEMENTED;
}

int m_property_time_ro(m_option_t* prop,int action,
                       void* arg,double var) {
    switch(action) {
    case M_PROPERTY_PRINT:
	if (!arg)
	    return M_PROPERTY_ERROR;
	else {
	    int h, m, s = var;
	    h = s / 3600;
	    s -= h * 3600;
	    m = s / 60;
	    s -= m * 60;
	    *(char **) arg = malloc(20);
	    if (h > 0)
		sprintf(*(char **) arg, "%d:%02d:%02d", h, m, s);
	    else if (m > 0)
		sprintf(*(char **) arg, "%d:%02d", m, s);
	    else
		sprintf(*(char **) arg, "%d", s);
	    return M_PROPERTY_OK;
        }
    }
    return m_property_double_ro(prop,action,arg,var);
}

int m_property_string_ro(m_option_t* prop,int action,void* arg,char* str) {
    switch(action) {
    case M_PROPERTY_GET:
        if(!arg) return 0;
        *(char**)arg = str;
        return 1;
    case M_PROPERTY_PRINT:
        if(!arg) return 0;
        *(char**)arg = str ? strdup(str) : NULL;
        return 1;
    }
    return M_PROPERTY_NOT_IMPLEMENTED;
}

int m_property_bitrate(m_option_t* prop,int action,void* arg,int rate) {
    switch(action) {
    case M_PROPERTY_PRINT:
        if (!arg)
	    return M_PROPERTY_ERROR;
        *(char**)arg = malloc (16);
        sprintf(*(char**)arg, "%d kbps", rate*8/1000);
        return M_PROPERTY_OK;
    }
    return m_property_int_ro(prop, action, arg, rate);
}