From 0586430b3c91646a8021fbcf2adf272497ae4e3d Mon Sep 17 00:00:00 2001 From: samsamsam Date: Sat, 7 Jan 2017 10:26:34 +0100 Subject: [PATCH] Add support for live streams. --- makefile | 2 +- src/curl.c | 85 +++++++++++-- src/curl.h | 9 +- src/hls.c | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/hls.h | 18 +++ src/main.c | 24 ++-- src/misc.c | 2 +- src/msg.c | 57 +++++---- src/msg.h | 2 + 9 files changed, 498 insertions(+), 62 deletions(-) diff --git a/makefile b/makefile index b158ec5..b9cd760 100755 --- a/makefile +++ b/makefile @@ -11,7 +11,7 @@ CFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes CFLAGS+=-Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual CFLAGS+=-Wsign-compare -Iincludes -g CFLAGS+=-DPREFIX='"$(PREFIX)"' -LDFLAGS+=-lcurl -lavformat -lavutil -lavcodec -lswresample +LDFLAGS+=-lpthread -lcurl -lavformat -lavutil -lavcodec -lswresample OSNAME=$(shell uname -s | sed -e 's/[-_].*//g' | tr A-Z a-z) ifeq ("$(OSNAME)", "darwin") diff --git a/src/curl.c b/src/curl.c index a48f4b9..01c4e7f 100755 --- a/src/curl.c +++ b/src/curl.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "msg.h" #include "curl.h" #include "hls.h" @@ -31,45 +32,105 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) return realsize; } -size_t get_data_from_url(char *url, char **str, uint8_t **bin, int type) + +void * init_http_session(void) { CURL *c; + c = curl_easy_init(); + return c; +} + +long get_data_from_url_with_session(void **session, char **url, char **out, size_t *size, int type, bool update_url) +{ + assert(session && *session); + assert(url && *url); + assert(size); + + CURL *c = (CURL *)(*session); CURLcode res; + long http_code = 0; + char *e_url = NULL; - url[strcspn(url, "\r")] = '\0'; + (*url)[strcspn(*url, "\r")] = '\0'; struct MemoryStruct chunk; chunk.memory = malloc(1); chunk.size = 0; - c = curl_easy_init(); - curl_easy_setopt(c, CURLOPT_URL, url); + curl_easy_setopt(c, CURLOPT_URL, *url); curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(c, CURLOPT_WRITEDATA, (void *)&chunk); curl_easy_setopt(c, CURLOPT_USERAGENT, USER_AGENT); res = curl_easy_perform(c); + + curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &http_code); + if (update_url && CURLE_OK == curl_easy_getinfo(c, CURLINFO_EFFECTIVE_URL, &e_url)) + { + if (0 != strcmp(*url, e_url)) + { + free(*url); + *url = strdup(e_url); + } + } if (res != CURLE_OK) { MSG_ERROR("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } else { if (type == STRING) { - *str = strdup(chunk.memory); + *out = strdup(chunk.memory); } else if (type == BINKEY) { - *bin = malloc(KEYLEN); - *bin = memcpy(*bin, chunk.memory, KEYLEN); + *out = malloc(KEYLEN); + *out = memcpy(*out, chunk.memory, KEYLEN); } else if (type == BINARY) { - *bin = malloc(chunk.size); - *bin = memcpy(*bin, chunk.memory, chunk.size); + *out = malloc(chunk.size); + *out = memcpy(*out, chunk.memory, chunk.size); } } - - curl_easy_cleanup(c); + + *size = chunk.size; if (chunk.memory) { free(chunk.memory); } - return chunk.size; + return http_code; +} + +void clean_http_session(void *session) +{ + curl_easy_cleanup((CURL *)session); +} + +long get_data_from_url_ext(char **url, char **out, size_t *size, int type, bool update_url) +{ + CURL *c = (CURL *)init_http_session(); + long http_code = get_data_from_url_with_session(&c, url, out, size, type, update_url); + clean_http_session(c); + return http_code; +} + +size_t get_data_from_url(char **url, char **str, uint8_t **bin, int type, bool update_url) +{ + CURL *c = (CURL *)init_http_session(); + size_t size; + char *out = NULL; + get_data_from_url_with_session(&c, url, &out, &size, type, update_url); + + switch (type){ + case STRING: + *str = out; + break; + case BINARY: + case BINKEY: + *bin = (uint8_t *)out; + break; + default: + break; + } + + clean_http_session(c); + + return size; } diff --git a/src/curl.h b/src/curl.h index 12d1444..61605e9 100755 --- a/src/curl.h +++ b/src/curl.h @@ -1,6 +1,8 @@ #ifndef __HLS_DownLoad__curl__ #define __HLS_DownLoad__curl__ +#include + #define STRING 0x0001 #define BINKEY 0x0002 #define BINARY 0x0003 @@ -10,6 +12,11 @@ "Mobile/10A5355d Safari/8536.25" -size_t get_data_from_url(char *url, char **str, uint8_t **bin, int type); +size_t get_data_from_url(char **url, char **str, uint8_t **bin, int type, bool update_url); + +void * init_http_session(void); +long get_data_from_url_with_session(void **session, char **url, char **out, size_t *size, int type, bool update_url); +long get_data_from_url_ext(char **url, char **out, size_t *size, int type, bool update_url); +void clean_http_session(void *session); #endif /* defined(__HLS_DownLoad__curl__) */ diff --git a/src/hls.c b/src/hls.c index 34648b3..54d8b66 100755 --- a/src/hls.c +++ b/src/hls.c @@ -5,6 +5,10 @@ #include #include #include +#include +#include +#include + #include "curl.h" #include "hls.h" #include "msg.h" @@ -83,7 +87,7 @@ static int parse_tag(struct hls_media_playlist *me, struct hls_media_segment *ms me->is_endlist = true; return 0; } else if (!strncmp(tag, "#EXT-X-MEDIA-SEQUENCE:", 22)){ - if(sscanf(tag+22, "%d", &(me->last_media_sequence)) == 1){ + if(sscanf(tag+22, "%d", &(me->first_media_sequence)) == 1){ return 0; } } else if (!strncmp(tag, "#EXT-X-TARGETDURATION:", 22)){ @@ -114,7 +118,7 @@ static int parse_tag(struct hls_media_playlist *me, struct hls_media_segment *ms extend_url(&link_to_key, me->url); uint8_t *decrypt; - if (get_data_from_url(link_to_key, NULL, &decrypt, BINKEY) == 0) { + if (get_data_from_url(&link_to_key, NULL, &decrypt, BINKEY, false) == 0) { MSG_ERROR("Getting key-file failed.\n"); free(link_to_key); return 1; @@ -196,7 +200,7 @@ static int media_playlist_get_links(struct hls_media_playlist *me) /* Get full url */ extend_url(&(ms->url), me->url); - ms->sequence_number = i + me->last_media_sequence; + ms->sequence_number = i + me->first_media_sequence; /* Add new segment to segment list */ if (me->first_media_segment == NULL) @@ -221,7 +225,7 @@ static int media_playlist_get_links(struct hls_media_playlist *me) me->last_media_segment = curr_ms; if (i > 0) { - me->last_media_sequence += i - 1; + me->last_media_sequence = me->first_media_sequence + i - 1; } if (ms != NULL) { @@ -234,12 +238,23 @@ static int media_playlist_get_links(struct hls_media_playlist *me) return 0; } +static int get_duration_hls_media_playlist(struct hls_media_playlist *me) +{ + int duration = 0; + struct hls_media_segment *ms = me->first_media_segment; + while(ms) { + duration += ms->duration; + ms = ms->next; + } + return duration; +} + int handle_hls_media_playlist(struct hls_media_playlist *me) { me->encryption = false; me->encryptiontype = ENC_NONE; - get_data_from_url(me->url, &me->source, NULL, STRING); + get_data_from_url(&(me->url), &me->source, NULL, STRING, true); if (get_playlist_type(me->source) != MEDIA_PLAYLIST) { return 1; @@ -254,6 +269,7 @@ int handle_hls_media_playlist(struct hls_media_playlist *me) MSG_ERROR("Could not parse links. Exiting.\n"); return 1; } + me->total_duration = get_duration_hls_media_playlist(me); return 0; } @@ -558,12 +574,310 @@ static int decrypt_aes128(struct hls_media_segment *s, struct ByteBuffer *buf) return 0; } +static void *hls_playlist_update_thread(void *arg) +{ + hls_playlist_updater_params *updater_params = arg; + + struct hls_media_playlist *me = updater_params->media_playlist; + pthread_mutex_t *media_playlist_mtx = (pthread_mutex_t *)(updater_params->media_playlist_mtx); + + pthread_cond_t *media_playlist_refresh_cond = (pthread_cond_t *)(updater_params->media_playlist_refresh_cond); + pthread_cond_t *media_playlist_empty_cond = (pthread_cond_t *)(updater_params->media_playlist_empty_cond); + + void *session = init_http_session(); + bool is_endlist = false; + char *url = NULL; + int refresh_delay = 0; + + // no lock is needed here because download_live_hls not change this fields + //pthread_mutex_lock(media_playlist_mtx); + is_endlist = me->is_endlist; + url = strdup(me->url); + refresh_delay = me->target_duration; + //pthread_mutex_unlock(media_playlist_mtx); + + if (refresh_delay > MAX_REFRESH_DELAY) { + refresh_delay = MAX_REFRESH_DELAY; + } else if (refresh_delay < MIN_REFRESH_DELAY) { + refresh_delay = MIN_REFRESH_DELAY; + } + + struct timespec ts; + memset(&ts, 0x00, sizeof(ts)); + MSG_VERBOSE("Update thread started\n"); + while (!is_endlist) { + // download live hls can interrupt waiting + ts.tv_sec = time(NULL) + refresh_delay; + pthread_mutex_lock(media_playlist_mtx); + pthread_cond_timedwait(media_playlist_refresh_cond, media_playlist_mtx, &ts); + pthread_mutex_unlock(media_playlist_mtx); + + // update playlist + struct hls_media_playlist new_me; + memset(&new_me, 0x00, sizeof(new_me)); + new_me.url = strdup(url); + + size_t size = 0; + long http_code = get_data_from_url_with_session(&session, &new_me.url, &new_me.source, &size, STRING, false); + if (200 == http_code && 0 == media_playlist_get_links(&new_me)) { + // no mutex is needed here because download_live_hls not change this fields + if (new_me.is_endlist || + new_me.first_media_sequence != me->first_media_sequence || + new_me.last_media_sequence != me->last_media_sequence) + { + bool list_extended = false; + // we need to update list + pthread_mutex_lock(media_playlist_mtx); + me->is_endlist = new_me.is_endlist; + is_endlist = new_me.is_endlist; + me->first_media_sequence = new_me.first_media_sequence; + + if (new_me.last_media_sequence > me->last_media_sequence) + { + // add new segments + struct hls_media_segment *ms = new_me.first_media_segment; + while (ms) { + if (ms->sequence_number > me->last_media_sequence) { + if (ms->prev) { + ms->prev->next = NULL; + } + ms->prev = NULL; + + if (me->last_media_segment) { + me->last_media_segment->next = ms; + } else { + assert(me->first_media_segment == NULL); + me->first_media_segment = ms; + } + + me->last_media_segment = new_me.last_media_segment; + me->last_media_sequence = new_me.last_media_sequence; + + if (ms == new_me.first_media_segment) { + // all segments are new + new_me.first_media_segment = NULL; + new_me.last_media_segment = NULL; + } + + while (ms) { + me->total_duration += ms->duration; + ms = ms->next; + } + + list_extended = true; + break; + } + + ms = ms->next; + } + } + if (list_extended) { + pthread_cond_signal(media_playlist_empty_cond); + } + pthread_mutex_unlock(media_playlist_mtx); + } + } else { + MSG_WARNING("Fail to update playlist \"%s\". http_code[%d].\n", new_me.url, (int)http_code); + } + media_playlist_cleanup(&new_me); + } + + clean_http_session(session); + free(url); + pthread_exit(NULL); +} + +int download_live_hls(struct hls_media_playlist *me) +{ + MSG_API("{\"download_type\":\"live\"}\n"); + + char filename[MAX_FILENAME_LEN]; + if (hls_args.custom_filename) { + strcpy(filename, hls_args.filename); + } else { + strcpy(filename, "000_hls_output.ts"); + } + + if (access(filename, F_OK) != -1) { + if (hls_args.force_overwrite) { + if (remove(filename) != 0) { + MSG_ERROR("Error overwriting file"); + exit(1); + } + } else { + char userchoice; + MSG_PRINT("File already exists. Overwrite? (y/n) "); + scanf("\n%c", &userchoice); + if (userchoice == 'y') { + if (remove(filename) != 0) { + MSG_ERROR("Error overwriting file"); + exit(1); + } + } else { + MSG_WARNING("Choose a different filename. Exiting.\n"); + exit(0); + } + } + } + + FILE *pFile = fopen(filename, "wb"); + if (pFile == NULL) + { + MSG_ERROR("Error can not open output file\n"); + exit(1); + } + + hls_playlist_updater_params updater_params; + + /* declaration synchronization prymitives */ + pthread_mutex_t media_playlist_mtx; + + pthread_cond_t media_playlist_refresh_cond; + pthread_cond_t media_playlist_empty_cond; + + /* init synchronization prymitives */ + pthread_mutex_init(&media_playlist_mtx, NULL); + + pthread_cond_init(&media_playlist_refresh_cond, NULL); + pthread_cond_init(&media_playlist_empty_cond, NULL); + + memset(&updater_params, 0x00, sizeof(updater_params)); + updater_params.media_playlist = me; + updater_params.media_playlist_mtx = (void *)&media_playlist_mtx; + updater_params.media_playlist_refresh_cond = (void *)&media_playlist_refresh_cond; + updater_params.media_playlist_empty_cond = (void *)&media_playlist_empty_cond; + + // skip first segments + if (me->first_media_segment != me->last_media_segment) { + struct hls_media_segment *ms = me->last_media_segment; + int live_start_offset = LIVE_START_OFFSET; + int duration = 0; + while (ms) { + duration += ms->duration; + if (duration >= live_start_offset) { + break; + } + ms = ms->prev; + } + + if (ms && ms != me->first_media_segment){ + // remove segments + while (me->first_media_segment != ms) { + struct hls_media_segment *tmp_ms = me->first_media_segment; + me->first_media_segment = me->first_media_segment->next; + free(tmp_ms->url); + free(tmp_ms); + } + ms->prev = NULL; + me->first_media_segment = ms; + } + + me->total_duration = get_duration_hls_media_playlist(me); + } + + // start update thread + pthread_t thread; + void *ret; + + pthread_create(&thread, NULL, hls_playlist_update_thread, &updater_params); + + void *session = init_http_session(); + int downloaded_duration = 0; + int total_duration = 0; + + bool download = true; + while(download) { + + pthread_mutex_lock(&media_playlist_mtx); + struct hls_media_segment *ms = me->first_media_segment; + if (ms != NULL) { + me->first_media_segment = ms->next; + if (me->first_media_segment) { + me->first_media_segment->prev = NULL; + } + } + else { + me->last_media_segment = NULL; + download = !me->is_endlist; + } + total_duration = me->total_duration; + if (ms == NULL) { + if (download) { + pthread_cond_signal(&media_playlist_refresh_cond); + pthread_cond_wait(&media_playlist_empty_cond, &media_playlist_mtx); + } + } + pthread_mutex_unlock(&media_playlist_mtx); + if (ms == NULL) { + continue; + } + + MSG_PRINT("Downloading part %d\n", ms->sequence_number); + do { + + struct ByteBuffer seg; + memset(&seg, 0x00, sizeof(seg)); + size_t size = 0; + long http_code = get_data_from_url_with_session(&session, &(ms->url), (char **)&(seg.data), &size, BINARY, false); + seg.len = (int)size; + if (http_code != 200) { + int first_media_sequence = 0; + if (seg.data) { + free(seg.data); + seg.data = NULL; + } + + pthread_mutex_lock(&media_playlist_mtx); + first_media_sequence = me->first_media_sequence; + total_duration = me->total_duration; + pthread_mutex_unlock(&media_playlist_mtx); + + if(ms->sequence_number > first_media_sequence) { + sleep(1); + MSG_WARNING("Live retry segment %d download, due to previous error. http_code[%d].\n", ms->sequence_number, (int)http_code); + continue; + } + else + { + MSG_WARNING("Live mode skipping segment %d. http_code[%d].\n", ms->sequence_number, (int)http_code); + break; + } + } + + downloaded_duration += ms->duration; + MSG_API("{\"t_duration\":%d,\"d_duration\":%d}\n", total_duration, downloaded_duration); + if (me->encryption == true && me->encryptiontype == ENC_AES128) { + decrypt_aes128(ms, &seg); + } else if (me->encryption == true && me->encryptiontype == ENC_AES_SAMPLE) { + decrypt_sample_aes(ms, &seg); + } + fwrite(seg.data, 1, seg.len, pFile); + free(seg.data); + break; + } while(true); + + free(ms->url); + free(ms); + } + + pthread_join(thread, &ret); + pthread_mutex_destroy(&media_playlist_mtx); + + pthread_cond_destroy(&media_playlist_refresh_cond); + pthread_cond_destroy(&media_playlist_empty_cond); + + clean_http_session(session); + fclose(pFile); + return 0; +} + int download_hls(struct hls_media_playlist *me) { MSG_VERBOSE("Downloading segments.\n"); + MSG_API("{\"download_type\":\"vod\"}\n"); + MSG_API("{\"t_duration\":%d,\"d_duration\":0}\n", me->total_duration); char filename[MAX_FILENAME_LEN]; - if (hls_args.custom_filename) { strcpy(filename, hls_args.filename); } else { @@ -593,12 +907,35 @@ int download_hls(struct hls_media_playlist *me) } FILE *pFile = fopen(filename, "wb"); - + if (pFile == NULL) + { + MSG_ERROR("Error can not open output file\n"); + exit(1); + } + + int ret = 0; + void *session = init_http_session(); + assert(session); + + int downloaded_duration = 0; struct hls_media_segment *ms = me->first_media_segment; - while(ms) { + while(ms && ret == 0) { MSG_PRINT("Downloading part %d\n", ms->sequence_number); struct ByteBuffer seg; - seg.len = (int)get_data_from_url(ms->url, NULL, &(seg.data), BINARY); + size_t size = 0; + long http_code = get_data_from_url_with_session(&session, &(ms->url), (char **)&(seg.data), &size, BINARY, true); + seg.len = (int)size; + if (http_code != 200) { + MSG_API("{\"http_error_code\":%d}\n", (int) http_code); + if (seg.data) { + free(seg.data); + } + ret = 1; + break; + } + + downloaded_duration += ms->duration; + MSG_API("{\"t_duration\":%d,\"d_duration\":%d}\n", me->total_duration, downloaded_duration); if (me->encryption == true && me->encryptiontype == ENC_AES128) { decrypt_aes128(ms, &seg); } else if (me->encryption == true && me->encryptiontype == ENC_AES_SAMPLE) { @@ -610,8 +947,9 @@ int download_hls(struct hls_media_playlist *me) ms = ms->next; } + clean_http_session(session); fclose(pFile); - return 0; + return ret; } int print_enc_keys(struct hls_media_playlist *me) @@ -634,7 +972,6 @@ int print_enc_keys(struct hls_media_playlist *me) return 0; } - void media_playlist_cleanup(struct hls_media_playlist *me) { struct hls_media_segment *ms = me->first_media_segment; @@ -647,7 +984,7 @@ void media_playlist_cleanup(struct hls_media_playlist *me) free(ms); ms = me->first_media_segment; } - // me->first_media_segment = NULL; // <- this must be NULL already + assert(me->first_media_segment == NULL); me->last_media_segment = NULL; } diff --git a/src/hls.h b/src/hls.h index bd15089..cd640e1 100755 --- a/src/hls.h +++ b/src/hls.h @@ -12,6 +12,12 @@ #define KEYLEN 16 +#define MIN_REFRESH_DELAY 1 +#define MAX_REFRESH_DELAY 5 +#define MAX_RETRIES 3 +#define DWN_TIMEOUT 10 +#define LIVE_START_OFFSET 120 + struct enc_aes128 { bool iv_is_static; uint8_t iv_value[KEYLEN]; @@ -32,9 +38,11 @@ struct hls_media_playlist { char *source; unsigned int bitrate; int target_duration; + int total_duration; bool is_endlist; bool encryption; int encryptiontype; + int first_media_sequence; int last_media_sequence; struct hls_media_segment *first_media_segment; struct hls_media_segment *last_media_segment; @@ -48,9 +56,19 @@ struct hls_master_playlist { struct hls_media_playlist *media_playlist; }; +struct hls_playlist_updater_params { + struct hls_media_playlist *media_playlist; + void *media_playlist_mtx; + void *media_playlist_refresh_cond; + void *media_playlist_empty_cond; +}; +typedef struct hls_playlist_updater_params hls_playlist_updater_params; + + int get_playlist_type(char *source); int handle_hls_master_playlist(struct hls_master_playlist *ma); int handle_hls_media_playlist(struct hls_media_playlist *me); +int download_live_hls(struct hls_media_playlist *me); int download_hls(struct hls_media_playlist *me); int print_enc_keys(struct hls_media_playlist *me); void print_hls_master_playlist(struct hls_master_playlist *ma); diff --git a/src/main.c b/src/main.c index f225386..9d6c14b 100755 --- a/src/main.c +++ b/src/main.c @@ -27,9 +27,15 @@ int main(int argc, const char * argv[]) char *hlsfile_source; struct hls_media_playlist media_playlist; memset(&media_playlist, 0x00, sizeof(media_playlist)); - - if (get_data_from_url(hls_args.url, &hlsfile_source, NULL, STRING) == 0) { - MSG_ERROR("No result from server.\n"); + char *url = strdup(hls_args.url); + size_t size = 0; + long http_code = get_data_from_url_ext(&url, &hlsfile_source, &size, STRING, true); + + + if (http_code != 200) { + MSG_API("{\"error_code\":%d, \"error_msg\":\"\"}\n", (int)http_code); + } else if (size == 0) { + MSG_API("{\"error_code\":-1, \"error_msg\":\"No result from server.\"}\n"); return 1; } @@ -38,7 +44,7 @@ int main(int argc, const char * argv[]) if (playlist_type == MASTER_PLAYLIST) { struct hls_master_playlist master_playlist; master_playlist.source = hlsfile_source; - master_playlist.url = strdup(hls_args.url); + master_playlist.url = strdup(url); if (handle_hls_master_playlist(&master_playlist)) { return 1; } @@ -95,13 +101,13 @@ int main(int argc, const char * argv[]) if (download_hls(&media_playlist)) { return 1; } + } else if (!media_playlist.is_endlist) { + if (download_live_hls(&media_playlist)) { + return 1; + } } - else { - /* Live download */ - - } - + free(url); media_playlist_cleanup(&media_playlist); curl_global_cleanup(); return 0; diff --git a/src/misc.c b/src/misc.c index c87458b..86157cc 100755 --- a/src/misc.c +++ b/src/misc.c @@ -28,7 +28,7 @@ int parse_argv(int argc, const char *argv[]) if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) { hls_args.loglevel++; } else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet")) { - hls_args.loglevel--; + hls_args.loglevel = -1; } else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--best")) { hls_args.use_best = 1; } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { diff --git a/src/msg.c b/src/msg.c index fd9b23e..5303799 100755 --- a/src/msg.c +++ b/src/msg.c @@ -9,34 +9,39 @@ int msg_print_va(int lvl, char *fmt, ...) int result = 0; va_list args; va_start(args, fmt); - - if (lvl == LVL_ERROR) { - fputs("Error: ", stderr); - result = vfprintf(stderr, fmt, args); - } - - if (lvl == LVL_WARNING) { - fputs("Warning: ", stderr); - result = vfprintf(stdout, fmt, args); - } - - if (lvl == LVL_VERBOSE) { - if (hls_args.loglevel > 0) { - result = vfprintf(stdout, fmt, args); + + if (hls_args.loglevel >= 0 || lvl == LVL_API) { + switch(lvl) + { + case LVL_API: + result = vfprintf(stderr, fmt, args); + break; + case LVL_ERROR: + fputs("Error: ", stderr); + result = vfprintf(stderr, fmt, args); + break; + case LVL_WARNING: + fputs("Warning: ", stderr); + result = vfprintf(stdout, fmt, args); + break; + case LVL_VERBOSE: + if (hls_args.loglevel > 0) { + result = vfprintf(stdout, fmt, args); + } + break; + case LVL_DBG: + if (hls_args.loglevel > 1) { + fputs("Debug: ", stdout); + result = vfprintf(stdout, fmt, args); + } + break; + case LVL_PRINT: + result = vfprintf(stdout, fmt, args); + break; + default: + break; } } - - if (lvl == LVL_DBG) { - if (hls_args.loglevel > 1) { - fputs("Debug: ", stdout); - result = vfprintf(stdout, fmt, args); - } - } - - if (lvl == LVL_PRINT) { - result = vfprintf(stdout, fmt, args); - } - va_end(args); return result; } diff --git a/src/msg.h b/src/msg.h index bd7a18e..d282088 100755 --- a/src/msg.h +++ b/src/msg.h @@ -6,7 +6,9 @@ #define LVL_VERBOSE 0x03 #define LVL_DBG 0x04 #define LVL_PRINT 0x05 +#define LVL_API 0x06 +#define MSG_API(...) msg_print_va(LVL_API, __VA_ARGS__) #define MSG_ERROR(...) msg_print_va(LVL_ERROR, __VA_ARGS__) #define MSG_WARNING(...) msg_print_va(LVL_WARNING, __VA_ARGS__) #define MSG_VERBOSE(...) msg_print_va(LVL_VERBOSE, __VA_ARGS__)