FFmpeg读取并定位代码中的视频

什么情况下视频会存储在代码中?

在没有文件系统时或者内存紧缺时,就需要把视频资源转成常量数组直接编译到代码中。

实现

实现方式

将常量数组分块放入内存,再从内存中读取数据。

从内存中读取数据

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 从内存中读取数据(或将数据输出到内存)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容