LCOV - code coverage report
Current view: top level - filters - in_http.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 201 278 72.3 %
Date: 2021-04-29 23:48:07 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2017-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / HTTP input filter using GPAC http stack
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : 
      27             : #include <gpac/filters.h>
      28             : #include <gpac/constants.h>
      29             : #include <gpac/download.h>
      30             : 
      31             : 
      32             : typedef enum
      33             : {
      34             :         GF_HTTPIN_STORE_DISK=0,
      35             :         GF_HTTPIN_STORE_DISK_KEEP,
      36             :         GF_HTTPIN_STORE_MEM,
      37             :         GF_HTTPIN_STORE_MEM_KEEP,
      38             :         GF_HTTPIN_STORE_NONE,
      39             :         GF_HTTPIN_STORE_NONE_KEEP,
      40             : } GF_HTTPInStoreMode;
      41             : 
      42             : enum
      43             : {
      44             :         HTTP_PCK_NONE=0,
      45             :         HTTP_PCK_OUT=1,
      46             :         HTTP_PCK_OUT_EOS=2,
      47             : };
      48             : 
      49             : typedef struct
      50             : {
      51             :         //options
      52             :         char *src;
      53             :         u32 block_size;
      54             :         GF_HTTPInStoreMode cache;
      55             :         GF_Fraction64 range;
      56             :         char *ext;
      57             :         char *mime;
      58             : 
      59             :         //internal
      60             :         Bool initial_ack_done;
      61             :         GF_DownloadManager *dm;
      62             : 
      63             :         //only one output pid declared
      64             :         GF_FilterPid *pid;
      65             : 
      66             :         GF_DownloadSession *sess;
      67             : 
      68             :         char *block;
      69             :         u32 pck_out;
      70             :         Bool is_end;
      71             :         u64 nb_read, file_size;
      72             :         FILE *cached;
      73             :         u32 blob_size;
      74             : 
      75             :         Bool do_reconfigure;
      76             :         Bool full_file_only;
      77             :         GF_Err last_state;
      78             :         Bool is_source_switch;
      79             :         Bool prev_was_init_segment;
      80             : } GF_HTTPInCtx;
      81             : 
      82           2 : static void httpin_notify_error(GF_Filter *filter, GF_HTTPInCtx *ctx, GF_Err e)
      83             : {
      84           2 :         if (filter && (ctx->last_state == GF_OK)) {
      85           2 :                 if (!ctx->initial_ack_done) {
      86           0 :                         gf_filter_setup_failure(filter, e);
      87           0 :                         ctx->initial_ack_done = GF_TRUE;
      88             :                 } else {
      89           2 :                         gf_filter_notification_failure(filter, e, GF_FALSE);
      90             :                 }
      91           2 :                 ctx->last_state = e;
      92             :         }
      93           2 : }
      94             : 
      95         117 : static GF_Err httpin_initialize(GF_Filter *filter)
      96             : {
      97         117 :         GF_HTTPInCtx *ctx = (GF_HTTPInCtx *) gf_filter_get_udta(filter);
      98             :         GF_Err e;
      99             :         char *server;
     100             :         u32 flags = 0;
     101             : 
     102         117 :         if (!ctx || !ctx->src) return GF_BAD_PARAM;
     103         117 :         ctx->dm = gf_filter_get_download_manager(filter);
     104         117 :         if (!ctx->dm) return GF_SERVICE_ERROR;
     105             : 
     106         117 :         ctx->block = gf_malloc(ctx->block_size +1);
     107             : 
     108             :         flags = GF_NETIO_SESSION_NOT_THREADED | GF_NETIO_SESSION_PERSISTENT;
     109         117 :         if (ctx->cache==GF_HTTPIN_STORE_MEM)
     110             :                 flags |= GF_NETIO_SESSION_MEMORY_CACHE;
     111         117 :         else if (ctx->cache==GF_HTTPIN_STORE_NONE)
     112             :                 flags |= GF_NETIO_SESSION_NOT_CACHED;
     113         115 :         else if (ctx->cache==GF_HTTPIN_STORE_DISK_KEEP)
     114             :                 flags |= GF_NETIO_SESSION_KEEP_CACHE;
     115         115 :         else if (ctx->cache==GF_HTTPIN_STORE_MEM_KEEP) {
     116             :                 flags |= GF_NETIO_SESSION_MEMORY_CACHE|GF_NETIO_SESSION_KEEP_FIRST_CACHE;
     117          67 :                 ctx->cache = GF_HTTPIN_STORE_MEM;
     118             :         }
     119          48 :         else if (ctx->cache==GF_HTTPIN_STORE_NONE_KEEP) {
     120             :                 flags |= GF_NETIO_SESSION_NOT_CACHED|GF_NETIO_SESSION_MEMORY_CACHE|GF_NETIO_SESSION_KEEP_FIRST_CACHE;
     121           0 :                 ctx->cache = GF_HTTPIN_STORE_NONE;
     122             :         }
     123             : 
     124         117 :         server = strstr(ctx->src, "://");
     125         117 :         if (server) server += 3;
     126         117 :         if (server && strstr(server, "://")) {
     127           5 :                 ctx->is_end = GF_TRUE;
     128           5 :                 return gf_filter_pid_raw_new(filter, server, server, NULL, NULL, NULL, 0, GF_FALSE, &ctx->pid);
     129             :         }
     130             : 
     131         112 :         ctx->sess = gf_dm_sess_new(ctx->dm, ctx->src, flags, NULL, NULL, &e);
     132         112 :         if (e) {
     133           0 :                 gf_filter_setup_failure(filter, e);
     134           0 :                 ctx->initial_ack_done = GF_TRUE;
     135           0 :                 return e;
     136             :         }
     137         112 :         if (ctx->range.num || ctx->range.den) {
     138           9 :                 gf_dm_sess_set_range(ctx->sess, ctx->range.num, ctx->range.den, GF_TRUE);
     139             :         }
     140             : 
     141             : #ifdef GPAC_ENABLE_COVERAGE
     142         112 :         if (gf_sys_is_cov_mode())
     143             :                 httpin_notify_error(NULL, NULL, GF_OK);
     144             : #endif
     145             : 
     146         112 :         return GF_OK;
     147             : }
     148             : 
     149             : 
     150             : static void httpin_set_eos(GF_HTTPInCtx *ctx)
     151             : {
     152             :         //no pending packets, signal eos right away
     153         653 :         if (!ctx->pck_out) {
     154          41 :                 gf_filter_pid_set_eos(ctx->pid);
     155             :         }
     156             :         else
     157         612 :                 ctx->pck_out = HTTP_PCK_OUT_EOS;
     158             : }
     159             : 
     160             : 
     161         117 : void httpin_finalize(GF_Filter *filter)
     162             : {
     163         117 :         GF_HTTPInCtx *ctx = (GF_HTTPInCtx *) gf_filter_get_udta(filter);
     164             : 
     165         117 :         if (ctx->sess) gf_dm_sess_del(ctx->sess);
     166             : 
     167         117 :         if (ctx->block) gf_free(ctx->block);
     168         117 :         if (ctx->cached) gf_fclose(ctx->cached);
     169         117 : }
     170             : 
     171        2939 : static GF_FilterProbeScore httpin_probe_url(const char *url, const char *mime_type)
     172             : {
     173        2939 :         if (!strnicmp(url, "http://", 7) ) return GF_FPROBE_SUPPORTED;
     174        2848 :         if (!strnicmp(url, "https://", 8) ) return GF_FPROBE_SUPPORTED;
     175        2821 :         if (!strnicmp(url, "gmem://", 7) ) return GF_FPROBE_SUPPORTED;
     176        2821 :         return GF_FPROBE_NOT_SUPPORTED;
     177             : }
     178             : 
     179        5622 : static void httpin_rel_pck(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPacket *pck)
     180             : {
     181        5622 :         GF_HTTPInCtx *ctx = (GF_HTTPInCtx *) gf_filter_get_udta(filter);
     182             : 
     183        5622 :         if (ctx->pck_out==HTTP_PCK_OUT_EOS) {
     184         612 :                 gf_filter_pid_set_eos(ctx->pid);
     185             :         }
     186        5622 :         ctx->pck_out = HTTP_PCK_NONE;
     187             :         //ready to process again
     188        5622 :         gf_filter_post_process_task(filter);
     189        5622 : }
     190             : 
     191         633 : static Bool httpin_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     192             : {
     193             :         GF_Err e;
     194         633 :         GF_HTTPInCtx *ctx = (GF_HTTPInCtx *) gf_filter_get_udta(filter);
     195             : 
     196         633 :         if (evt->base.on_pid && (evt->base.on_pid != ctx->pid)) return GF_FALSE;
     197             : 
     198         633 :         switch (evt->base.type) {
     199             :         //we only check PLAY for full_file_only hint
     200           0 :         case GF_FEVT_PLAY:
     201           0 :                 ctx->full_file_only = evt->play.full_file_only;
     202             :                 //do NOT reset is_end to false, restarting the session is always done via a source_seek event
     203           0 :                 return GF_TRUE;
     204          88 :         case GF_FEVT_STOP:
     205          88 :                 if (!ctx->is_end) {
     206          41 :                         ctx->is_end = GF_TRUE;
     207             :                         //abort session
     208          41 :                         if (ctx->sess) {
     209          41 :                                 GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTPIn] Stop requested, aborting download %s (pck out %d) this %p\n", ctx->src, ctx->pck_out, ctx) );
     210          41 :                                 gf_dm_sess_abort(ctx->sess);
     211          41 :                                 gf_dm_sess_del(ctx->sess);
     212          41 :                                 ctx->sess = NULL;
     213             :                         }
     214          41 :                         httpin_set_eos(ctx);
     215             :                 }
     216             :                 return GF_TRUE;
     217           0 :         case GF_FEVT_SOURCE_SEEK:
     218           0 :                 if (evt->seek.start_offset < ctx->file_size) {
     219           0 :                         ctx->is_end = GF_FALSE;
     220             :                         //open cache if needed
     221           0 :                         if (!ctx->cached && ctx->file_size && (ctx->nb_read==ctx->file_size) && ctx->sess) {
     222           0 :                                 const char *cached = gf_dm_sess_get_cache_name(ctx->sess);
     223           0 :                                 if (cached) ctx->cached = gf_fopen(cached, "rb");
     224             :                         }
     225           0 :                         ctx->nb_read = evt->seek.start_offset;
     226             : 
     227           0 :                         if (ctx->cached) {
     228           0 :                                 gf_fseek(ctx->cached, ctx->nb_read, SEEK_SET);
     229           0 :                         } else if (ctx->sess) {
     230           0 :                                 gf_dm_sess_abort(ctx->sess);
     231           0 :                                 gf_dm_sess_set_range(ctx->sess, ctx->nb_read, 0, GF_TRUE);
     232             :                         }
     233           0 :                         ctx->range.den = 0;
     234           0 :                         ctx->range.num = ctx->nb_read;
     235           0 :                         ctx->last_state = GF_OK;
     236             :                 } else {
     237           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTPIn] Requested seek outside file range !\n") );
     238           0 :                         ctx->is_end = GF_TRUE;
     239             : 
     240           0 :                         httpin_set_eos(ctx);
     241             :                 }
     242             :                 return GF_TRUE;
     243         544 :         case GF_FEVT_SOURCE_SWITCH:
     244         544 :                 if (evt->seek.source_switch) {
     245             :                         assert(ctx->is_end);
     246             :                         assert(!ctx->pck_out);
     247         544 :                         if (ctx->src && ctx->sess && (ctx->cache!=GF_HTTPIN_STORE_DISK_KEEP) && !ctx->prev_was_init_segment) {
     248         510 :                                 gf_dm_delete_cached_file_entry_session(ctx->sess, ctx->src);
     249             :                         }
     250         544 :                         GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTPIn] Switch from %s to %s\n", gf_file_basename(ctx->src), gf_file_basename(evt->seek.source_switch) ));
     251         544 :                         if (ctx->src) gf_free(ctx->src);
     252         544 :                         ctx->src = gf_strdup(evt->seek.source_switch);
     253             :                 } else {
     254           0 :                         if (!ctx->is_end) {
     255           0 :                                 gf_filter_pid_set_eos(ctx->pid);
     256           0 :                                 ctx->is_end = GF_TRUE;
     257           0 :                                 if (ctx->sess) {
     258           0 :                                         gf_dm_sess_abort(ctx->sess);
     259           0 :                                         gf_dm_sess_del(ctx->sess);
     260           0 :                                         ctx->sess = NULL;
     261             :                                 }
     262             :                         }
     263           0 :                         if (ctx->src) gf_free(ctx->src);
     264           0 :                         ctx->src = NULL;
     265           0 :                         return GF_TRUE;
     266             :                 }
     267         544 :                 if (ctx->cached) gf_fclose(ctx->cached);
     268         544 :                 ctx->cached = NULL;
     269         544 :                 ctx->blob_size = 0;
     270         544 :                 ctx->is_source_switch = GF_TRUE;
     271         544 :                 ctx->prev_was_init_segment = GF_FALSE;
     272             : 
     273             :                 //handle isobmff:// url
     274         544 :                 if (!strncmp(ctx->src, "isobmff://", 10)) {
     275             :                         GF_FilterPacket *pck;
     276           1 :                         gf_filter_pid_raw_new(filter, ctx->src, ctx->src, NULL, NULL, NULL, 0, GF_FALSE, &ctx->pid);
     277           1 :                         ctx->is_end = GF_TRUE;
     278           1 :                         pck = gf_filter_pck_new_shared(ctx->pid, ctx->block, 0, httpin_rel_pck);
     279           1 :                         if (!pck) return GF_TRUE;
     280           1 :                         gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
     281             : 
     282           1 :                         ctx->pck_out = GF_TRUE;
     283           1 :                         gf_filter_pck_send(pck);
     284             : 
     285           1 :                         gf_filter_pid_set_eos(ctx->pid);
     286           1 :                         return GF_TRUE;
     287             :                 }
     288         543 :                 ctx->prev_was_init_segment = evt->seek.is_init_segment;
     289             : 
     290             :                 //abort type
     291         543 :                 if (evt->seek.start_offset == (u64) -1) {
     292           0 :                         if (!ctx->is_end) {
     293           0 :                                 if (ctx->sess)
     294           0 :                                         gf_dm_sess_abort(ctx->sess);
     295           0 :                                 ctx->is_end = GF_TRUE;
     296           0 :                                 httpin_set_eos(ctx);
     297             :                         }
     298           0 :                         ctx->nb_read = 0;
     299           0 :                         ctx->last_state = GF_OK;
     300           0 :                         return GF_TRUE;
     301             :                 }
     302         543 :                 ctx->last_state = GF_OK;
     303         543 :                 if (ctx->sess) {
     304         538 :                         if ((ctx->cache==GF_HTTPIN_STORE_MEM) && evt->seek.is_init_segment)
     305          29 :                                 gf_dm_sess_force_memory_mode(ctx->sess, 2);
     306         538 :                         e = gf_dm_sess_setup_from_url(ctx->sess, ctx->src, evt->seek.skip_cache_expiration);
     307             :                 } else {
     308             :                         u32 flags;
     309             : 
     310             :                         flags = GF_NETIO_SESSION_NOT_THREADED | GF_NETIO_SESSION_PERSISTENT;
     311           5 :                         if (ctx->cache==GF_HTTPIN_STORE_MEM) {
     312             :                                 flags |= GF_NETIO_SESSION_MEMORY_CACHE;
     313           5 :                                 if (evt->seek.is_init_segment)
     314             :                                         flags |= GF_NETIO_SESSION_KEEP_FIRST_CACHE;
     315             :                         }
     316           0 :                         else if (ctx->cache==GF_HTTPIN_STORE_NONE) flags |= GF_NETIO_SESSION_NOT_CACHED;
     317             : 
     318           5 :                         ctx->sess = gf_dm_sess_new(ctx->dm, ctx->src, flags, NULL, NULL, &e);
     319             :                 }
     320             : 
     321         543 :                 if (!e && (evt->seek.start_offset || evt->seek.end_offset))
     322          37 :             e = gf_dm_sess_set_range(ctx->sess, evt->seek.start_offset, evt->seek.end_offset, GF_TRUE);
     323             :                 
     324         543 :         if (e) {
     325             :                         //use info and not error, as source switch is done by dashin and can be scheduled too early in live cases
     326             :                         //but recovered later, so we let DASH report the error
     327           2 :                         GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTPIn] Cannot resetup session from URL %s: %s\n", ctx->src, gf_error_to_string(e) ) );
     328           2 :                         httpin_notify_error(filter, ctx, e);
     329           2 :                         ctx->is_end = GF_TRUE;
     330           2 :                         if (ctx->src) gf_free(ctx->src);
     331           2 :                         ctx->src = NULL;
     332           2 :                         return GF_TRUE;
     333             :                 }
     334         541 :                 ctx->nb_read = ctx->file_size = 0;
     335         541 :                 ctx->do_reconfigure = GF_TRUE;
     336         541 :                 ctx->is_end = GF_FALSE;
     337         541 :                 ctx->last_state = GF_OK;
     338         541 :                 gf_filter_post_process_task(filter);
     339         541 :                 gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_FILE_CACHED, &PROP_BOOL(GF_FALSE) );
     340         541 :                 return GF_TRUE;
     341             :         default:
     342             :                 break;
     343             :         }
     344             :         return GF_TRUE;
     345             : }
     346             : 
     347             : 
     348             : 
     349      137163 : static GF_Err httpin_process(GF_Filter *filter)
     350             : {
     351             :         Bool is_start;
     352      137163 :         u32 nb_read=0;
     353             :         GF_FilterPacket *pck;
     354             :         GF_Err e=GF_OK;
     355      137163 :         u32 bytes_per_sec=0;
     356      137163 :         u64 bytes_done=0, total_size, byte_offset;
     357             :         GF_NetIOStatus net_status;
     358      137163 :         GF_HTTPInCtx *ctx = (GF_HTTPInCtx *) gf_filter_get_udta(filter);
     359             : 
     360             :         //until packet is released we return EOS (no processing), and ask for processing again upon release
     361      137163 :         if (ctx->pck_out)
     362             :                 return GF_EOS;
     363             : 
     364      136922 :         if (ctx->is_end)
     365             :                 return GF_EOS;
     366             : 
     367      136199 :         if (!ctx->sess)
     368             :                 return GF_EOS;
     369             : 
     370      136199 :         if (!ctx->pid) {
     371        8721 :                 if (ctx->nb_read)
     372             :             return GF_SERVICE_ERROR;
     373             :         } else {
     374             :                 //TODO: go on fetching data to cache even when not consuming, and reread from cache
     375      127478 :                 if (gf_filter_pid_would_block(ctx->pid))
     376             :                         return GF_OK;
     377             :         }
     378             : 
     379      136199 :         is_start = ctx->nb_read ? GF_FALSE : GF_TRUE;
     380      136199 :         ctx->is_end = GF_FALSE;
     381             : 
     382             :         //we read from cache file
     383      136199 :         if (ctx->cached) {
     384             :                 u32 to_read;
     385           0 :                 u64 lto_read = ctx->file_size - ctx->nb_read;
     386             : 
     387           0 :                 if (lto_read > (u64) ctx->block_size)
     388             :                         to_read = (u64) ctx->block_size;
     389             :                 else
     390           0 :                         to_read = (u32) lto_read;
     391             : 
     392           0 :                 if (ctx->full_file_only) {
     393           0 :                         pck = gf_filter_pck_new_shared(ctx->pid, ctx->block, 0, httpin_rel_pck);
     394           0 :                         if (!pck) return GF_OUT_OF_MEM;
     395           0 :                         ctx->is_end = GF_TRUE;
     396           0 :                         gf_filter_pck_set_framing(pck, is_start, ctx->is_end);
     397             : 
     398             :                         //mark packet out BEFORE sending, since the call to send() may destroy the packet if cloned
     399           0 :                         ctx->pck_out = HTTP_PCK_OUT;
     400           0 :                         gf_filter_pck_send(pck);
     401             : 
     402           0 :                         httpin_set_eos(ctx);
     403             :                         return GF_EOS;
     404             :                 }
     405           0 :                 nb_read = (u32) gf_fread(ctx->block, to_read, ctx->cached);
     406           0 :                 bytes_per_sec = 0;
     407             :         }
     408      136199 :         else if (ctx->blob_size) {
     409             :                 u8 *b_data;
     410             :                 u32 b_size;
     411           5 :                 const char *cached = gf_dm_sess_get_cache_name(ctx->sess);
     412             :                 assert(cached);
     413             : 
     414           5 :                 gf_blob_get(cached, &b_data, &b_size, NULL);
     415             :                 assert(ctx->nb_read <= b_size);
     416           5 :                 nb_read = b_size - (u32) ctx->nb_read;
     417           5 :                 if (nb_read>ctx->block_size)
     418           4 :                         nb_read = ctx->block_size;
     419             : 
     420           5 :                 if (nb_read) {
     421           5 :                         memcpy(ctx->block, b_data + ctx->nb_read, nb_read);
     422             :                         e = GF_OK;
     423             :                 } else {
     424           0 :                         if (b_size == ctx->blob_size) {
     425           0 :                                 e = gf_dm_sess_fetch_data(ctx->sess, ctx->block, ctx->block_size, &nb_read);
     426             :                         } else {
     427           0 :                                 ctx->blob_size = b_size;
     428             :                         }
     429             :                 }
     430           5 :         gf_blob_release(cached);
     431             :         }
     432             :         //we read from network
     433             :         else {
     434             : 
     435      136194 :                 e = gf_dm_sess_fetch_data(ctx->sess, ctx->block, ctx->block_size, &nb_read);
     436      136194 :                 if (e<0) {
     437       88459 :                         if (e==GF_IP_NETWORK_EMPTY) {
     438       88459 :                                 if (ctx->pid) {
     439       87873 :                                         gf_dm_sess_get_stats(ctx->sess, NULL, NULL, NULL, NULL, &bytes_per_sec, NULL);
     440       87873 :                                         gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_RATE, &PROP_UINT(8*bytes_per_sec) );
     441             :                                 }
     442       88459 :                                 gf_filter_ask_rt_reschedule(filter, 1000);
     443       88459 :                                 return GF_OK;
     444             :                         }
     445           0 :                         if (! ctx->nb_read)
     446           0 :                                 httpin_notify_error(filter, ctx, e);
     447             : 
     448           0 :                         ctx->is_end = GF_TRUE;
     449             : 
     450             :                         //do not return an error if first fetch after source switch fails with removed or 404, this happens in DASH dynamic
     451             :                         //and the error might be absorbed by the dash demux later
     452             :                         //also do not signal eos in that case, this could trigger the consuming filter (dashdmx, filein) to consider the stream is in regular EOS
     453             :                         //and not wait for notify failure
     454           0 :                         if (ctx->is_source_switch && !ctx->nb_read && ((e==GF_URL_REMOVED) || (e==GF_URL_ERROR)))
     455             :                                 return GF_OK;
     456             : 
     457             :                         //no packet out, we can signal eos, except for source switch failures
     458           0 :                         if (ctx->pid) {
     459           0 :                                 gf_filter_pid_set_eos(ctx->pid);
     460             :                         }
     461             : 
     462           0 :                         gf_dm_sess_abort(ctx->sess);
     463           0 :                         ctx->is_source_switch = GF_FALSE;
     464           0 :                         return e;
     465             :                 }
     466       47735 :                 gf_dm_sess_get_stats(ctx->sess, NULL, NULL, &total_size, &bytes_done, &bytes_per_sec, &net_status);
     467             : 
     468             :                 //wait until we have some data to declare the pid
     469       47735 :         if ((e!= GF_EOS) && !nb_read) {
     470       42119 :             gf_filter_ask_rt_reschedule(filter, 1000);
     471       42119 :             return GF_OK;
     472             :         }
     473             : 
     474        5616 :                 if (!ctx->pid || ctx->do_reconfigure) {
     475             :                         u32 idx;
     476             :                         GF_Err cfg_e;
     477             :                         const char *hname, *hval;
     478         635 :                         const char *cached = gf_dm_sess_get_cache_name(ctx->sess);
     479             : 
     480         635 :                         ctx->do_reconfigure = GF_FALSE;
     481             : 
     482         635 :                         if ((e==GF_EOS) && cached) {
     483         318 :                                 if (!strnicmp(cached, "gmem://", 7)) {
     484             :                                         u8 *b_data;
     485             :                                         u32 b_size, b_flags;
     486         296 :                                         gf_blob_get(cached, &b_data, &b_size, &b_flags);
     487         296 :                                         if (b_size>ctx->block_size) {
     488           1 :                                                 ctx->blob_size = b_size;
     489           1 :                                                 b_size = ctx->block_size;
     490             :                                                 e = GF_OK;
     491             :                                         }
     492             :                                         assert(! (b_flags&GF_BLOB_IN_TRANSFER));
     493         296 :                                         memcpy(ctx->block, b_data, b_size);
     494         296 :                                         nb_read = b_size;
     495         296 :                                         gf_blob_release(cached);
     496             :                                 } else {
     497          22 :                                         ctx->cached = gf_fopen(cached, "rb");
     498          22 :                                         if (ctx->cached) {
     499          22 :                                                 nb_read = (u32) gf_fread(ctx->block, ctx->block_size, ctx->cached);
     500             :                                         } else {
     501           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTPIn] Failed to open cached file %s\n", cached));
     502             :                                         }
     503             :                                 }
     504             :                         }
     505         635 :                         ctx->block[nb_read] = 0;
     506         635 :                         cfg_e = gf_filter_pid_raw_new(filter, ctx->src, cached, ctx->mime ? ctx->mime : gf_dm_sess_mime_type(ctx->sess), ctx->ext, ctx->block, nb_read, ctx->mime ? GF_TRUE : GF_FALSE, &ctx->pid);
     507         635 :                         if (cfg_e) return cfg_e;
     508             : 
     509         635 :                         gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_FILE_CACHED, &PROP_BOOL(GF_FALSE) );
     510             : 
     511         635 :                         if (!ctx->initial_ack_done) {
     512         117 :                                 ctx->initial_ack_done = GF_TRUE;
     513         117 :                                 gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_DOWNLOAD_SESSION, &PROP_POINTER( (void*)ctx->sess ) );
     514             :                         }
     515             : 
     516             :                         /*in test mode don't expose http headers (they contain date/version/etc)*/
     517         635 :                         if (! gf_sys_is_test_mode()) {
     518          27 :                                 idx = 0;
     519         380 :                                 while (gf_dm_sess_enum_headers(ctx->sess, &idx, &hname, &hval) == GF_OK) {
     520         326 :                                         gf_filter_pid_set_property_dyn(ctx->pid, (char *) hname, & PROP_STRING(hval));
     521             :                                 }
     522             :                         }
     523             :                 }
     524             :                 //update file size at each call to get_stats, since we may use a dynamic blob in case of route
     525        5616 :                 ctx->file_size = total_size;
     526             : 
     527        5616 :                 gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_RATE, &PROP_UINT(8*bytes_per_sec) );
     528        5616 :                 if (ctx->range.num && ctx->file_size) {
     529           0 :                         gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_BYTES, &PROP_LONGUINT(bytes_done + ctx->range.num) );
     530           0 :                         gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_SIZE, &PROP_LONGUINT(ctx->file_size) );
     531             :                 } else {
     532        5616 :                         gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_BYTES, &PROP_LONGUINT(bytes_done) );
     533        5616 :                         gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_SIZE, &PROP_LONGUINT(ctx->file_size ? ctx->file_size : bytes_done) );
     534             :                 }
     535             :         }
     536             : 
     537        5621 :         byte_offset = ctx->nb_read;
     538             : 
     539        5621 :         ctx->nb_read += nb_read;
     540        5621 :         if (ctx->file_size && (ctx->nb_read==ctx->file_size)) {
     541         609 :                 if (net_status!=GF_NETIO_DATA_EXCHANGE)
     542         609 :                         ctx->is_end = GF_TRUE;
     543        5012 :         } else if (e==GF_EOS) {
     544           3 :                 ctx->is_end = GF_TRUE;
     545             :         }
     546             : 
     547        5621 :         pck = gf_filter_pck_new_shared(ctx->pid, ctx->block, nb_read, httpin_rel_pck);
     548        5621 :         if (!pck) return GF_OUT_OF_MEM;
     549             : 
     550        5621 :         gf_filter_pck_set_cts(pck, 0);
     551             : 
     552        5621 :         gf_filter_pck_set_framing(pck, is_start, ctx->is_end);
     553        5621 :         gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1);
     554        5621 :         gf_filter_pck_set_byte_offset(pck, byte_offset);
     555             : 
     556             :         //mark packet out BEFORE sending, since the call to send() may destroy the packet if cloned
     557        5621 :         ctx->pck_out = HTTP_PCK_OUT;
     558        5621 :         gf_filter_pck_send(pck);
     559             : 
     560        5621 :         if (ctx->file_size && gf_filter_reporting_enabled(filter)) {
     561             :                 char szStatus[1024], *szSrc;
     562           0 :                 szSrc = gf_file_basename(ctx->src);
     563             : 
     564           0 :                 sprintf(szStatus, "%s: % 16"LLD_SUF" /% 16"LLD_SUF" (%02.02f) % 8d kbps", szSrc, (s64) bytes_done, (s64) ctx->file_size, ((Double)bytes_done*100.0)/ctx->file_size, bytes_per_sec*8/1000);
     565           0 :                 gf_filter_update_status(filter, (u32) (bytes_done*10000/ctx->file_size), szStatus);
     566             :         }
     567             : 
     568        5621 :         if (ctx->is_end) {
     569         612 :                 const char *cached = gf_dm_sess_get_cache_name(ctx->sess);
     570         612 :                 if (cached)
     571         610 :                         gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_FILE_CACHED, &PROP_BOOL(GF_TRUE) );
     572             : 
     573         612 :                 httpin_set_eos(ctx);
     574             :                 return GF_EOS;
     575             :         }
     576             : 
     577        5009 :         return ctx->pck_out ? GF_EOS : GF_OK;
     578             : }
     579             : 
     580             : 
     581             : 
     582             : #define OFFS(_n)        #_n, offsetof(GF_HTTPInCtx, _n)
     583             : 
     584             : static const GF_FilterArgs HTTPInArgs[] =
     585             : {
     586             :         { OFFS(src), "URL of source content", GF_PROP_NAME, NULL, NULL, 0},
     587             :         { OFFS(block_size), "block size used to read file", GF_PROP_UINT, "100000", NULL, GF_FS_ARG_HINT_ADVANCED},
     588             :         { OFFS(cache), "set cache mode\n"
     589             :         "- disk: cache to disk,  discard once session is no longer used\n"
     590             :         "- keep: cache to disk and keep\n"
     591             :         "- mem: stores to memory, discard once session is no longer used\n"
     592             :         "- mem_keep: stores to memory, keep after session is reassigned but move to `mem` after first download\n"
     593             :         "- none: no cache\n"
     594             :         "- none_keep: stores to memory, keep after session is reassigned but move to `none` after first download"
     595             :         , GF_PROP_UINT, "disk", "disk|keep|mem|mem_keep|none|none_keep", GF_FS_ARG_HINT_ADVANCED},
     596             :         { OFFS(range), "set byte range, as fraction", GF_PROP_FRACTION64, "0-0", NULL, 0},
     597             :         { OFFS(ext), "override file extension", GF_PROP_NAME, NULL, NULL, 0},
     598             :         { OFFS(mime), "set file mime type", GF_PROP_NAME, NULL, NULL, 0},
     599             :         {0}
     600             : };
     601             : 
     602             : static const GF_FilterCapability HTTPInCaps[] =
     603             : {
     604             :         CAP_UINT(GF_CAPS_OUTPUT,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     605             : };
     606             : 
     607             : GF_FilterRegister HTTPInRegister = {
     608             :         .name = "httpin",
     609             :         GF_FS_SET_DESCRIPTION("HTTP input")
     610             :         GF_FS_SET_HELP("This filter dispatch raw blocks from a remote HTTP resource into a filter chain.\n"
     611             :         "Block size can be adjusted using [-block_size](), and disk caching policies can be adjusted.\n"
     612             :         "Content format can be forced through [-mime]() and file extension can be changed through [-ext]().\n"
     613             :         "Note: Unless disabled at session level (see [-no-probe](CORE) ), file extensions are usually ignored and format probing is done on the first data block.")
     614             :         .private_size = sizeof(GF_HTTPInCtx),
     615             :         .flags = GF_FS_REG_BLOCKING,
     616             :         .args = HTTPInArgs,
     617             :         SETCAPS(HTTPInCaps),
     618             :         .initialize = httpin_initialize,
     619             :         .finalize = httpin_finalize,
     620             :         .process = httpin_process,
     621             :         .process_event = httpin_process_event,
     622             :         .probe_url = httpin_probe_url
     623             : };
     624             : 
     625             : 
     626        2877 : const GF_FilterRegister *httpin_register(GF_FilterSession *session)
     627             : {
     628        2877 :         return &HTTPInRegister;
     629             : }
     630             : 

Generated by: LCOV version 1.13