/* * Buffered I/O for ffmpeg system * Copyright (c) 2000,2001 Fabrice Bellard * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * LOOPBUF READ * Write By zhi.zhou@amlogic.com * */ #include "libavutil/crc.h" #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio.h" #include #include "aviolpcache.h" #include "aviolpbuf.h" //#include "amconfigutils.h" //#include "../udrm/udrm.h" /* Pos buffer rp wp buffer_end | | | | ================================ | | |<--------------buffer_size-------------->| valid_data_too_read: if wp >= rp wp-rp else buffer_size-(rp-wp) valid_data_size:for seek; alway ++,utill seek to empte the buff; Can seek back size: min(valid_data_size-valid_data_too_read,data between buffe,rp) */ //#define LP_BUFFER_DEBUG #define LP_SK_DEBUG //#define LP_RD_DEBUG #define lp_print(level, fmt...) av_log(NULL, level, ##fmt) #ifdef LP_SK_DEBUG #define lp_sprint(level, fmt...) av_log(NULL, level, ##fmt) #else #define lp_sprint(level, fmt...) #endif #ifdef LP_RD_DEBUG #define lp_rprint(level, fmt...) av_log(NULL, level, ##fmt) #else #define lp_rprint(level, fmt...) #endif #ifdef LP_BUFFER_DEBUG #define lp_bprint(level, fmt...) av_log(NULL, level, ##fmt) #else #define lp_bprint(level, fmt...) #endif #define LP_ASSERT(x) \ do \ { \ if (!(x)) \ av_log(NULL, AV_LOG_INFO, "****\t\tERROR at line file%s=%d\n\n\n", __FILE__, __LINE__); \ } while (0) #define DEF_MAX_READ_SEEK (1024 * 1024 * 3) #define MAX_DECRYPT_DATA_LEN (20 * 188) int url_lpopen(URLContext *s, int size) { av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpopen=%d\n", __FUNCTION__, __LINE__, size); url_lpbuf_t *lp; int blocksize = 32 * 1024; int ret; float value = 0.0; int bufsize = 0; if (size == 0) { ret = 0; //am_getconfig_float("libplayer.ffmpeg.lpbufsizemax", &value); if (ret < 0 || value < 1024 * 32) { if (0) //(am_getconfig_bool_def("media.amplayer.low_ram", 0)) { size = IO_LP_BUFFER_SIZE / 8; } else { size = IO_LP_BUFFER_SIZE; } } else { size = (int)value; } } lp_bprint(AV_LOG_INFO, "url_lpopen=%d\n", size); if (!s) return -1; lp_bprint(AV_LOG_INFO, "url_lpopen2=%d\n", size); ret = 0; //am_getconfig_float("libplayer.ffmpeg.lpbufblocksize", &value); if (ret >= 0 && value >= 32) { blocksize = (int)value; } lp_sprint(AV_LOG_INFO, "lpbuffer block size=%d\n", blocksize); lp = av_mallocz(sizeof(url_lpbuf_t)); if (!lp) return AVERROR(ENOMEM); lp->buffer = av_malloc(size); if (!lp->buffer) { unsigned int failedsize = size / 2; /*if no memory used 1/2 size */ ret = 0; //am_getconfig_float("libplayer.ffmpeg.lpbuffaildsize", &value); if (ret >= 0 && value >= 1024) { failedsize = (unsigned int)value; } lp_sprint(AV_LOG_INFO, "malloc buf failed,used failed size=%d\n", failedsize); lp->buffer = av_malloc(failedsize); while (!lp->buffer) { failedsize = failedsize / 2; if (failedsize < 16 * 1024) { /*do't malloc too small size failed size*/ av_free(lp); return AVERROR(ENOMEM); } lp->buffer = av_malloc(failedsize); } bufsize = failedsize; } else { bufsize = size; } lp_sprint(AV_LOG_INFO, "url_lpopen used lp buf size=%d\n", bufsize); s->lpbuf = lp; lp->buffer_size = bufsize; lp->rp = lp->buffer; lp->wp = lp->buffer; lp->buffer_end = lp->buffer + bufsize; lp->valid_data_size = 0; lp->pos = 0; lp->block_read_size = FFMIN(blocksize, bufsize >> 4); lp_lock_init(&lp->mutex, NULL); lp->file_size = url_lpseek(s, 0, AVSEEK_SIZE); lp->cache_enable = 0; lp->cache_id = aviolp_cache_open(s->filename, url_lpseek(s, 0, AVSEEK_SIZE)); lp->dbg_cnt = 0; ret = 0; //am_getconfig_float("libplayer.ffmpeg.lpbufmaxbuflv", &value); if (ret < 0) lp->max_forword_level = 1; else { lp->max_forword_level = value; } if (lp->cache_id != 0) lp->cache_enable = 1; lp_bprint(AV_LOG_INFO, "url_lpopen4%d\n", bufsize); ret = 0; //am_getconfig_float("libplayer.ffmpeg.maxreadseek", &value); if (ret >= 0 && value >= 0) { lp->max_read_seek = value; } else { lp->max_read_seek = DEF_MAX_READ_SEEK; } lp->drm_ts_flags = 0; lp->decrypt_data = NULL; lp->decrypt_data_len = 0; lp->decrypt_read_len = 0; lp->need_find_ts_start_pos = 1; lp->ts_tail_len = 0; lp->ts_tail_pos = 0; lp->drm_handle = 0; lp->fp1 = NULL; lp->fp2 = NULL; return 0; } #if 0 int url_lpopen_ex(URLContext *s, int size, int flags, int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), int64_t (*seek)(void *opaque, int64_t offset, int whence)) { int ret; URLContext *uc=s; uc->av_class = NULL; uc->filename = (char *)NULL; uc->prot =uc+ sizeof(URLContext) ; uc->flags = flags; uc->is_streamed = 0; /* default = not streamed */ uc->max_packet_size = 0; /* default: stream file */ ret=url_lpopen(uc,size); if (ret == 0) { uc->prot->url_read=read_packet; uc->prot->url_seek=seek; uc->prot->url_exseek=seek; uc->prot->flags=uc->flags ; }else{ } return ret; } #endif static int url_ts_probe(unsigned char *data, int len) { unsigned char *buf = data; unsigned char scan_num = 188; do { if ((len - 188 + scan_num) > 188 * 3 && buf[0] == 0x47 && buf[188] == 0x47 && buf[188 * 2] == 0x47) return 188 - scan_num; //ts format scan_num--; buf++; } while (scan_num > 0); return -1; //other format } int url_lpopen_ex(URLContext *s, int size, int flags, int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), int64_t (*seek)(void *opaque, int64_t offset, int whence)) { int ret; URLContext *uc = s; if (!uc->prot) { lp_print(AV_LOG_INFO, "url_lpopen_ex failed\n"); return -1; } uc->av_class = NULL; uc->filename = (char *)NULL; uc->flags = flags; uc->is_streamed = 0; /* default = not streamed */ uc->max_packet_size = 0; /* default: stream file */ // uc->UrlCtx.prot->url_read = read_packet; // uc->UrlCtx.prot->url_seek = seek; // uc->UrlCtx.prot->url_exseek = seek; // uc->UrlCtx.prot->flags = uc->UrlCtx.flags; ret = url_lpopen(uc, size); if (ret < 0) { lp_print(AV_LOG_INFO, " url_lpopen -failed\n"); return -1; } return ret; } int url_lpfillbuffer(URLContext *s, int size) { av_log(NULL, AV_LOG_DEBUG, "[%s,%d],size:%d,s->priv_data:%d", __FUNCTION__, __LINE__, size, s->priv_data); url_lpbuf_t *lp; int rlen = 0; int ssread; int cache_read_len = 0; int64_t tmprp; if (!s || !s->lpbuf) return AVERROR(EINVAL); lp = s->lpbuf; lp_lock(&lp->mutex); if (lp->wp >= lp->rp) { if (lp->rp != lp->buffer) ssread = FFMIN(size, lp->buffer_end - lp->wp); else ssread = FFMIN(size, lp->buffer_end - lp->wp - 32); } else ssread = FFMIN(size, lp->rp - lp->wp - 32); /*reversed 32bytes;*/ if (ssread <= 0) { rlen = 0; goto release; } if (lp->cache_enable) { /*do read on cache first*/ rlen = aviolp_cache_read(lp->cache_id, lp->pos, lp->wp, ssread); cache_read_len = rlen; } if (rlen <= 0) { if (lp->file_size > 0 && lp->pos >= lp->file_size) { rlen = 0; /*file read end*/ goto release; } else if (lp->cache_enable) { /*maybe do read from cache file before,so seek it now*/ int64_t lowlevelpos = s->prot->url_seek(s, 0, SEEK_CUR); if (lowlevelpos > 0 && lowlevelpos != lp->pos) { int ret = s->prot->url_seek(s, lp->pos, SEEK_SET); if (ret != lp->pos) { rlen = -1; /*error*/ goto release; } } } tmprp = lp->pos; lp_unlock(&lp->mutex); /*release lock for long time read*/ if (lp->drm_ts_flags && lp->decrypt_data == NULL) { av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpfillbuffer begin init drm-------\n", __FUNCTION__, __LINE__); lp->decrypt_data = av_malloc(MAX_DECRYPT_DATA_LEN); if (lp->decrypt_data == NULL) { av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpfillbuffer decrypt_data malloc failed!!\n", __FUNCTION__, __LINE__); lp->drm_ts_flags = 0; } #if 0 else { //get decrypt handle lp->drm_handle = udrm_decrypt_start(0); if (lp->drm_handle == -1) { lp->drm_ts_flags = 0; lp->drm_handle = 0; lp_sprint(AV_LOG_ERROR, "url_lpfillbuffer udrm_decrypt_start error!!\n"); } else { lp_sprint(AV_LOG_INFO, "url_lpfillbuffer drm_handle=0x%x-------\n", lp->drm_handle); //clear lpbuf lp->rp = lp->buffer; lp->wp = lp->buffer; lp->valid_data_size = 0; lp->pos = 0; if (0)//(am_getconfig_bool_def("libplayer.ts.udrm.dumpfile", 0)) { lp->fp1 = fopen("/data/lp_recv.ts", "w"); if (lp->fp1 == NULL) lp_sprint(AV_LOG_INFO, "url_lpfillbuffer open lp_recv.ts failed!\n"); lp->fp2 = fopen("/data/lp_descrypt.ts", "w"); if (lp->fp2 == NULL) lp_sprint(AV_LOG_INFO, "url_lpfillbuffer open lp_descrypt.ts failed!\n"); } } } #endif } if (!lp->drm_ts_flags) { rlen = s->prot->url_read(s, lp->wp, ssread); } else { if (lp->decrypt_read_len == 0 && lp->decrypt_data_len == 0) { int read_len = 0; if (lp->ts_tail_len > 0) { //add ts tail memcpy(lp->decrypt_data, lp->decrypt_data + lp->ts_tail_pos, lp->ts_tail_len); lp->decrypt_data_len = lp->ts_tail_len; lp->ts_tail_len = 0; lp->ts_tail_pos = 0; av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpfillbuffer add ts_tail %d, [0]=0x%x\n", __FUNCTION__, __LINE__, lp->decrypt_data_len, lp->decrypt_data[0]); } //download ts data read_len = (lp->decrypt_data_len > 0) ? (MAX_DECRYPT_DATA_LEN - lp->decrypt_data_len) : MAX_DECRYPT_DATA_LEN; rlen = s->prot->url_read(s, lp->decrypt_data + lp->decrypt_data_len, read_len); lp->decrypt_data_len += (rlen >= 0 ? rlen : 0); lp->decrypt_read_len = 0; //lp_sprint(AV_LOG_INFO, "url_lpfillbuffer url_read_len=%d, rlen=%d\n", read_len, rlen); } } lp_lock(&lp->mutex); if (tmprp != lp->pos) rlen = AVERROR(EAGAIN); /*pos have changed,so I think we have a seek on read*/ av_log(NULL, AV_LOG_DEBUG, "[%s,%d],filled buffer from remote=%d\n", __FUNCTION__, __LINE__, rlen); } if (lp->drm_ts_flags) { if (lp->decrypt_read_len == 0 && lp->decrypt_data_len > 0) { int decrypt_len = 0; //check ts tail if (lp->decrypt_data_len % 188 != 0 && lp->need_find_ts_start_pos == 0) { lp->ts_tail_len = lp->decrypt_data_len % 188; lp->ts_tail_pos = lp->decrypt_data_len - lp->ts_tail_len; lp->decrypt_data_len -= lp->ts_tail_len; av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpfillbuffer check tail_len=%d, [0]=0x%x\n", __FUNCTION__, __LINE__, lp->ts_tail_len, lp->decrypt_data[lp->ts_tail_pos]); } if (lp->decrypt_data_len > 0 && lp->need_find_ts_start_pos == 1) { //check ts start pos int ts_start_pos = url_ts_probe(lp->decrypt_data, lp->decrypt_data_len); if (ts_start_pos > 0) { av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpfillbuffer truncate_len=%d\n", __FUNCTION__, __LINE__, ts_start_pos); lp->decrypt_data_len -= ts_start_pos; memcpy(lp->decrypt_data, lp->decrypt_data + ts_start_pos, lp->decrypt_data_len); //rlen = s->prot->url_read(s, lp->decrypt_data+lp->decrypt_data_len, ts_start_pos); //lp->decrypt_data_len += rlen; //check ts tail if (lp->decrypt_data_len % 188 != 0) { lp->ts_tail_len = lp->decrypt_data_len % 188; lp->decrypt_data_len -= lp->ts_tail_len; lp->ts_tail_pos = lp->decrypt_data_len; av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpfillbuffer truncate tail_len=%d(%d),[0]=0x%x\n", __FUNCTION__, __LINE__, lp->ts_tail_len, lp->decrypt_data_len + lp->ts_tail_len, lp->decrypt_data[lp->ts_tail_pos]); } } lp->need_find_ts_start_pos = 0; } if (lp->decrypt_data_len > 0) { //udrm decrypt if (lp->fp1) fwrite(lp->decrypt_data, lp->decrypt_data_len, 1, lp->fp1); #if 0 decrypt_len = udrm_ts_decrypt(lp->drm_handle, lp->decrypt_data, lp->decrypt_data_len, lp->decrypt_data, lp->decrypt_data_len); //lp_sprint(AV_LOG_INFO, "url_lpfillbuffer decrypt_len = %d, [0]=0x%x %d\n", // decrypt_len, lp->decrypt_data[0], lp->save_flag); if (decrypt_len >= 0) { lp->decrypt_data_len = decrypt_len; if (lp->fp2) fwrite(lp->decrypt_data, decrypt_len, 1, lp->fp2); } #endif //else // lp_sprint(AV_LOG_ERROR, "url_lpfillbuffer udrm_ts_decrypt error!!\n"); } } else { rlen = 0; } if (lp->decrypt_data_len > 0) { //fill decrypt_data to lpbuf if (lp->wp >= lp->rp) ssread = FFMIN(lp->decrypt_data_len - lp->decrypt_read_len, lp->buffer_end - lp->wp); else ssread = FFMIN(lp->decrypt_data_len - lp->decrypt_read_len, lp->rp - lp->wp); memcpy(lp->wp, lp->decrypt_data + lp->decrypt_read_len, ssread); lp->decrypt_read_len += ssread; if (lp->cache_enable && cache_read_len <= 0) /*not read from cache itself*/ aviolp_cache_write(lp->cache_id, lp->pos, lp->wp, ssread); lp->pos += ssread; lp->wp += ssread; lp->valid_data_size += ssread; if (lp->wp >= lp->buffer_end) { lp->wp = lp->buffer; } rlen = ssread; if (lp->decrypt_read_len >= lp->decrypt_data_len) lp->decrypt_read_len = lp->decrypt_data_len = 0; //lp_sprint(AV_LOG_INFO, "url_lpfillbuffer fill_len = %d\n", ssread); } } else if (rlen > 0) { if (lp->cache_enable && cache_read_len <= 0) /*not read from cache itself*/ aviolp_cache_write(lp->cache_id, lp->pos, lp->wp, rlen); lp->valid_data_size += rlen; lp->pos += rlen; lp->wp += rlen; if (lp->wp >= lp->buffer_end) lp->wp = lp->buffer; } release: lp_unlock(&lp->mutex); return rlen; } int url_lpread(void *void_p, unsigned char *buf, int size) { url_lpbuf_t *lp; int len = size; int valid_data_can_read; URLContext *s = (URLContext *)void_p; unsigned char *tbuf = buf; if (!s || !s->lpbuf) { return -1; } lp = s->lpbuf; lp_lock(&lp->mutex); av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpread:buffer=%x,rp=%x,wp=%x,end=%x,lp->valid_data_size=%d,pos=%lld\n", __FUNCTION__, __LINE__, lp->buffer, lp->rp, lp->wp, lp->buffer_end, lp->valid_data_size, lp->pos); while (len > 0) { if (lp->wp >= lp->rp) valid_data_can_read = lp->wp - lp->rp; else valid_data_can_read = lp->buffer_end - lp->rp; valid_data_can_read = FFMIN(len, valid_data_can_read); LP_ASSERT(valid_data_can_read >= 0); if (valid_data_can_read == 0) { int rlen; lp_unlock(&lp->mutex); if ((len < lp->block_read_size) && (lp->seekflags & LESS_BUFF_DATA)) rlen = url_lpfillbuffer(s, len); else rlen = url_lpfillbuffer(s, lp->block_read_size); if (rlen <= 0) { //lp_unlock(&lp->mutex); return ((size - len) > 0) ? (size - len) : rlen; } lp_lock(&lp->mutex); } av_log(NULL, AV_LOG_DEBUG, "[%s,%d],url_lpread:buffer=%x,rp=%x,wp=%x,end=%x,tbuf=%x,valid_data_can_read=%x(%d)\n", __FUNCTION__, __LINE__, lp->buffer, lp->rp, lp->wp, lp->buffer_end, tbuf, valid_data_can_read, valid_data_can_read); if (valid_data_can_read > 0) { if (tbuf != NULL) { memcpy(tbuf, lp->rp, valid_data_can_read); tbuf += valid_data_can_read; } lp->rp += valid_data_can_read; if (lp->rp >= lp->buffer_end) lp->rp = lp->buffer; len -= valid_data_can_read; } LP_ASSERT(lp->rp >= lp->buffer); LP_ASSERT(lp->rp < lp->buffer_end); } lp_unlock(&lp->mutex); return (size - len); } int64_t url_lpseek(void *void_p, int64_t offset, int whence) { av_log(NULL, AV_LOG_DEBUG, "[%s,%d]\n", __FUNCTION__, __LINE__); int64_t pos_on_read; url_lpbuf_t *lp; int valid_data_can_seek_forward; int valid_data_can_seek_back; URLContext *s = (URLContext *)void_p; int64_t offset1; int ret; if (!s || !s->lpbuf) return AVERROR(EINVAL); lp = s->lpbuf; lp_lock(&lp->mutex); lp_sprint(AV_LOG_INFO, "url_lpseek:offset=%lld whence=%d,buffer=%x,rp=%x,wp=%x,end=%x,pos=%lld\n", offset, whence, lp->buffer, lp->rp, lp->wp, lp->buffer_end, lp->pos); if (whence == AVSEEK_SIZE) { int64_t size; if (!s->prot->url_seek) { lp_unlock(&lp->mutex); return -1; } size = s->prot->url_seek(s, 0, AVSEEK_SIZE); if (size < 0 && size != AVERROR_STREAM_SIZE_NOTVALID) { if ((size = s->prot->url_seek(s, -1, SEEK_END)) < 0) { lp_unlock(&lp->mutex); return size; } size++; s->prot->url_seek(s, lp->pos, SEEK_SET); } lp_sprint(AV_LOG_INFO, "get file size=%lld\n", size); lp_unlock(&lp->mutex); return size; } else if (whence == SEEK_END) { if (!s->prot->url_seek) { lp_unlock(&lp->mutex); return -1; } if ((offset1 = s->prot->url_seek(s, offset, SEEK_END)) < 0) { lp_unlock(&lp->mutex); return offset1; } lp->rp = lp->buffer; lp->wp = lp->buffer; lp->valid_data_size = 0; lp->pos = offset1; lp->decrypt_data_len = 0; lp->decrypt_read_len = 0; lp->ts_tail_len = 0; lp->ts_tail_pos = 0; lp->need_find_ts_start_pos = 1; lp_unlock(&lp->mutex); return offset1; } if (whence != SEEK_CUR && whence != SEEK_SET) { lp_unlock(&lp->mutex); return AVERROR(EINVAL); } if (lp->wp >= lp->rp) valid_data_can_seek_forward = lp->wp - lp->rp; else valid_data_can_seek_forward = lp->buffer_size - (lp->rp - lp->wp); pos_on_read = lp->pos - valid_data_can_seek_forward; if (whence == SEEK_CUR) { offset1 = pos_on_read; if (offset == 0) { lp_unlock(&lp->mutex); return offset1; } offset += offset1; } valid_data_can_seek_back = FFMIN(lp->valid_data_size - valid_data_can_seek_forward, lp->buffer_size - valid_data_can_seek_forward - 64); if (valid_data_can_seek_back < 0) valid_data_can_seek_back = 0; offset1 = offset - pos_on_read; /*seek forword or back*/ lp_sprint(AV_LOG_INFO, "url_lpseek:pos_on_read=%lld,can seek forwart=%d,can seek bacd=%d,offset1=%lld\n", pos_on_read, valid_data_can_seek_forward, valid_data_can_seek_back, offset1); if (offset1 >= 0 && offset1 <= valid_data_can_seek_forward) { /*seek forward in lp buffer*/ lp_sprint(AV_LOG_INFO, "url_lpseek:buffer seek forword offset=%lld offset1=%lld whence=%d\n", offset, offset1, whence); lp->rp += (int)offset1; if (lp->rp >= lp->buffer_end) lp->rp -= lp->buffer_size; } else if (offset1 < 0 && (-offset1) <= valid_data_can_seek_back) { /*seek back in lp buffer*/ lp_sprint(AV_LOG_INFO, "url_lpseek:buffer seek back offset=%lld offset1=%lld whence=%d,(int)offset1=%d\n", offset, offset1, whence, (int)offset1); lp->rp += (int)offset1; if (lp->rp < lp->buffer) lp->rp += lp->buffer_size; } else if ((s->is_streamed && offset1 > 0) || /*can't suport seek,and can support read seek.*/ ((offset1 > 0 && s->is_slowmedia) && /*is slowmedia and seek formard*/ (offset1 < lp->buffer_size - lp->block_read_size && offset1 <= ((lp->seekflags & MORE_READ_SEEK) ? lp->max_read_seek * 4 : lp->max_read_seek)) && /*don't do too big size seek*/ (lp->file_size <= 0 || (lp->file_size > 0 && offset1 < (3 * lp->file_size / 4))) && /*if offset1>filesize*3/4,thendo first seek end,don't buffer*/ (offset1 <= ((lp->seekflags & LESS_READ_SEEK) ? lp->max_read_seek / 16 : ((lp->seekflags & MORE_READ_SEEK) ? lp->max_read_seek * 4 : lp->max_read_seek))))) /*do less readseek,if have less seek flags*/ { /*seek to buffer end,but buffer is not full,do read seek*/ int read_offset, ret; lp_sprint(AV_LOG_INFO, "url_lpseek:buffer read seek forward offset=%lld offset1=%lld whence=%d\n", offset, offset1, whence); lp->rp += valid_data_can_seek_forward; if (lp->rp >= lp->buffer_end) lp->rp -= lp->buffer_size; lp_unlock(&lp->mutex); read_offset = offset1 - valid_data_can_seek_forward; while (read_offset > 0) { ret = url_lpread(s, NULL, read_offset); /*do read seek*/ if (ret > 0) read_offset -= ret; else if (ret != AVERROR(EAGAIN)) { offset = ret; /*get error,exit now*/ break; } } lp_lock(&lp->mutex); } else { /*not support in buffer seek,do low level seek now*/ lp_sprint(AV_LOG_INFO, "url_lpseek:buffer lowlevel seek offset=%lld offset1=%lld whence=%d\n", offset, offset1, whence); if (!s->prot->url_seek) { lp_unlock(&lp->mutex); return -1; } if (lp->cache_enable && offset < lp->file_size) { /*if cache enable not need to seek here,seek on cache missed*/ ; /*do't do seek here*/ } else if ((offset1 = s->prot->url_seek(s, offset, SEEK_SET)) < 0) { lp->valid_data_size = 0; /*seek failed clear all old datas*/ #if 0 if ((s->seekflags & URL_SEEK_FAIL_NO_BACK) == 0) offset1 = s->prot->url_seek(s, lp->pos, SEEK_SET);/*clear the lowlevel errors*/ #endif lp_unlock(&lp->mutex); return offset1; } lp->rp = lp->buffer; lp->wp = lp->buffer; lp->valid_data_size = 0; lp->pos = offset; lp->decrypt_data_len = 0; lp->decrypt_read_len = 0; lp->ts_tail_len = 0; lp->ts_tail_pos = 0; lp->need_find_ts_start_pos = 1; } lp_sprint(AV_LOG_INFO, "url_lpseekend:offset=%lld whence=%d,buffer=%x,rp=%x,wp=%x,end=%x,pos=%lld\n", offset, whence, lp->buffer, lp->rp, lp->wp, lp->buffer_end, lp->pos); LP_ASSERT(lp->rp >= lp->buffer); LP_ASSERT(lp->rp < lp->buffer_end); lp_unlock(&lp->mutex); return offset; } int64_t url_lpexseekoffset(URLContext *s, int64_t offset) { int forward_data = 0; int back_data = 0; lp_print(AV_LOG_INFO, "[%s:%d] doing url_lpexseekoffset\n", __FUNCTION__, __LINE__); int buffer_validdata_size = url_lp_getbuffering_size(s, &forward_data, &back_data); if (buffer_validdata_size <= 0) { lp_print(AV_LOG_ERROR, "[%s:%d] LOOPBUFFER buffer is empty\n", __FUNCTION__, __LINE__); return -1; } int64_t seek_time = 0; #if 0 if ((seek_time = s->UrlCtx.prot->url_exseek(s, offset, AVSEEK_LOOPBUFFER_OFFSET)) < 0) { lp_print(AV_LOG_ERROR, "[%s:%d] LOOPBUFFER seek failed seek_time=%lld\n", __FUNCTION__, __LINE__, seek_time); return -1; } #endif int64_t seek_pos = 0; if (s->priv_info > buffer_validdata_size) { lp_print(AV_LOG_ERROR, "[%s:%d] LOOPBUFFER seek out of range\n", __FUNCTION__, __LINE__); return -1; } else if (s->priv_info < 0) { lp_print(AV_LOG_ERROR, "[%s:%d] LOOPBUFFER seek error, maybe cached hls data is not continuous !\n", __FUNCTION__, __LINE__); return -1; } else { seek_pos = forward_data - s->priv_info; } if (url_lpseek(s, seek_pos, SEEK_CUR) < 0) { lp_print(AV_LOG_ERROR, "[%s:%d] Failed to seek SEEK_CUR pos=%lld,time=%lld,forward_data=%d\n", __FUNCTION__, __LINE__, seek_pos, seek_time, forward_data); return -1; } lp_print(AV_LOG_INFO, "[%s:%d] seek SEEK_CUR pos=%lld,time=%lld,forward_data=%d\n", __FUNCTION__, __LINE__, seek_pos, seek_time, forward_data); return seek_time; } int64_t url_lpexseek(URLContext *s, int64_t offset, int whence) { url_lpbuf_t *lp; int64_t ret; if (!s || !s->lpbuf /*|| !s->UrlCtx.prot->url_exseek*/) return AVERROR(EINVAL); lp = s->lpbuf; lp_lock(&lp->mutex); if (whence == AVSEEK_FULLTIME) { // ret = s->UrlCtx.prot->url_exseek(s, 0, AVSEEK_FULLTIME); /*clear the lowlevel errors*/ } else if (whence == AVSEEK_BUFFERED_TIME) { // ret = s->UrlCtx.prot->url_exseek(s, 0, AVSEEK_BUFFERED_TIME); /*clear the lowlevel errors*/ } else if (whence == AVSEEK_TO_TIME) { if (0) //(s->UrlCtx.prot->url_exseek) { #if 0 if (strcmp(s->prot->name, "vhls") == 0) {// hls can return the stream offset lp_print( AV_LOG_INFO,"[%s:%d] doing LOOPBUFFER seek\n",__FUNCTION__,__LINE__); lp_unlock(&lp->mutex); ret = url_lpexseekoffset(s,offset); lp_lock(&lp->mutex); if (ret >= 0) { s->priv_info = AVSEEK_LOOPBUFFER_OFFSET; lp_print( AV_LOG_INFO,"[%s:%d] LOOPBUFFER seek deal successful\n",__FUNCTION__,__LINE__); goto seek_end; } s->priv_info = AVSEEK_TO_TIME; lp_print( AV_LOG_INFO,"[%s:%d] Failed LOOPBUFFER seek\n",__FUNCTION__,__LINE__); } #endif if (0) // ((ret = s->UrlCtx.prot->url_exseek(s, offset, AVSEEK_TO_TIME)) >= 0) { lp->rp = lp->buffer; lp->wp = lp->buffer; lp->valid_data_size = 0; lp->pos = 0; lp->decrypt_data_len = 0; lp->decrypt_read_len = 0; lp->ts_tail_len = 0; lp->ts_tail_pos = 0; lp->need_find_ts_start_pos = 1; goto seek_end; } } ret = AVERROR(EPIPE); } else if (whence == AVSEEK_CMF_TS_TIME) { if (0) //(s->UrlCtx.prot->url_exseek) { if (0) //((ret = s->UrlCtx.prot->url_exseek(s, offset, AVSEEK_CMF_TS_TIME)) >= 0) { lp->rp = lp->buffer; lp->wp = lp->buffer; lp->valid_data_size = 0; lp->pos = 0; lp->decrypt_data_len = 0; lp->decrypt_read_len = 0; lp->ts_tail_len = 0; lp->ts_tail_pos = 0; lp->need_find_ts_start_pos = 1; goto seek_end; } } ret = AVERROR(EPIPE); } else if (whence == AVSEEK_SLICE_BYTIME) { ret = s->prot->url_seek(s, offset, AVSEEK_SLICE_BYTIME); if (ret < 0) { lp_unlock(&lp->mutex); return -1; } lp_unlock(&lp->mutex); return ret; } else if (whence == AVSEEK_ITEM_TIME) { ret = s->prot->url_seek(s, offset, AVSEEK_ITEM_TIME); if (ret < 0) { lp_unlock(&lp->mutex); return -1; } lp_unlock(&lp->mutex); return ret; } seek_end: lp_unlock(&lp->mutex); return ret; } int url_lpreset(URLContext *s) { url_lpbuf_t *lp; if (!s || !s->lpbuf) { return AVERROR(EINVAL); } lp = s->lpbuf; if (lp) { lp->rp = lp->buffer; lp->wp = lp->buffer; lp->pos = 0; lp->valid_data_size = 0; lp->decrypt_data_len = 0; lp->decrypt_read_len = 0; lp->ts_tail_len = 0; lp->ts_tail_pos = 0; } return 0; } int url_lp_getbuffering_size(URLContext *s, int *forward_data, int *back_data) { url_lpbuf_t *lp; int valid_data_can_seek_forward; int valid_data_can_seek_back; int ret; if (!s || !s->lpbuf) return AVERROR(EINVAL); lp = s->lpbuf; lp_lock(&lp->mutex); if (lp->wp >= lp->rp) valid_data_can_seek_forward = lp->wp - lp->rp; else valid_data_can_seek_forward = lp->buffer_size - (lp->rp - lp->wp); valid_data_can_seek_back = FFMIN(lp->valid_data_size - valid_data_can_seek_forward, lp->buffer_size - valid_data_can_seek_forward - 64); if (valid_data_can_seek_back < 0) valid_data_can_seek_back = 0; lp_unlock(&lp->mutex); if (forward_data) *forward_data = valid_data_can_seek_forward; if (back_data) *back_data = valid_data_can_seek_back; return (valid_data_can_seek_back + valid_data_can_seek_forward); } int url_lp_set_seekflags(URLContext *s, int seekflagmask) { url_lpbuf_t *lp; if (!s || !s->lpbuf) return AVERROR(EINVAL); lp = s->lpbuf; lp->seekflags |= seekflagmask; return 0; } int url_lp_clear_seekflags(URLContext *s, int seekflagmask) { url_lpbuf_t *lp; if (!s || !s->lpbuf) return AVERROR(EINVAL); lp = s->lpbuf; lp->seekflags &= ~seekflagmask; return 0; } int64_t url_lp_get_buffed_pos(URLContext *s) { url_lpbuf_t *lp; int64_t pos; int buffer_in_cache = 0; if (!s || !s->lpbuf) return AVERROR(EINVAL); lp = s->lpbuf; pos = lp->pos; if (lp->cache_enable && !lp_trylock(&lp->mutex)) { if (lp->cache_enable) { buffer_in_cache = aviolp_cache_next_valid_bytes(lp->cache_id, pos, INT_MAX); if (buffer_in_cache > 0) pos += buffer_in_cache; } lp_unlock(&lp->mutex); } /*lp_sprint(AV_LOG_INFO,"buffered pos=%lld,file_size=%lld,percent=%d.%02d%%,buffer_in_cache=%d\n", pos,lp->file_size,(int)(pos*100/lp->file_size),(int)((pos*10000/lp->file_size)%100),buffer_in_cache); */ return pos; } int64_t url_buffed_size(AVIOContext *s) { if (!s) return 0; if (s->enabled_lp_buffer) { int64_t buffed = url_lp_get_buffed_pos(s->opaque) - avio_seek(s, 0, SEEK_CUR); return buffed > 0 ? buffed : 0; } else return 0; } int url_lp_intelligent_buffering(URLContext *s, int size) { int forward_data, back_data; int datalen; url_lpbuf_t *lp; int ret = -1; if (!s || !s->lpbuf) return AVERROR(EINVAL); lp = s->lpbuf; lp->dbg_cnt++; if (size <= 0) size = lp->block_read_size; datalen = url_lp_getbuffering_size(s, &forward_data, &back_data); if (lp->dbg_cnt % 100 == 0) lp_print(AV_LOG_DEBUG, "url_lp buffering:datalen=%d,forward_datad=%d,back_data=%d,lp->buffer_size=%d,size=%d\n", datalen, forward_data, back_data, lp->buffer_size, size); if (datalen >= 0 && ((forward_data / lp->buffer_size) < lp->max_forword_level) && ((datalen < lp->buffer_size - size - 1024) || (back_data > (forward_data / 2 + 1)) || (back_data > 3 * 1024 * 1024))) ret = url_lpfillbuffer(s, size); /*lest 1/3 back data && < 3M back data*/ return ret; } int url_lpfree(URLContext *s) { if (s->lpbuf) { lp_lock(&s->lpbuf->mutex); if (s->lpbuf->cache_enable) aviolp_cache_close(s->lpbuf->cache_id); /*release other threadlater...*/ lp_unlock(&s->lpbuf->mutex); if (s->lpbuf->buffer) av_free(s->lpbuf->buffer); if (s->lpbuf->decrypt_data) av_free(s->lpbuf->decrypt_data); #if 0 if (s->lpbuf->drm_handle != 0) { udrm_decrypt_stop(s->lpbuf->drm_handle); s->lpbuf->drm_handle = 0; } #endif if (s->lpbuf->fp1) { fclose(s->lpbuf->fp1); s->lpbuf->fp1 = NULL; } if (s->lpbuf->fp2) { fclose(s->lpbuf->fp2); s->lpbuf->fp2 = NULL; } av_free(s->lpbuf); s->lpbuf = NULL; } return 0; }