1073 lines
35 KiB
C
1073 lines
35 KiB
C
|
|
/*
|
|
* 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 <stdarg.h>
|
|
#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(URLContext *s, unsigned char *buf, int size)
|
|
{
|
|
url_lpbuf_t *lp;
|
|
int len = size;
|
|
int valid_data_can_read;
|
|
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(URLContext *s, 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;
|
|
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;
|
|
}
|