从mpeg ts文件中提取I帧(5):I帧的解码

找到含有I帧的pes后就可以对其进行解码,对I帧的解码我们使用ffmpeg,这里用到的版本是3.0.0。

一、pes解码为yuv,ffmpeg一般都是这个套路,直接上代码:

int ffdecode_pes(uint8_t *pes_data, int32_t pes_size, ffbuffer_t *ffbuffer, char *out_file)
{
    int ret = 0;
    
    AVPacket packet;
    AVCodec *codec = NULL;
    AVCodecContext *codec_ctx  = NULL;
    AVFrame *frame = NULL;
    int got_picture = 0;
    
    if (NULL != out_file) {
        write_pes_file(pes_data, pes_size, out_file);
    }

    avcodec_register_all();
    
    codec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
    codec_ctx = avcodec_alloc_context3(codec);
    ret = avcodec_open2(codec_ctx, codec, NULL);

    av_init_packet(&packet);
    packet.data = NULL;
    packet.size = 0;

    frame = av_frame_alloc();

    packet.size = pes_size;  
    packet.data = pes_data;

    while (1) {
        ret = avcodec_decode_video2(codec_ctx, frame, &got_picture, (const AVPacket *)&packet);
        if (got_picture) {
            frame->format = codec_ctx->pix_fmt;
            ffdecode_rgb(frame, ffbuffer);
            if (NULL != out_file) {
                write_yuv_file(frame   , out_file);
                write_rgb_file(ffbuffer, out_file);
                write_bmp_file(ffbuffer, out_file);
            }
            break;
        }
        
        av_packet_unref(&packet);
    }

    av_packet_unref(&packet);
    av_frame_free(&frame);
    avcodec_close(codec_ctx);
    avcodec_free_context(&codec_ctx);

    return ret;
}

二、yuv解码为rbg,同样也是使用ffmpeg提供的api

int ffdecode_rgb(AVFrame *frame_yuv, ffbuffer_t *buffer_rgb)
{
    struct SwsContext *sws_ctx = NULL;
    AVFrame *frame_rgb = NULL;
    
    int32_t r = 0;
    int32_t w = frame_yuv->width;
    int32_t h = frame_yuv->height;  
    
    frame_rgb = av_frame_alloc();

    r = av_image_fill_arrays(frame_rgb->data, frame_rgb->linesize, \
                             buffer_rgb->data, buffer_rgb->format, w, h, 1);

    sws_ctx = sws_getContext(w, h, frame_yuv->format, \
                             w, h, AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, 0, 0, 0);
    if (sws_ctx == NULL) {
        print_err("sws_getContext() failed. \n");
        return -1;
    }

    r = sws_scale(sws_ctx, (const uint8_t* const*)frame_yuv->data, frame_yuv->linesize, 0, h, frame_rgb->data, frame_rgb->linesize);

    av_frame_free(&frame_rgb);
    sws_freeContext(sws_ctx);

    return r;
}

三、yuv保存为文件
这里保存的是yuv 4:2:0 planer格式,逐行写入,uv分量各占y分量的4分之一。
保存的文件可以用yuv播放器进行播放。

static int write_yuv_file(AVFrame *frame, char *out_file)
{
    FILE *fd = NULL;
    int i = 0;
    char file_name[64];

    memset(file_name, 0x00, sizeof(char)*64);
    snprintf(file_name, 64, "%s.yuv", out_file);
    fd = fopen(file_name, "wb");
    
    /*yuv 4:2:0 planer*/
    /*write y*/
    for (i = 0; i<frame->height; i++) {
        fwrite(frame->data[0]+i*frame->linesize[0], 1, frame->width  , fd);
    }

    /*write u*/
    for (i = 0; i<(frame->height/2); i++) {
        fwrite(frame->data[1]+i*frame->linesize[1], 1, frame->width/2, fd);
    }

    /*write v*/
    for (i = 0; i<(frame->height/2); i++) {
        fwrite(frame->data[2]+i*frame->linesize[2], 1, frame->width/2, fd);
    }
    
    fclose(fd);
    print_log("%s\n", file_name);
    return 0;
}

四、rgb保存为bmp文件
bmp实际就是位图头+rgb数据,需要说明就是高度可能为负数。
可以参考下面这一段解释:
bmp图象的高度,以象素为单位。
这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。
如果该值是一个正数,说明图像是倒向的,即:数据的第一行其实是图像的最后一行,
如果该值是一个负数,则说明图像是正向的。
大多数的BMP文件都是倒向的位图,也就是时,高度值是一个正数
代码如下:

static int write_bmp_file(ffbuffer_t *buffer_rgb, char *out_file)
{
    bitmap_info_header_t biheader;
    bitmap_file_header_t bfheader;
    char file_name[64];
    FILE* pd = NULL;
    int r = 0;

    biheader.bi_size            = sizeof(bitmap_info_header_t);
    biheader.bi_width           = buffer_rgb->width;
    biheader.bi_height          = buffer_rgb->height*(-1);
    biheader.bi_planes          = 1;
    biheader.bi_bit_count       = 24;
    biheader.bi_compression     = 0;
    biheader.bi_size_image      = 0;
    biheader.bi_xpels_per_meter = 0;
    biheader.bi_ypels_per_meter = 0;
    biheader.bi_clr_used        = 0;
    biheader.bi_clr_important   = 0;
    
    bfheader.bf_type      = 0x4d42;
    bfheader.bf_size      = sizeof(bitmap_file_header_t) + sizeof(bitmap_info_header_t) + buffer_rgb->size;
    bfheader.bf_reserved1 = 0;
    bfheader.bf_reserved2 = 0;
    bfheader.bf_off_bits  = sizeof(bitmap_file_header_t) + sizeof(bitmap_info_header_t);
    
    memset(file_name, 0x00, sizeof(char)*64);
    snprintf(file_name, 64, "%s.bmp", out_file);

    pd = fopen(file_name, "wb");
    r = fwrite(&bfheader.bf_type         , sizeof(uint16_t), 1, pd);
    r = fwrite(&bfheader+sizeof(uint16_t), sizeof(bfheader)-sizeof(uint16_t), 1, pd);
    r = fwrite(&biheader                 , sizeof(biheader), 1, pd);
    r = fwrite(buffer_rgb->data          , buffer_rgb->size, 1, pd);
    fclose(pd);
    print_log("%s\n", file_name);
    return r;
}

mpeg2标准:https://download.csdn.net/download/maxzero/10402761
完整的代码:https://download.csdn.net/download/maxzero/10572383

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值