(八) 解码mp4,导出为YUV420P

#include <unistd.h>
#include "hjcommon.hpp"
extern "C" {
    #include "libavutil/imgutils.h"
}

JNIEXPORT void JNICALL Java_hankin_hjmedia_mpeg_some_MP42YUVActivity_decode(JNIEnv *env, jobject instance, jstring src_, jstring dst_)
{
    char src[128];
    hjcpyJstr2char(env, src_, src);
    char dst[128];
    hjcpyJstr2char(env, dst_, dst);

    AVFormatContext * avFormatContext = 0;
    int ret = avformat_open_input(&avFormatContext, src, 0, 0);
    if (ret!=0)
    {
        LOGE("avformat_open_input error.");
        return;
    }
    ret = avformat_find_stream_info(avFormatContext, 0);
    if (ret!=0)
    {
        LOGE("avformat_find_stream_info error.");
        return;
    }
    int videoStream = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);
    AVStream * avs = avFormatContext->streams[videoStream];
    int width = avs->codecpar->width;
    int height = avs->codecpar->height;
    // duration=10024000, fps=25.00, width=1920, height=1080
    LOGD("duration=%lld, fps=%.2f, width=%d, height=%d", avFormatContext->duration, hj_r2d(avs->avg_frame_rate), width, height);

    AVCodecContext * videoCodec = avcodec_alloc_context3(0);
    int gvRet = hjgetAVDecoder6_1(videoCodec, avs->codecpar, true, false); // nexus上,硬解码的效果没有软解码好
    if (gvRet!=0) return;

    int frameCount = 0;
    AVPacket * packet = av_packet_alloc();
    AVFrame * frame = av_frame_alloc();
    bool is = true;
    FILE * fp = fopen(dst, "wb+");
    AVFrame * tmpFrame = 0;
    int format = -1;
    AVFrame * convertFrame = 0;
    SwsContext * swsContext = 0;

    while (true)
    {
        int re = av_read_frame(avFormatContext, packet);
        if (re!=0) break;
        if (packet->stream_index==videoStream)
        {
            int num = 0;
            while (true)
            {
                num++;
                ret = avcodec_send_packet(videoCodec, packet); // 硬解码的时候,avcodec_send_packet可能会失败,这里循环尝试发送5次
                if (ret!=0)
                {
                    LOGW("avcodec_send_packet error");
                    usleep(1000*2);
                }
                if (ret==0 || num>=5) break;
            }

            while (true)
            {
                ret = avcodec_receive_frame(videoCodec, frame);
                if (ret!=0) break;
                frameCount++;
                tmpFrame = frame;

                format = frame->format; // mp4软解码的format为AV_PIX_FMT_YUV420P,硬解码看硬件
                LOGD("pts=%lld, size=%d, format=%d, 帧数=%d", frame->pts, frame->pkt_size, format, frameCount);
                if (is && format!=AV_PIX_FMT_YUV420P) // 视频格式不为 AV_PIX_FMT_YUV420P ,需要转换为yuv,然后保存到sdcard
                {
                    is = false;
                    convertFrame = av_frame_alloc();
                    /*
                         int av_image_get_buffer_size( // 返回使用给定参数存储图像所需的数据量的大小(以字节为单位)。
                             enum AVPixelFormat pix_fmt, // the pixel format of the image
                             int width, // 宽度
                             int height, // 高度
                             int align // 一个像素占几个字节?
                         );
                     */
                    int imgSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1);
                    uint8_t * buf = (uint8_t*) av_malloc(imgSize);
                    /*
                         int av_image_fill_arrays( // 为AVFrame的数据分配内存
                             uint8_t *dst_data[4], // framd的data
                             int dst_linesize[4], // frame的linesize
                             const uint8_t *src, // 申请好了的内存
                             enum AVPixelFormat pix_fmt, // 视频格式
                             int width, // 宽
                             int height, // 高
                             int align // 一个像素占几个字节?
                         );
                     */
                    int ss = av_image_fill_arrays(convertFrame->data, convertFrame->linesize, buf, AV_PIX_FMT_YUV420P, width, height, 1);
                    // av_image_get_buffer_size=3110400, av_image_fill_arrays=3110400, format=23
                    LOGI("av_image_get_buffer_size=%d, av_image_fill_arrays=%d, format=%d", imgSize, ss, format);
                }
                if (format!=AV_PIX_FMT_YUV420P) // 视频格式不为 AV_PIX_FMT_YUV420P ,需要转换为yuv,然后保存到sdcard
                {
                    // 视频格式转换
                    swsContext = sws_getCachedContext(swsContext, width, height, (AVPixelFormat) frame->format,
                                                      width, height, AV_PIX_FMT_YUV420P,
                                                      SWS_FAST_BILINEAR, NULL, NULL, NULL);
                    sws_scale(swsContext, (const uint8_t**)frame->data, frame->linesize, 0, height, convertFrame->data, convertFrame->linesize);
                    tmpFrame = convertFrame;
                }

                // 保存到sdcard
                int y_size = width * height;
                fwrite(tmpFrame->data[0], 1, y_size, fp); // 1920*1080 10秒钟的视频(共241帧)存为yuv,大小为 1920*1080*1.5*241 = 700多M 。。
                fwrite(tmpFrame->data[1], 1, y_size / 4, fp);
                fwrite(tmpFrame->data[2], 1, y_size / 4, fp);
            }
        }

        av_packet_unref(packet);
    }

end:
    av_packet_free(&packet);
    av_frame_free(&frame);
    avcodec_close(videoCodec);
    avcodec_free_context(&videoCodec);
    avformat_close_input(&avFormatContext);
    fflush(fp);
    fclose(fp);

    // 调用java函数
    jclass  clz = env->GetObjectClass(instance);
    jmethodID mid = env->GetMethodID(clz, "setStatus", "()V");
    env->CallVoidMethod(instance, mid);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值