FFmpeg 将MP4转为YUV420P

将MP4文件转为yuv格式文件 并保存到存储卡。(Android java代码省略,只放C++部分重要代码)。
不墨迹上代码:

一、用到的动态库

static {
        System.loadLibrary("avcodec-56");
        System.loadLibrary("avdevice-56");
        System.loadLibrary("avfilter-5");
        System.loadLibrary("avformat-56");
        System.loadLibrary("avutil-54");
        System.loadLibrary("postproc-53");
        System.loadLibrary("swresample-1");
        System.loadLibrary("swscale-3");
        System.loadLibrary("native-lib");
    }

二、C代码部分:

java native 方法:

/**
     * MP4文件路径
     * 输出路径
     */
    public native String decode2Yuv(String inputPath,String outputPath);

jni 实现:

#include 
#include 
extern "C" {
//编码
#include "libavcodec/avcodec.h"
//封装格式处理
#include "libavformat/avformat.h"
//像素处理
#include "libswscale/swscale.h"
#include 
#include 
#include 
}
#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"jason",FORMAT,##__VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_testffmpeg_MainActivity_decode2Yuv(
        JNIEnv *env,
        jobject /* this */, jstring input_, jstring output_) {

    //ffmpeg 开始前必掉用此函数
    av_register_all();
    //路径  java String 转换 C 字符串
    const char *inputStr = env->GetStringUTFChars(input_, 0);
    const char *outputStr = env->GetStringUTFChars(output_, 0);

    //AVFormatContext结构体开辟内存
    AVFormatContext *pContext = avformat_alloc_context();

    //Open an input stream and read the header
    if (avformat_open_input(&pContext, inputStr, NULL, NULL) < 0) {
        LOGE("打开失败");   
    }

    //Read packets of a media file to get stream information
    if (avformat_find_stream_info(pContext, NULL) < 0) {
        LOGE("获取信息失败");
    }

    int video_stream_idx = -1;
    
    //找到视频流
    for (int i = 0; i < pContext->nb_streams; ++i) {
        LOGE("循环流 %d", i);
        if (pContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_idx = i;
        }
    }

    //    获取到解码器上下文
    AVCodecContext *codecContext = pContext->streams[video_stream_idx]->codec;
   //    获取到解码器
    AVCodec *decoder = avcodec_find_decoder(codecContext->codec_id);
    
    //Initialize the AVCodecContext to use the given AVCodec 在此为解码器上下文初始化?(不太确定)
    if (avcodec_open2(codecContext, decoder, NULL) < 0) {
        LOGE("解码失败");
    }

     //包
    AVPacket *avPacket = (AVPacket *) av_malloc(sizeof(AVPacket));
    av_init_packet(avPacket);
    //解码的帧
    AVFrame *frame = av_frame_alloc();
    //转换后YUV的帧
    AVFrame *pYUVFrame = av_frame_alloc();

    uint8_t *out_buffer = (uint8_t *) av_malloc(
            avpicture_get_size(AV_PIX_FMT_YUV420P, codecContext->width, codecContext->height));

    int re = avpicture_fill((AVPicture *) pYUVFrame, out_buffer, AV_PIX_FMT_YUV420P,
                            codecContext->width, codecContext->height);

    LOGE("宽 %d  高 %d", codecContext->width, codecContext->height);
    
    //解码入参出参 用于标记 帧
    int got_frame;

    //转换器上下文  转换方法需要
    SwsContext *pSwsContext = sws_getContext(codecContext->width, codecContext->height,
                                             codecContext->pix_fmt,
                                             codecContext->width, codecContext->height,
                                             AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);

    int frameCount = 0;
    
    //打开用于输出的文件
    FILE *fp_yuv = fopen(outputStr, "wb");
    
    
    while (av_read_frame(pContext, avPacket) >= 0) {
        avcodec_decode_video2(codecContext, frame, &got_frame, avPacket);
        LOGE("解码=%d", frameCount++);
        if (got_frame > 0) {
            //转换
            sws_scale(pSwsContext,
                      (const uint8_t *const *) frame->data,
                      frame->linesize,
                      0,
                      frame->height,
                      pYUVFrame->data,
                      pYUVFrame->linesize);

            //输出至文件
            int y_size = codecContext->width * codecContext->height;
            fwrite(pYUVFrame->data[0], 1, y_size, fp_yuv);
            fwrite(pYUVFrame->data[1], 1, y_size / 4, fp_yuv);
            fwrite(pYUVFrame->data[2], 1, y_size / 4, fp_yuv);
         }
        av_free_packet(avPacket);
    }
    //关闭文件  释放内存
    fclose(fp_yuv);
    av_frame_free(&frame);
    av_frame_free(&pYUVFrame);
    avcodec_close(codecContext);
    avformat_free_context(pContext);
}

三、验证

YUV无法用常规播放器播放,在此用ffmpeg命令测试:

ffplay -f rawvideo -video_size 480x208 C:\Users\……\Desktop\output_1280x720_yuv420p.yuv

需安装ffmpeg包,下载地址:
http://ffmpeg.zeranoe.com/builds/.

你可能感兴趣的:(FFmpeg 将MP4转为YUV420P)