📄 msdl.c
字号:
static int download_target(char *target_str,struct options_t *options,struct dlresult_t *result, char **local_name){ struct url_t *url = NULL; /* for each target url */ struct download_opts_t *dlopts = NULL; int ret = 0; ret = prepare_download(target_str, options, &url,&dlopts, local_name); /* local filename will be determined */ if(!ret) { goto failed; } ret = do_download(*local_name,url,dlopts); if(ret > 0) { /* succeeded */ list_h_append(&result->success_list,strdup(target_str)); } else if(ret < 0) { /* failed */ list_h_append(&result->failed_list,strdup(target_str)); } else { /* ret == 0 which is NO_NEED_TO_DOWNLOAD */ /* it's not download success but of course not failure */ /* do nothing and 0 will be returned */ } if(url) free_url_t(url); if(dlopts) free_download_opts_t(dlopts); return ret; failed: if(url) free_url_t(url); if(dlopts) free_download_opts_t(dlopts); return -1;}/* * allocate url and dlopts, from target_str and options * return value: 1 ... success 0 ... failure */static int prepare_download(char *target_str,struct options_t *options, struct url_t **url_ret,struct download_opts_t **dlopts_ret, char **local_name){ struct url_t *url = NULL; /* for each target url */ struct download_opts_t *dlopts = NULL; /* check if target is really a file */ url = new_url_t(target_str); if(url == NULL) { /* invalid URL, just continue */ display(MSDL_VER,"ignored \"%s\"\n",target_str); goto ignore; } /* * check if target url is valid */ if(!url->hostname || !strcmp(url->hostname,"")) { display(MSDL_NOR,"ignored \"%s\" (no host information)\n",target_str); goto ignore; } else if(!url->filepath || !strcmp(url->filepath,"")) { display(MSDL_NOR,"warning: \"%s\" (no filepath)\n",target_str); *local_name = NULL; } else { /* normal case */ *local_name = create_local_file_name(target_str,options); } if((!*local_name) || !strcmp(*local_name,"")) { *local_name = strdup(default_file_name_for_empty); /* allocate default file name */ } dlopts = new_download_opts_t(); set_dlopts_from_options(options,dlopts); dlopts->dl_protocol = url->protocol_type; /* if url starts with "http", try MMSH first, and fallback on HTTP. also, if MMST and HTTP_PROXY specified, use http. */ if(url->protocol_type == HTTP || (url->protocol_type == MMST && dlopts->http_proxy)) { dlopts->dl_protocol = MMSH; } /* if url starts with "mms", try rtsp because it's the standard. pure mmst protocol is obsolate. */ if(url->protocol_type == MMST) { dlopts->dl_protocol = RTSP_WMS; } /* choose protocol if specified */ if(options->protocol) { dlopts->dl_protocol = protocol_type_from_string(options->protocol); if(dlopts->dl_protocol == UNKNOWN_PROTOCOL) { display(MSDL_ERR,"protocol [ %s ] not supported: ignore this option \n", options->protocol); dlopts->dl_protocol = url->protocol_type; } } *url_ret = url; *dlopts_ret = dlopts; return 1; ignore: if(url) free_url_t(url); if(dlopts) free_download_opts_t(dlopts); *url_ret = NULL; *dlopts_ret = NULL; return 0;}/* * do actual download * streaming_download() wrapper * return value: what streaming_download returns * ((CAUTION)) * streaming_download will return 0 if no need for downloading, and * in metafile case, it should be continue */static int do_download(const char *local_name,struct url_t *url,struct download_opts_t *dlopts){ int ret = 0; int retry_again = 0; /* display download file name */ display(MSDL_NOR,"download [ %s ]\n",local_name); display(MSDL_VER,"url: %s\n",url->url); /* download file */ do { retry_again = 0; ret = streaming_download(local_name,url,dlopts); if((ret == -2) && (dlopts->auto_retry > 0)) { display(MSDL_NOR,"*** try again downloading (%d)\n",dlopts->auto_retry); dlopts->resume_download = 1; /* set resume enable */ dlopts->auto_retry--; retry_again = 1; } } while(retry_again); return ret;}/* * print protocol name to screen. */static void display_protocol(struct stream_t *stream){ display(MSDL_VER,"download protocol: "); switch (stream->stream_ctrl->protocol) { case MMST: display(MSDL_VER,"mmst"); break; case MMSH: display(MSDL_VER,"mmsh"); break; case RTSP_REAL: display(MSDL_VER,"rtsp - real"); break; case RTSP_WMS: display(MSDL_VER,"rtsp - wms"); break; case HTTP: display(MSDL_VER,"http"); break; case FTP: display(MSDL_VER,"ftp"); break; default: display(MSDL_ERR," :) ...bug"); break; } display(MSDL_VER,"\n\n");}struct stream_t *streaming_init(struct url_t *url,struct download_opts_t *dlopts){ struct stream_t *stream = NULL; switch(dlopts->dl_protocol) { case MMST: stream = mmst_streaming_init(); break; case HTTP: stream = http_streaming_init(); break; case MMSH: stream = mmsh_streaming_init(); break; case FTP: stream = ftp_streaming_init(); break; case RTSP: case RTSP_WMS: case RTSP_REAL: stream = rtsp_streaming_init(); break; default: display(MSDL_ERR,"protocol [ %s:// ] not supported\n",url->protocol); return NULL; } stream->url = url; stream->dlopts = dlopts; return stream;}/* * main downloading function. * select DLing function according to url->protocol. * download 'url' to 'localfile' * * return value; 1 ... succeeded * 0 ... did nothing, failure don't count * -1 ... failed * -2 ... failed while downloading, which means * protocol negotiation succeeded * thus, can retry later */int streaming_download(const char *local_file,struct url_t *url,struct download_opts_t *dlopts){ int ret = 0; struct stream_t *stream = NULL; int retries = 0; FILE *fp = NULL; uint8_t *buffer = NULL; /* download header. */ for(retries = 2 ; retries > 0 ; retries--) { stream = streaming_init(url,dlopts); if(stream == NULL) { goto failed; } stream->localfile = strdup(local_file); /* protocol initiation */ ret = stream->start(stream); if(ret > 0) { /* success */ break; } else if(stream->stream_ctrl->status == STREAMING_OTHER_PROTOCOL) { /* retry required (maximum chance is 'retries' times) */ dlopts->dl_protocol = stream->stream_ctrl->retry_protocol; stream->close(stream); /* close stream for this protocol */ stream = NULL; continue; } else if(stream->stream_ctrl->status == STREAMING_NO_NEED_TO_DOWNLOAD) { display(MSDL_NOR,"file \"%s\" already complete: not downloading\n", stream->localfile); goto already_done; } else if(stream->stream_ctrl->status == STREAMING_REDIRECTED) { if(stream->stream_ctrl->retry_urlstr) { struct url_t *newurl = new_url_t(stream->stream_ctrl->retry_urlstr); if(!newurl) { display(MSDL_ERR,"invalid redirection url %s",stream->stream_ctrl->retry_urlstr); goto failed; } else { char *p = strrchr(stream->stream_ctrl->retry_urlstr,'/'); if(!p || p[1] == '\0') { /* not found , or "/" */ free(stream->localfile); stream->localfile = strdup(local_file); /* copy the 'before redirection' filename */ } else { /* filename found */ free(stream->localfile); stream->localfile = strdup(p+1); /* set new filename */ } /* reset url members to new one */ copy_url_t(url,newurl); free_url_t(newurl); stream->close(stream); stream = NULL; continue; } } else { display(MSDL_ERR,"stream_ctrl->retry_urlstr not set\n"); goto failed; } } else { /* protocol initiation error */ display(MSDL_ERR,"cannot establish stream\n"); goto failed; } } if(retries <= 0) { display(MSDL_ERR,"no more retry chance\n"); goto failed; } else { /* success */ uint64_t size_written = 0; /* size written to file (current offset of file) */ uint64_t real_downloaded_size = 0; /* bytes got from network */ struct progressinfo_t pgi; int resume_seek_ok = 0; /* success initialize */ /* * display download protocol. * -- IMPORTANT -- * this may differ from dlotps->dl_protocol or url->protocol_type, * as those are just request. there might be fallbacks or retries. */ display_protocol(stream); /* download media. */ resume_seek_ok = 0; if(stream->resumeinfo->resume_req_success) { if((fp = fopen(stream->localfile,"r+b")) == NULL) { /* append open */ display(MSDL_ERR,"error: open existing file \"%s\"\n", stream->localfile); perror(""); resume_seek_ok = 0; } else { if(fseek(fp,stream->resumeinfo->resume_start_offset,SEEK_SET) < 0) { display(MSDL_ERR,"error: cannot seek \"%s\" to [%x]\n", stream->localfile,stream->resumeinfo->resume_start_offset); perror(""); resume_seek_ok = 0; fclose(fp); /* close file */ } else { /* success */ display(MSDL_NOR,"resume: seek OK, start writing from %lld [0x%x]\n", stream->resumeinfo->resume_start_offset, stream->resumeinfo->resume_start_offset); stream->stream_ctrl->status = STREAMING_RESUME_BUFFERING; progress_dl_start(&pgi); pgi.last_speed_size_written = stream->resumeinfo->resume_start_offset; pgi.last_etl_speed_size_written = stream->resumeinfo->resume_start_offset; size_written = stream->resumeinfo->resume_start_offset; resume_seek_ok = 1; } } } if(!resume_seek_ok) { /* not resuming --> just open normally */ /* create file here */ if((fp = fopen(stream->localfile,"wb")) == NULL) { display(MSDL_ERR,"cannot create file \"%s\"\n",stream->localfile); perror(""); goto failed; } progress_dl_start(&pgi); } buffer = (uint8_t *)xmalloc(BUFSIZE_4_DL); real_downloaded_size = 0; for(;;) { if((stream->stream_ctrl->write_data_len < BUFSIZE_4_DL) && /* write queue not enough */ stream_check_data(stream,STREAM_CHECK_DATA_TIME) <= 0) { /* nothing to read */ /* some error or data didn't come within few(default 5) seconds*/ display_progress_info(stream->stream_ctrl->file_size,size_written,0,0); } ret = stream->read(stream,buffer,BUFSIZE_4_DL); /* write received packet directory to file. everything, such as padding has been done alrady, just write it. */ if(ret > 0) { /* success */ if(stream->stream_ctrl->status == STREAMING_REWIND) { /* have to write data AFTER deleting all current file content. */ display(MSDL_VER,"rewind file!!\n"); rewind(fp); size_written = 0; real_downloaded_size = 0; stream->stream_ctrl->packet_count = 0; stream->stream_ctrl->status = STREAMING_DOWNLOADING; /* cont as download restart */ progress_dl_start(&pgi); } size_written += ret; real_downloaded_size += ret; progress_update(&pgi,stream->stream_ctrl->file_size,size_written); /* display(MSDL_DBG,"\npacket count: %d\n",stream->stream_ctrl->packet_count); display(MSDL_DBG,"ftell: %d [%x] --> write %d [%x]\n",ftell(fp),ftell(fp),ret,ret); */ ret = fwrite(buffer,sizeof(uint8_t),ret,fp); } else if(stream->stream_ctrl->status == STREAMING_RESUME_BUFFERING) { /* * just do nothing, stream->read will automatically seek to THAT position, * and remove STREAMING_RESUME_BUFFERING. */ display_progress_info(stream->stream_ctrl->file_size,size_written,-1,-1); continue; } else if(stream->stream_ctrl->status == STREAMING_FINISHED) { /* show 100%, because 99% sucks ... have to do this because stream->stream_ctrl->file_size may show wrong file size. */ int64_t average_speed = progress_get_average_bandwidth(&pgi,real_downloaded_size); display_progress_info(size_written,size_written,average_speed,0); display(MSDL_NOR,"\nfinished!!\n"); break; } /* return value is 0 if failure */ else { /* error while download */ display(MSDL_ERR,"!!! aborted by error !!!\n"); goto failed_while_downloading; } } } display(MSDL_NOR,"\n"); if(stream) stream->close(stream); if(buffer) free(buffer); fclose(fp); return 1; already_done: display(MSDL_NOR,"\n"); if(stream) stream->close(stream); if(buffer) free(buffer); if(fp) fclose(fp); return 0; failed: display(MSDL_NOR,"\n"); if(stream) stream->close(stream); if(buffer) free(buffer); if(fp) fclose(fp); return -1; failed_while_downloading: /* --> can retry later */ display(MSDL_NOR,"\n"); if(stream) stream->close(stream); if(buffer) free(buffer); if(fp) fclose(fp); return -2;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -