Android中使用FFMPEG将yuv420p编码为h264

单帧

JNIEXPORT jbyteArray JNICALL
Java_com_uestc_smileteeth_view_recordvideo_RecordVideoLib_encodeFrame(JNIEnv *env, jclass type,
                                                                      jbyteArray yuv420p_,
                                                                      jint width, jint height,
                                                                      jint index) {
    jbyte *yuv420p = (*env)->GetByteArrayElements(env, yuv420p_, NULL);

    AVCodec *pCodec;
    AVCodecContext *pCodecCtx = NULL;
    int i, ret, got_output;
    AVFrame *pFrame;
    AVPacket pkt;
    int y_size;
    jbyteArray h264;
    //编码格式;
    enum AVCodecID codec_id = AV_CODEC_ID_H264;
    int in_w = width, in_h = height;

    //注册所有编解码器;
    avcodec_register_all();

    //寻找解码器;
    pCodec = avcodec_find_encoder(codec_id);
    if (!pCodec) {
        LOGI("Codec not found");
    }
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (!pCodecCtx) {
        LOGI("Could not allocate video codec context!");
    }
    pCodecCtx->bit_rate = 4000000;
    pCodecCtx->width = in_w;
    pCodecCtx->height = in_h;
    pCodecCtx->time_base.num = 1;
    pCodecCtx->time_base.den = 25;
    pCodecCtx->gop_size = 10;
    pCodecCtx->max_b_frames = 0;
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;

    if (codec_id == AV_CODEC_ID_H264) {
        av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);
//        av_opt_set(pCodecCtx->priv_data, "preset", "superfast", 0);
//        av_opt_set(pCodecCtx->priv_data, "tune", "zerolatency", 0);
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        LOGI("Could not open codec!");
    }

    pFrame = av_frame_alloc();
    if (!pFrame) {
        LOGI("Could not allocate video frame!");
    }

    pFrame->format = pCodecCtx->pix_fmt;
    pFrame->width = pCodecCtx->width;
    pFrame->height = pCodecCtx->height;


    ret = av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height,
                         pCodecCtx->pix_fmt, 16);
    if (ret < 0) {
        LOGI("Could not allocate raw picture buffer!");
    }

    y_size = pCodecCtx->width * pCodecCtx->height;

    //Encode
    av_init_packet(&pkt);
    pkt.data = NULL;    // packet data will be allocated by the encoder
    pkt.size = 0;

    //YUV这样去是正确的;
    //Y
    pFrame->data[0] = yuv420p;
    //U
    pFrame->data[1] = yuv420p + y_size;
    //V
    pFrame->data[2] = yuv420p + y_size * 5 / 4;

    //pts和dts;
    pFrame->pts = index;
    //int64_t类型的打印方式有所不同;
    LOGI("pts %"
                 PRId64
                 "\n", pFrame->pts);
    /* encode the image */
    ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_output);

    if (ret < 0) {
        LOGI("Error encoding frame!");
    }

    //没有用到;
    if (got_output) {
        LOGI("Succeed to encode frame size:%5d!", pkt.size);
        h264 = (*env)->NewByteArray(env, pkt.size);
        (*env)->SetByteArrayRegion(env, h264, 0, pkt.size, pkt.data);
        av_free_packet(&pkt);
    }

    //Flush Encoder
    for (got_output = 1; got_output; i++) {
        ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output);
        if (ret < 0) {
            LOGI("Error encoding frame!");
        }
        if (got_output) {
            LOGI("Flush Encoder: Succeed to encode 1 frame!  size:%5d", pkt.size);
            h264 = (*env)->NewByteArray(env, pkt.size);
            (*env)->SetByteArrayRegion(env, h264, 0, pkt.size, pkt.data);
            av_free_packet(&pkt);
        }
    }

    avcodec_close(pCodecCtx);
    av_free(pCodecCtx);
    av_frame_free(&pFrame);

    (*env)->ReleaseByteArrayElements(env, yuv420p_, yuv420p, 0);

    return h264;
}

文件

JNIEXPORT jbyteArray JNICALL
Java_com_uestc_smileteeth_view_recordvideo_RecordVideoLib_encodeVideoFrame(JNIEnv *env, jclass type,
                                                                           jstring path1_,
                                                                           jstring path2_,
                                                                           jint frameNum,
                                                                           jint width, jint height,
                                                                           jintArray pts_) {
    const char *path1 = (*env)->GetStringUTFChars(env, path1_, 0);
    const char *path2 = (*env)->GetStringUTFChars(env, path2_, 0);
    jint *pts = (*env)->GetIntArrayElements(env, pts_, NULL);

    AVFormatContext *pFormatCtx;
    AVOutputFormat *fmt;
    AVStream *video_st;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVPacket pkt;
    uint8_t *picture_buf;
    AVFrame *pFrame;
    int picture_size;
    int y_size;
    int framecnt = 0;
    //FILE *in_file = fopen("src01_480x272.yuv", "rb"); //Input raw YUV data
    FILE *in_file = fopen(path1, "rb");   //Input raw YUV data
    int in_w = width, in_h = height;                              //Input data's width and height
    //总帧数;
    int framenum = frameNum;                                   //Frames to encode

    //输出路径;
    const char *out_file = path2;

    av_register_all();
    //Method1.
    pFormatCtx = avformat_alloc_context();
    //Guess Format
    fmt = av_guess_format(NULL, out_file, NULL);
    pFormatCtx->oformat = fmt;

    //Method 2.
    //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);
    //fmt = pFormatCtx->oformat;


    //Open output URL
    if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
        LOGI("Failed to open output file! ");
    }

    video_st = avformat_new_stream(pFormatCtx, 0);
    //video_st->time_base.num = 1;
    //video_st->time_base.den = 25;

    if (video_st == NULL) {
    }
    //Param that must set
    pCodecCtx = video_st->codec;
    //pCodecCtx->codec_id =AV_CODEC_ID_HEVC;
    pCodecCtx->codec_id = fmt->video_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    pCodecCtx->width = in_w;
    pCodecCtx->height = in_h;
    pCodecCtx->bit_rate = 30000000;
    pCodecCtx->gop_size = 5;

    pCodecCtx->time_base.num = 1;
    pCodecCtx->time_base.den = 25;

    //H264
    pCodecCtx->me_range = 32;
    //pCodecCtx->max_qdiff = 4;
    //pCodecCtx->qcompress = 0.6;
    pCodecCtx->qmin = 10;
    pCodecCtx->qmax = 40;

    //Optional Param
    pCodecCtx->max_b_frames = 0;

    // Set Option
    AVDictionary *param = 0;
    //H.264
    if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
        av_dict_set(&param, "preset", "slow", 0);
        av_dict_set(&param, "tune", "zerolatency", 0);
        //av_dict_set(&param, "profile", "main", 0);
    }
    //H.265
    if (pCodecCtx->codec_id == AV_CODEC_ID_H265) {
        av_dict_set(&param, "preset", "ultrafast", 0);
        av_dict_set(&param, "tune", "zero-latency", 0);
    }

    //Show some Information
    av_dump_format(pFormatCtx, 0, out_file, 1);

    pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
    if (!pCodec) {
        LOGI("Can not find encoder! ");
    }
    if (avcodec_open2(pCodecCtx, pCodec, &param) < 0) {
        LOGI("Failed to open encoder! ");
    }


    pFrame = av_frame_alloc();
    picture_size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    picture_buf = (uint8_t *) av_malloc(picture_size);
    avpicture_fill((AVPicture *) pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width,
                   pCodecCtx->height);

    //Write File Header
    avformat_write_header(pFormatCtx, NULL);

    av_new_packet(&pkt, picture_size);

    y_size = pCodecCtx->width * pCodecCtx->height;

    for (int i = 0; i < framenum; i++) {
        LOGI("完整视频正在编码第%d帧", i + 1);
        //Read raw YUV data
        if (fread(picture_buf, 1, y_size * 3 / 2, in_file) <= 0) {
            LOGI("Failed to read raw data! ");
        } else if (feof(in_file)) {
            break;
        }
        pFrame->data[0] = picture_buf;              // Y
        pFrame->data[1] = picture_buf + y_size;      // U
        pFrame->data[2] = picture_buf + y_size * 5 / 4;  // V
        //PTS
        pFrame->pts = pts[i];

        LOGI("pts %"
                     PRId64
                     "\n", pFrame->pts);

        int got_picture = 0;
        //Encode
        int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
        if (ret < 0) {
            LOGI("Failed to encode! ");
        }
        if (got_picture == 1) {
            framecnt++;
            LOGI("Succeed to encode frame: %5d\tsize:%5d", framecnt, pkt.size);
            pkt.stream_index = video_st->index;
            ret = av_write_frame(pFormatCtx, &pkt);
            av_free_packet(&pkt);
        }
    }
    //Flush Encoder
    int ret = flush_encoder(pFormatCtx, 0);
    if (ret < 0) {
        LOGI("Flushing encoder failed!");
    }

    //Write file trailer
    av_write_trailer(pFormatCtx);

    //Clean
    if (video_st) {
        avcodec_close(video_st->codec);
        av_free(pFrame);
        av_free(picture_buf);
    }
    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);

    fclose(in_file);


    (*env)->ReleaseStringUTFChars(env, path1_, path1);
    (*env)->ReleaseStringUTFChars(env, path2_, path2);
    (*env)->ReleaseIntArrayElements(env, pts_, pts, 0);
}
int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index) {
    int ret;
    int got_frame;
    AVPacket enc_pkt;
    if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
          CODEC_CAP_DELAY))
        return 0;
    while (1) {
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);
        ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt,
                                    NULL, &got_frame);
        av_frame_free(NULL);
        if (ret < 0)
            break;
        if (!got_frame) {
            ret = 0;
            break;
        }
        LOGI("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d", enc_pkt.size);
        /* mux encoded frame */
        ret = av_write_frame(fmt_ctx, &enc_pkt);
        if (ret < 0)
            break;
    }
    return ret;
}

推荐
雷神的FFMPEG专栏

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值