什么情况下视频会存储在代码中?
在没有文件系统时或者内存紧缺时,就需要把视频资源转成常量数组直接编译到代码中。
实现
实现方式
将常量数组分块放入内存,再从内存中读取数据。
从内存中读取数据
在 avformat_open_input()
之前初始化一个 AVIOContext
,而且将原本的 AVFormatContext
的指针pb( AVIOContext
类型)指向这个自行初始化 AVIOContext
。当自行指定了 AVIOContext
之后,avformat_open_input()
里面的 URL 参数就不起作用了(URL类型还是起作用的,最好填 NULL
或 ""
)。
#define ASSET_BUFFER_SIZE (32 * 1024) /* 内存块大小 */
AVFormatContext *ic = NULL;
ic = avformat_alloc_context();
ic->pb = avio_alloc_context((unsigned char*)av_malloc(ASSET_BUFFER_SIZE), ASSET_BUFFER_SIZE, 0,
is /* 回调函数上下文 */,
read_asset /* 读取数据的回调函数指针 */,
NULL /* 输出数据的回调函数指针 */,
seek_asset /* 定位数据的回调函数指针 */);
avformat_open_input(&ic, ""/* URL */, NULL, NULL);
/* 省略... */
exit:
if (ic) avformat_close_input(&ic);
读取视频
以下是读取文件线程的代码:
#define ASSET_BUFFER_SIZE (32 * 1024) /* 内存块大小 */
typedef struct _VideoState {
/* 省略... */
const asset_info_t* asset_info; /* 单个资源的描述信息,通过该对象获取常量数组 */
int64_t asset_offset; /* 记录获取数据的偏移量 */
char asset_name[32]; /* 资源名 */
} VideoState;
static int read_thread(void* arg) {
VideoState* is = arg;
int ret;
bool_t is_fail = FALSE;
AVFormatContext* ic = NULL;
/* 省略... */
ic = avformat_alloc_context();
if (!ic) {
av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");
ret = AVERROR(ENOMEM);
goto fail;
}
/* 省略... */
/* 通过 AWTK 中 的 assets_manager 获取常量数组 */
is->asset_info = assets_manager_ref(assets_manager(), ASSET_TYPE_DATA, is->asset_name);
is->asset_offset = 0;
ic->pb = avio_alloc_context((unsigned char*)av_malloc(ASSET_BUFFER_SIZE), ASSET_BUFFER_SIZE, 0,
is, read_asset, NULL, seek_asset);
avformat_open_input(&ic, "", NULL, NULL);
/* 省略... */
goto exit;
fail:
is_fail = TRUE;
exit:
/* 省略... */
if (ic) avformat_close_input(&ic);
return 0;
}
读取数据的回调函数实现:
/* 实现功能: 把buf_size字节数据送入buf,返回值为获取到的数据大小,返回0表示读取数据完成,返回-1表示出错。 */
static int read_asset(void* opaque, uint8_t* buf, int buf_size) {
VideoState* is = (VideoState*)opaque;
uint32_t ret_size = 0;
return_value_if_fail(is != NULL && is->asset_info != NULL, 0);
if (is->asset_offset < is->asset_info->size) {
uint32_t size = is->asset_info->size - is->asset_offset;
ret_size = tk_min(buf_size, size);
memcpy(buf, is->asset_info->data + is->asset_offset, ret_size);
is->asset_offset += ret_size; /* 记录数据的偏移量 */
}
return ret_size;
}
定位视频
static int64_t seek_asset(void* opaque, int64_t offset, int whence) {
VideoState* is = (VideoState*)opaque;
return_value_if_fail(is != NULL && is->asset_info != NULL, -1);
switch (whence) {
case AVSEEK_SIZE: { /* 不进行 seek 操作,而是要求返回 视频 大小 */
return (int64_t)is->asset_info->size;
} break;
case SEEK_SET: { /* 表示 offset 是相对 视频开头的位置 */
is->asset_offset = offset;
} break;
case SEEK_END: { /* 表示 offset 是相对 视频结尾的位置, offset通常小于0 */
is->asset_offset = is->asset_info->size + offset;
} break;
default: {
av_log(NULL, AV_LOG_FATAL, "seek_asset(): Do not implement \"whence\" = %d\n", whence);
return -1;
} break;
}
return (int64_t)(is->asset_info->data + offset); /* 返回数据地址 */
}
参考
AWTK:github.com/zlgopen/awtk
awtk-media-player:github.com/zlgopen/awtk-media-player
ffmpeg 从内存中读取数据(或将数据输出到内存)