lhd666 发表于 2016-12-20 11:41

求教,ffmpeg怎么自定义输出流

本帖最后由 lhd666 于 2017-02-16 10:04 编辑

以前没搞过视频相关的东西,现在需要使用ffmpeg在ceph对象存储上进行转码。ffmpeg自带示例avio_reading.c演示了怎么自定义输入流,我参照它成功从ceph对象存储中读取到数据并解码,但是又该怎么自定义输出流呢?
ffmpeg示例中,也有不使用AVFormatContext,用fwrite()写入AVPacket的示例,那么我只需要把fwrite()替换成rados的写入操作即可,但是不知道avformat_write_header()和av_write_trailer()这两个api写入的数据该怎么使用fwrite()写入,归根结底还是对ffmpeg不熟,网上资料又少。
下面是自定义输入流的代码:
struct ceph_handler {
      rados_t cluster;
      rados_ioctx_t io;
      uint64_t off;
      char *objname;
};

struct ceph_handler *ceph_handler_create(const char * const clustername,
                               const char * const username,
                               uint64_t flags,
                               const char * const conf,
                               const char * const poolname,
                               const char * const objname)
{
      struct ceph_handler *handler;
      int err;

      handler = malloc(sizeof(*handler));
      if (!handler)
                return NULL;

      err = rados_create2(&handler->cluster, clustername, username, flags);
      if (err < 0) {
                free(handler);
                return NULL;
      }

      err = rados_conf_read_file(handler->cluster, conf);
      if (err < 0) {
                rados_shutdown(handler->cluster);
                free(handler);
                return NULL;
      }

      err = rados_connect(handler->cluster);
      if (err < 0) {
                rados_shutdown(handler->cluster);
                free(handler);
                return NULL;
      }

      err = rados_ioctx_create(handler->cluster, poolname, &handler->io);
      if (err < 0) {
                rados_shutdown(handler->cluster);
                free(handler);
                return NULL;
      }

      handler->objname = strdup(objname);
      if (!handler->objname) {
                rados_ioctx_destroy(handler->io);
                rados_shutdown(handler->cluster);
                free(handler);
                return NULL;
      }

      handler->off = 0;

      return handler;
}

void ceph_handler_destroy(struct ceph_handler *handler)
{
      rados_ioctx_destroy(handler->io);
      rados_shutdown(handler->cluster);
      free(handler->objname);
      free(handler);
}

static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
      struct ceph_handler *handler = opaque;
      int nr;

      nr = rados_read(handler->io, handler->objname, (char *)buf, buf_size, handler->off);
      if (nr < 0)
                errno = -nr;
      else
                handler->off += nr;

      return nr;
}

static int write_packet(void *opaque, uint8_t *buf, int buf_size)
{
      struct ceph_handler *handler = opaque;
      int err;

      /* 成功时返回0,而非写入字节数 */
      err = rados_write(handler->io, handler->objname, (char *)buf, buf_size, handler->off);
      if (err)
                errno = - err;
      else
                handler->off += buf_size;

      return err ? err : buf_size;
}

static int64_t seek(void *opaque, int64_t offset, int whence)
{
      struct ceph_handler *handler = opaque;
      uint64_t size;
      time_t mtime;
      int err;

      switch (whence) {
      case SEEK_SET:
                handler->off = offset;
                break;

      case SEEK_CUR:
                handler->off += offset;
                break;

      case SEEK_END:
                err = rados_stat(handler->io, handler->objname, &size, &mtime);
                if (err < 0) {
                        errno = - err;
                        return -1;
                }
                if (size < offset)
                        handler->off = 0;
                else
                        handler->off = size - offset;
      }

      return handler->off;
}

static int open_input_file(const char *filename)
{
      AVIOContext *avio_ctx = NULL;
      uint8_t *avio_ctx_buffer = NULL;
      size_t avio_ctx_buffer_size = 1 << 2;
      struct ceph_handler *handler = NULL;
      unsigned int i;
      int ret;

      ifmt_ctx = NULL;
      
      handler = ceph_handler_create("ceph", "client.admin", 0,
                                    "ceph.conf", "mypool", filename);
      if (!handler)
                return -1;

      ifmt_ctx = avformat_alloc_context();
      if (!ifmt_ctx)
                goto err;

      avio_ctx_buffer = av_malloc(avio_ctx_buffer_size);
      if (!avio_ctx_buffer)
                goto err;

      avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
                                    0, handler, read_packet, write_packet,
                                    seek);
      if (!avio_ctx)
                goto err;
      ifmt_ctx->pb = avio_ctx;

      ret = avformat_open_input(&ifmt_ctx, NULL, NULL, NULL);
      if (ret < 0)
                goto err;

      ret = avformat_find_stream_info(ifmt_ctx, NULL);
      if (ret < 0)
                goto err;

      for (i = 0; i < ifmt_ctx->nb_streams; i++) {
                AVStream *stream;
                AVCodecContext *codec_ctx;
                stream = ifmt_ctx->streams;
                codec_ctx = stream->codec;
                /* Reencode video & audio and remux subtitles etc. */
                if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
                  codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
                        /* Open decoder */
                        ret = avcodec_open2(codec_ctx,
                                          avcodec_find_decoder(codec_ctx->codec_id), NULL);
                        if (ret < 0)
                              goto err;
                }
      }

      
      av_dump_format(ifmt_ctx, 0, filename, 0);
      return 0;

err:
      if (ifmt_ctx)
                avformat_close_input(&ifmt_ctx);
      ifmt_ctx = NULL;
      if (handler)
                ceph_handler_destroy(handler);
      if (avio_ctx) {
                av_freep(&avio_ctx->buffer);
                av_freep(&avio_ctx);
      }

      return -1;
}

lhd666 发表于 2017-02-16 10:00

找方法了,在学习雷大的教程的时候,看到了相关介绍:http://blog.csdn.net/leixiaohua1020/article/details/39759623
和读的方法很相似,但是注意必须提供seek和write,否则有的视频编码会失败。看来ffmpeg没举输出流的例子,是觉得没有必要,归根结底还是自身水平太低,需要不断学习、练习和归纳总结{:qq9:}
页: [1]
查看完整版本: 求教,ffmpeg怎么自定义输出流