/* * ioxml.c * * Copyright (C) Marzio Malanchini - Febrary 2002 * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "transcode.h" #include "libtc/libtc.h" #ifdef HAVE_LIBXML2 #include "tcinfo.h" #include "magic.h" #include "tc.h" #include "ioxml.h" #define AUDIO_VIDEO_UNKNOWN 0x00 #define AUDIO_ITEM 0x01 #define VIDEO_ITEM 0x02 #define UNSUPPORTED_PARAM 0x00 #define IN_VIDEO_CODEC 0x01 #define IN_AUDIO_CODEC 0x02 #define IN_VIDEO_MAGIC 0x03 #define IN_AUDIO_MAGIC 0x04 #define OUT_VIDEO_HEIGHT 0x05 #define OUT_VIDEO_WIDTH 0x06 #define OUT_VIDEO_RES_FILTER 0x07 audiovideo_limit_t f_det_time(char *p_options) { char *p_data,*p_temp,*p_temp1; double s_hh,s_mm,s_ss,s_app; audiovideo_limit_t s_limit; s_hh=s_mm=s_ss=0; s_limit.s_frame=0; if (strcasecmp(p_options,"smpte") == 0) s_limit.s_smpte=smpte; else if (strcasecmp(p_options,"smpte-25") == 0) s_limit.s_smpte=smpte25; else if (strcasecmp(p_options,"smpte-30-drop") == 0) s_limit.s_smpte=smpte30drop; else if (strcasecmp(p_options,"npt") == 0) s_limit.s_smpte=npt; else s_limit.s_smpte=npt; p_data=strstr(p_options,"="); if ((p_data != NULL) || (s_limit.s_smpte == npt)) { if (p_data != NULL) p_temp1=p_data+1; else p_temp1=p_options; if (strstr(p_temp1,":") != NULL) { if ((p_temp = strtok(p_temp1,":")) != NULL) s_hh=strtod(p_temp,NULL); if ((p_temp = strtok(NULL,":")) != NULL) s_mm=strtod(p_temp,NULL); if ((p_temp = strtok(NULL,":")) != NULL) s_ss=strtod(p_temp,NULL); if ((p_temp = strtok(NULL,":")) != NULL) s_limit.s_frame=strtod(p_temp,NULL); s_limit.s_time=3600*s_hh+60*s_mm+s_ss; } else { s_app=strtod(p_temp1,NULL); switch (*(p_temp1+strlen(p_temp1)-1)) //find the last char of temp1 { case 'h': s_app*=60; case 'm': s_app*=60; case 's': s_limit.s_time=(long)s_app; s_limit.s_frame=0; break; default: s_limit.s_time=0; s_limit.s_frame=(long)s_app; ;; } } } else { tc_log_warn(__FILE__,"Invalid parameter %s force default",p_options); s_limit.s_time=-1; s_limit.s_frame=0; } return(s_limit); } void f_free_tree(audiovideo_t *p_node) { if (p_node) { audiovideo_t *p_temp = NULL , *p_temp1 = NULL; for (p_temp=p_node->p_next;p_temp !=NULL;) { //first element is static p_temp1 = p_temp->p_next; free(p_temp); p_temp=p_temp1; } } } int f_parse_tree(xmlNodePtr p_node,audiovideo_t *p_audiovideo) { static audiovideo_t *p_temp; audiovideo_limit_t s_limit; static int s_type,s_param; int s_rc; static int s_video_codec=TC_CODEC_UNKNOWN,s_audio_codec=TC_CODEC_UNKNOWN; s_rc=0; if (p_node != NULL) { if (xmlStrcmp(p_node->name, (const xmlChar*)"smil") == 0) { if(f_parse_tree(p_node->xmlChildrenNode,p_audiovideo)) s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"seq") == 0) { s_type=AUDIO_VIDEO_UNKNOWN; p_temp=tc_zalloc(sizeof(audiovideo_t)); p_temp->s_end_a_time=-1; p_temp->s_end_v_time=-1; p_temp->s_start_a_time=-1; p_temp->s_start_v_time=-1; p_temp->s_end_audio=-1; p_temp->s_end_video=-1; p_temp->s_start_audio=-1; p_temp->s_start_video=-1; p_temp->s_video_smpte=npt; //force npt p_temp->s_audio_smpte=npt; //force npt p_temp->s_a_real_codec=TC_CODEC_UNKNOWN; p_temp->s_v_real_codec=TC_CODEC_UNKNOWN; p_temp->s_a_codec=TC_CODEC_UNKNOWN; p_temp->s_v_codec=TC_CODEC_UNKNOWN; p_temp->s_a_magic=TC_MAGIC_UNKNOWN; p_temp->s_v_magic=TC_MAGIC_UNKNOWN; p_temp->s_a_rate=0; p_temp->s_a_bits=0; p_temp->s_a_chan=0; p_temp->s_v_width=0; p_temp->s_v_height=0; p_temp->s_v_tg_width=0; p_temp->s_v_tg_height=0; p_temp->s_v_tg_width=0; //target width p_temp->s_v_tg_height=0; //target height if(p_audiovideo == NULL) p_audiovideo=p_temp; else p_audiovideo->p_next=p_temp; if(f_parse_tree(p_node->xmlChildrenNode,p_temp)) //visit the branch s_rc=1; if(f_parse_tree(p_node->next,p_temp)) //eventually go to the next seq item s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"video") == 0) { if (s_type!=AUDIO_VIDEO_UNKNOWN) { p_temp=tc_zalloc(sizeof(audiovideo_t)); p_temp->s_end_a_time=-1; p_temp->s_end_v_time=-1; p_temp->s_start_a_time=-1; p_temp->s_start_v_time=-1; p_temp->s_end_audio=-1; p_temp->s_end_video=-1; p_temp->s_start_audio=-1; p_temp->s_start_video=-1; p_temp->s_video_smpte=npt; //force npt p_temp->s_audio_smpte=npt; //force npt p_temp->s_a_codec=TC_CODEC_UNKNOWN; p_temp->s_v_codec=TC_CODEC_UNKNOWN; p_temp->s_a_real_codec=TC_CODEC_UNKNOWN; p_temp->s_v_real_codec=TC_CODEC_UNKNOWN; p_temp->s_a_magic=TC_MAGIC_UNKNOWN; p_temp->s_v_magic=TC_MAGIC_UNKNOWN; p_temp->s_a_rate=0; p_temp->s_a_bits=0; p_temp->s_a_chan=0; p_temp->s_v_width=0; p_temp->s_v_height=0; p_temp->s_v_tg_width=0; //target width p_temp->s_v_tg_height=0; //target height if(p_audiovideo != NULL) p_audiovideo->p_next=p_temp; p_audiovideo=p_temp; } s_type=VIDEO_ITEM; //set origin to video if(f_parse_tree((xmlNodePtr)p_node->properties,p_audiovideo)) //visit the properties s_rc=1; if(f_parse_tree(p_node->xmlChildrenNode,p_audiovideo)) //visit the branch s_rc=1; if(f_parse_tree(p_node->next,p_audiovideo)) //eventually go to the next audio/video item s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"audio") == 0) { if (s_type!=AUDIO_VIDEO_UNKNOWN) { p_temp=tc_zalloc(sizeof(audiovideo_t)); p_temp->s_end_a_time=-1; p_temp->s_end_v_time=-1; p_temp->s_start_a_time=-1; p_temp->s_start_v_time=-1; p_temp->s_end_audio=-1; p_temp->s_end_video=-1; p_temp->s_start_audio=-1; p_temp->s_start_video=-1; p_temp->s_video_smpte=npt; //force npt p_temp->s_audio_smpte=npt; //force npt p_temp->s_a_codec=TC_CODEC_UNKNOWN; p_temp->s_v_codec=TC_CODEC_UNKNOWN; p_temp->s_a_real_codec=TC_CODEC_UNKNOWN; p_temp->s_v_real_codec=TC_CODEC_UNKNOWN; p_temp->s_a_magic=TC_MAGIC_UNKNOWN; p_temp->s_v_magic=TC_MAGIC_UNKNOWN; p_temp->s_a_rate=0; p_temp->s_a_bits=0; p_temp->s_a_chan=0; p_temp->s_v_width=0; p_temp->s_v_height=0; p_temp->s_v_tg_width=0; //target width p_temp->s_v_tg_height=0; //target height if(p_audiovideo != NULL) p_audiovideo->p_next=p_temp; p_audiovideo=p_temp; } s_type=AUDIO_ITEM; //set origin to audio if(f_parse_tree((xmlNodePtr)p_node->properties,p_audiovideo)) //visit the properties s_rc=1; if(f_parse_tree(p_node->xmlChildrenNode,p_audiovideo)) //visit the branch s_rc=1; if(f_parse_tree(p_node->next,p_audiovideo)) //eventually go to the next audio/video item s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"param") == 0) { if(f_parse_tree((xmlNodePtr)p_node->properties,p_audiovideo)) //visit the properties s_rc=1; if(f_parse_tree(p_node->xmlChildrenNode,p_audiovideo)) //visit the branch s_rc=1; if(f_parse_tree(p_node->next,p_audiovideo)) //eventually go to the next audio/video item s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"src") == 0) { if (s_type==AUDIO_ITEM) { p_audiovideo->p_nome_audio=p_node->xmlChildrenNode->content; //set the audio file name } else { p_audiovideo->p_nome_video=p_node->xmlChildrenNode->content; //set the video file name } if(f_parse_tree(p_node->next,p_audiovideo)) //goto to begin and end of clip s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"clipBegin") == 0) { s_limit=f_det_time((char *)p_node->xmlChildrenNode->content); if (s_type==AUDIO_ITEM) { p_audiovideo->s_audio_smpte=s_limit.s_smpte; p_audiovideo->s_start_a_time=s_limit.s_time; p_audiovideo->s_start_audio=s_limit.s_frame; } else { p_audiovideo->s_video_smpte=s_limit.s_smpte; p_audiovideo->s_start_v_time=s_limit.s_time; p_audiovideo->s_start_video=s_limit.s_frame; } if(f_parse_tree(p_node->next,p_audiovideo)) //goto the next param. s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"clipEnd") == 0) { s_limit=f_det_time((char *)p_node->xmlChildrenNode->content); if (s_type==AUDIO_ITEM) { p_audiovideo->s_audio_smpte=s_limit.s_smpte; p_audiovideo->s_end_a_time=s_limit.s_time; p_audiovideo->s_end_audio=s_limit.s_frame+1; } else { p_audiovideo->s_video_smpte=s_limit.s_smpte; p_audiovideo->s_end_v_time=s_limit.s_time; p_audiovideo->s_end_video=s_limit.s_frame+1; } if(f_parse_tree(p_node->next,p_audiovideo)) //goto the next param. s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"name") == 0) { if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"in-video-module") == 0) s_param=IN_VIDEO_MAGIC; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"in-audio-module") == 0) s_param=IN_AUDIO_MAGIC; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"in-video-codec") == 0) s_param=IN_VIDEO_CODEC; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"in-audio-codec") == 0) s_param=IN_AUDIO_CODEC; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"target-height") == 0) s_param=OUT_VIDEO_HEIGHT; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"target-width") == 0) s_param=OUT_VIDEO_WIDTH; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"resize-filter") == 0) s_param=OUT_VIDEO_RES_FILTER; else s_param=UNSUPPORTED_PARAM; if(f_parse_tree(p_node->next,p_audiovideo)) //goto the next param. s_rc=1; } else if (xmlStrcmp(p_node->name, (const xmlChar*)"value") == 0) { if ((s_type==AUDIO_ITEM) && ((s_param==IN_VIDEO_CODEC)||(s_param==IN_VIDEO_MAGIC)||(s_param==OUT_VIDEO_HEIGHT)||(s_param==OUT_VIDEO_WIDTH))) { if (s_param==OUT_VIDEO_HEIGHT) tc_log_warn(__FILE__,"The target-height parameter cannot be used in audio item, %s skipped.",(char *)p_node->xmlChildrenNode->content); else if (s_param==OUT_VIDEO_WIDTH) tc_log_warn(__FILE__,"The target-width parameter cannot be used in audio item, %s skipped.",(char *)p_node->xmlChildrenNode->content); else if (s_param==IN_VIDEO_MAGIC) tc_log_warn(__FILE__,"The in-video-module parameter cannot be used in audio item, %s skipped.",(char *)p_node->xmlChildrenNode->content); else if (s_param==IN_VIDEO_CODEC) tc_log_warn(__FILE__,"The in-video-codec parameter cannot be used in audio item, %s skipped.",(char *)p_node->xmlChildrenNode->content); s_rc=1; } else { switch(s_param) { case OUT_VIDEO_RES_FILTER: p_audiovideo->p_v_resize_filter=p_node->xmlChildrenNode->content; break; case OUT_VIDEO_HEIGHT: p_audiovideo->s_v_tg_height=atoi((char *)p_node->xmlChildrenNode->content); break; case OUT_VIDEO_WIDTH: p_audiovideo->s_v_tg_width=atoi((char *)p_node->xmlChildrenNode->content); break; case IN_VIDEO_MAGIC: if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"dv") == 0) p_audiovideo->s_v_magic=TC_MAGIC_DV_PAL; //the same for PAL and NTSC else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"avi") == 0) p_audiovideo->s_v_magic=TC_MAGIC_AVI; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"mov") == 0) p_audiovideo->s_v_magic=TC_MAGIC_AVI; else { tc_log_warn(__FILE__,"The in-video-magic %s parameter isn't yet supported.",(char *)p_node->xmlChildrenNode->content); s_rc=1; } break; case IN_AUDIO_MAGIC: if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"dv") == 0) p_audiovideo->s_a_magic=TC_MAGIC_DV_PAL; //the same for PAL and NTSC else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"avi") == 0) p_audiovideo->s_a_magic=TC_MAGIC_AVI; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"mov") == 0) p_audiovideo->s_a_magic=TC_MAGIC_AVI; else { tc_log_warn(__FILE__,"The in-audio-magic %s parameter isn't yet supported.",(char *)p_node->xmlChildrenNode->content); s_rc=1; } break; case IN_VIDEO_CODEC: if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"rgb") == 0) p_audiovideo->s_v_codec=CODEC_RGB; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"yuv2") == 0) p_audiovideo->s_v_codec=CODEC_YUV; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"yuv420p") == 0) p_audiovideo->s_v_codec=CODEC_YUV; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"yv12") == 0) p_audiovideo->s_v_codec=CODEC_YUV; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"yuy2") == 0) p_audiovideo->s_v_codec=CODEC_YUY2; else if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"raw") == 0) p_audiovideo->s_v_codec=CODEC_RAW; else { tc_log_warn(__FILE__,"The in-video-codec %s parameter isn't yet supported.",(char *)p_node->xmlChildrenNode->content); s_rc=1; } if (s_video_codec == TC_CODEC_UNKNOWN) s_video_codec=p_audiovideo->s_v_codec; else if (s_video_codec != p_audiovideo->s_v_codec) { tc_log_warn(__FILE__,"The XML file must contain the same video codec."); s_rc=1; } break; case IN_AUDIO_CODEC: if (xmlStrcmp((char *)p_node->xmlChildrenNode->content, (const xmlChar*)"pcm") == 0) p_audiovideo->s_a_codec=CODEC_PCM; else { tc_log_warn(__FILE__,"The in-audio-codec %s parameter isn't yet supported.",(char *)p_node->xmlChildrenNode->content); s_rc=1; } if (s_audio_codec == TC_CODEC_UNKNOWN) s_audio_codec=p_audiovideo->s_a_codec; else if (s_audio_codec != p_audiovideo->s_a_codec) { tc_log_warn(__FILE__,"The XML file must contain the same audio codec."); s_rc=1; } break; case UNSUPPORTED_PARAM: tc_log_warn(__FILE__,"The %s parameter isn't yet supported.",(char *)p_node->xmlChildrenNode->content); s_rc=1; break; } } if(f_parse_tree(p_node->next,p_audiovideo)) //goto the next param. s_rc=1; } } return(s_rc); } void f_delete_unused_node(xmlNodePtr p_node) { while (p_node != NULL) { xmlNodePtr p_kill_node = NULL; f_delete_unused_node(p_node->xmlChildrenNode); if (xmlStrcmp(p_node->name, (const xmlChar*)"smil") == 0) { //leave it in the tree } else if (xmlStrcmp(p_node->name, (const xmlChar*)"seq") == 0) { if (p_node->xmlChildrenNode == NULL) { //This node don't have any video sections so i can kill it p_kill_node = p_node; } } else if ((xmlStrcmp(p_node->name, (const xmlChar*)"param") == 0) ||(xmlStrcmp(p_node->name, (const xmlChar*)"video") == 0) || (xmlStrcmp(p_node->name, (const xmlChar*)"audio") == 0)) { //leave it in the tree } else { //not an interesting node p_kill_node = p_node; } p_node = p_node->next; if (p_kill_node != NULL) { //kill the node from the tree xmlUnlinkNode(p_kill_node); xmlFreeNode(p_kill_node); } } } int f_complete_tree(audiovideo_t *p_audiovideo) { audiovideo_t *p_temp; int s_video_codec=TC_CODEC_UNKNOWN,s_audio_codec=TC_CODEC_UNKNOWN; for (p_temp=p_audiovideo->p_next;p_temp != NULL;p_temp=p_temp->p_next) { if (p_temp->p_nome_video != NULL) { if (p_temp->s_start_v_time == -1) { p_temp->s_start_video=0; p_temp->s_start_v_time=0; } if (p_temp->s_end_v_time == -1) { p_temp->s_end_video=LONG_MAX; p_temp->s_end_v_time=0; } if (p_audiovideo->s_v_codec != TC_CODEC_UNKNOWN) { if ((s_video_codec!=TC_CODEC_UNKNOWN) && (p_audiovideo->s_v_codec != s_video_codec)) { tc_log_error(__FILE__,"The file must contain the same video codec (found 0x%lx but 0x%x is already defined)",p_audiovideo->s_v_codec,s_video_codec); return(1); } s_video_codec=p_audiovideo->s_v_codec; } } if (p_temp->p_nome_audio != NULL) { if (p_temp->s_start_a_time == -1) { p_temp->s_start_audio=0; p_temp->s_start_a_time=0; } if (p_temp->s_end_a_time == -1) { p_temp->s_end_audio=LONG_MAX; p_temp->s_end_a_time=0; } if (p_audiovideo->s_a_codec != TC_CODEC_UNKNOWN) { if ((s_audio_codec!=TC_CODEC_UNKNOWN) && (p_audiovideo->s_a_codec != s_audio_codec)) { tc_log_error(__FILE__,"The file must contain the same audio codec (found 0x%lx but 0x%x is already defined)",p_audiovideo->s_a_codec,s_audio_codec); return(1); } s_audio_codec=p_audiovideo->s_a_codec; } } else { p_temp->p_nome_audio=p_temp->p_nome_video; //force audio to has the same input file of video p_temp->s_start_audio=p_temp->s_start_video; p_temp->s_end_audio=p_temp->s_end_video; p_temp->s_end_a_time=p_temp->s_end_v_time; p_temp->s_start_a_time=p_temp->s_start_v_time; } } for (p_temp=p_audiovideo->p_next;p_temp != NULL;p_temp=p_temp->p_next) //initialize all unset codec { if (p_temp->p_nome_video != NULL) p_audiovideo->s_v_codec=s_video_codec; if (p_temp->p_nome_audio != NULL) p_audiovideo->s_a_codec=s_audio_codec; } return(0); } int f_manage_input_xml(const char *p_name,int s_type,audiovideo_t *p_audiovideo) { static xmlDocPtr p_doc; xmlNodePtr p_node; xmlNsPtr ns; if (s_type) //read the file from p_name { p_doc = xmlParseFile(p_name); p_node = xmlDocGetRootElement(p_doc); if (p_node == NULL) { xmlFreeDoc(p_doc); tc_log_error(__FILE__,"Invalid file format"); return(-1); } ns = xmlSearchNsByHref(p_doc, p_node, (const xmlChar *) "http://www.w3.org/2001/SMIL20/Language"); if (ns == NULL) { xmlFreeDoc(p_doc); tc_log_error(__FILE__,"Invalid Namespace"); return(-1); } ns = xmlSearchNs(p_doc, p_node, (const xmlChar *) "smil2"); if (ns == NULL) { xmlFreeDoc(p_doc); tc_log_error(__FILE__,"Invalid Namespace"); return(-1); } if (xmlStrcmp(p_node->name, (const xmlChar *) "smil")) { xmlFreeDoc(p_doc); tc_log_error(__FILE__,"Invalid Namespace"); return(-1); } f_delete_unused_node(p_node); memset(p_audiovideo,'\0',sizeof(audiovideo_t)); if(f_parse_tree(p_node,p_audiovideo)) return(1); if (f_complete_tree(p_audiovideo)) return(1); } else { f_free_tree(p_audiovideo); xmlFreeDoc(p_doc); } return(0); } #endif