读取h264文件,将其内容映射进内存buffer,通过AVIOContext从从自定义回调函数输入数据,并解码成yuv数据。

仓库路径:https://gitee.com/liudegui/test_avio_reading

  • 示例程序演示了:读取h264文件,将其内容映射进内存buffer,通过AVIOContext从从自定义回调函数输入数据,并解码成yuv数据。
  • 整合官方示例程序 avio_readingdemuxing_decoding 这两个例子
  • 可以使用ffplayer播放,如ffplay -f rawvideo -pix_fmt yuvj420p -video_size 1920x1080 output.yuv

主要代码

// ref: http://ffmpeg.org/doxygen/trunk/avio_reading_8c-example.html
// ref: http://ffmpeg.org/doxygen/trunk/demuxing_decoding_8c-example.html 
 extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
}

#include <iostream>       // std::cout
#include <thread>         // std::thread

static AVFormatContext *fmt_ctx = NULL;
static AVCodecContext *video_dec_ctx = NULL;
static int width, height;
static enum AVPixelFormat pix_fmt;
static AVStream *video_stream = NULL;
static const char *src_filename = NULL;
static const char *video_dst_filename = NULL;
static FILE *video_dst_file = NULL;
static uint8_t *video_dst_data[4] = { NULL };
static int      video_dst_linesize[4];
static int video_dst_bufsize;
static int video_stream_idx = -1;
static AVFrame *frame = NULL;
static AVPacket pkt;
static int video_frame_count = 0;

#define AVIO_BUFFER_SIZE 32*1024

 static int open_codec_context(int *stream_idx,
     AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
 {
     int ret, stream_index;
     AVStream *st;
     AVCodec *dec = NULL;
     AVDictionary *opts = NULL;
     ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
     if (ret < 0) {
         fprintf(stderr, "Could not find %s stream in input file '%s'\n",
             av_get_media_type_string(type), src_filename);
         return ret;
     }
     else {
         stream_index = ret;
         st = fmt_ctx->streams[stream_index];
         /* find decoder for the stream */
         dec = avcodec_find_decoder(st->codecpar->codec_id);
         if (!dec) {
             fprintf(stderr, "Failed to find %s codec\n",
                 av_get_media_type_string(type));
             return AVERROR(EINVAL);
         }
         /* Allocate a codec context for the decoder */
         *dec_ctx = avcodec_alloc_context3(dec);
         if (!*dec_ctx) {
             fprintf(stderr, "Failed to allocate the %s codec context\n",
                 av_get_media_type_string(type));
             return AVERROR(ENOMEM);
         }
         /* Copy codec parameters from input stream to output codec context */
         if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
             fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
                 av_get_media_type_string(type));
             return ret;
         }
         /* Init the decoders */
         if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
             fprintf(stderr, "Failed to open %s codec\n",
                 av_get_media_type_string(type));
             return ret;
         }
         *stream_idx = stream_index;
     }
     return 0;
 }

 static int output_video_frame(AVFrame *frame)
 {
     if (frame->width != width || frame->height != height ||
         frame->format != pix_fmt) {
         /* To handle this change, one could call av_image_alloc again and
          * decode the following frames into another rawvideo file. */
         fprintf(stderr, "Error: Width, height and pixel format have to be "
             "constant in a rawvideo file, but the width, height or "
             "pixel format of the input video changed:\n"
             "old: width = %d, height = %d, format = %s\n"
             "new: width = %d, height = %d, format = %s\n",
             width, height, av_get_pix_fmt_name(pix_fmt),
             frame->width, frame->height,
             av_get_pix_fmt_name((AVPixelFormat)frame->format));
         return -1;
     }
     printf("video_frame n:%d coded_n:%d\n",
         video_frame_count++, frame->coded_picture_number);
     /* copy decoded frame to destination buffer:
      * this is required since rawvideo expects non aligned data */
     av_image_copy(video_dst_data, video_dst_linesize,
         (const uint8_t **)(frame->data), frame->linesize,
         pix_fmt, width, height);
     /* write to rawvideo file */
     fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);
     return 0;
 }

 static int decode_packet(AVCodecContext *dec, const AVPacket *pkt)
 {
     int ret = 0;
     // submit the packet to the decoder
     ret = avcodec_send_packet(dec, pkt);
     if (ret < 0) {
         fprintf(stderr, "Error submitting a packet for decoding (%d)\n", ret);
         return ret;
     }
     // get all the available frames from the decoder
     while (ret >= 0) {
         ret = avcodec_receive_frame(dec, frame);
         if (ret < 0) {
             // those two return values are special and mean there is no output
             // frame available, but there were no errors during decoding
             if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
                 return 0;
             fprintf(stderr, "Error during decoding (%d)\n", ret);
             return ret;
         }
         // write the frame data to output file
         if (dec->codec->type == AVMEDIA_TYPE_VIDEO)
             ret = output_video_frame(frame);

         av_frame_unref(frame);
         if (ret < 0)
             return ret;
     }
     return 0;
 }
struct buffer_data {
    uint8_t *ptr;
    size_t size; ///< size left in the buffer
    int index; // 自定义添加
};

static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
    std::thread::id t_id = std::this_thread::get_id();

    struct buffer_data *bd = (struct buffer_data *)opaque;
    buf_size = FFMIN(buf_size, bd->size);
    if (!buf_size)
        return AVERROR_EOF;
    printf("ptr:%p size:%zu pid:%d  index:%d\n ", bd->ptr, bd->size, t_id, bd->index);
    /* copy internal buffer data to buf */
    memcpy(buf, bd->ptr, buf_size);
    bd->ptr  += buf_size;
    bd->size -= buf_size;
    return buf_size;
}

// index为自定义添加,为了做标识
int test_avio_reading(char *filename, int index)
{
    AVFormatContext *fmt_ctx = NULL;
    AVIOContext *avio_ctx = NULL;
    uint8_t *buffer = NULL, *avio_ctx_buffer = NULL;
    size_t buffer_size, avio_ctx_buffer_size = AVIO_BUFFER_SIZE;
    char *input_filename = NULL;
    int ret = 0;
    struct buffer_data bd = { 0 };
    input_filename = filename;
    
    /* slurp file content into buffer */
    ret = av_file_map(input_filename, &buffer, &buffer_size, 0, NULL);
    if (ret < 0)
        goto end;
    /* fill opaque structure used by the AVIOContext read callback */
    bd.ptr = buffer;
    bd.size = buffer_size;
    bd.index = index;
    if (!(fmt_ctx = avformat_alloc_context())) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    avio_ctx_buffer = (uint8_t *)av_malloc(avio_ctx_buffer_size);
    if (!avio_ctx_buffer) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
        0, &bd, &read_packet, NULL, NULL);
    if (!avio_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    fmt_ctx->pb = avio_ctx;
    ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open input\n");
        goto end;
    }

    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not find stream information\n");
        goto end;
    }
    av_dump_format(fmt_ctx, 0, input_filename, 0);

    if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
        video_stream = fmt_ctx->streams[video_stream_idx];
        video_dst_file = fopen(video_dst_filename, "wb");
        if (!video_dst_file) {
            fprintf(stderr, "Could not open destination file %s\n", video_dst_filename);
            ret = 1;
            goto end;
        }
        /* allocate image where the decoded image will be put */
        width = video_dec_ctx->width;
        height = video_dec_ctx->height;
        pix_fmt = video_dec_ctx->pix_fmt;
        ret = av_image_alloc(video_dst_data, video_dst_linesize,
            width, height, pix_fmt, 1);
        if (ret < 0) {
            fprintf(stderr, "Could not allocate raw video buffer\n");
            goto end;
        }
        video_dst_bufsize = ret;
    }

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate frame\n");
        ret = AVERROR(ENOMEM);
        goto end;
    }
    /* initialize packet, set data to NULL, let the demuxer fill it */
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
    if (video_stream)
        printf("Demuxing video from file '%s' into '%s'\n", src_filename, video_dst_filename);

    /* read frames from the file */
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        // check if the packet belongs to a stream we are interested in, otherwise
        // skip it
        if (pkt.stream_index == video_stream_idx)
            ret = decode_packet(video_dec_ctx, &pkt);
        av_packet_unref(&pkt);
        if (ret < 0)
            break;
    }
    /* flush the decoders */
    if (video_dec_ctx)
        decode_packet(video_dec_ctx, NULL);
    printf("Demuxing succeeded.\n");
    if (video_stream) {
        printf("Play the output video file with the command:\n"
            "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
            av_get_pix_fmt_name(pix_fmt), width, height,
            video_dst_filename);
    }

end:
    avformat_close_input(&fmt_ctx);
    /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
    if (avio_ctx)
        av_freep(&avio_ctx->buffer);
    avio_context_free(&avio_ctx);
    av_file_unmap(buffer, buffer_size);
    if (ret < 0) {
        fprintf(stderr, "Error occurred: %d\n", ret);
        return 1;
    }
    return 0;
}

int main(int argc, char *argv[])
{
    char *input_filename = NULL;
    if (argc < 2) {
        fprintf(stderr, "usage: %s input_file\n"
                "API example program to show how to read from a custom buffer "
                "accessed through AVIOContext.\n", argv[0]);
        return 1;
    }
    input_filename = argv[1];
    video_dst_filename = argv[2];
    std::thread first(test_avio_reading, input_filename, 11);

    std::cout << "first t_id" << first.get_id() << std::endl;
    first.join(); 

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值