1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
| // // Created by gloomy on 17-7-4. // #include <android/log.h> #include "com_gloomyer_ffmpegplay_VideoUtils.h" #include "include/libavcodec/avcodec.h" #include "include/libavformat/avformat.h" #include "include/libswscale/swscale.h"
#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__);
JNIEXPORT void JNICALL Java_com_gloomyer_ffmpegplay_VideoUtils_decode (JNIEnv *env, jclass jcls, jstring input_jstr, jstring output_jstr) {
//需要转码的视频文件(输入的视频文件) const char *input_cstr = (*env)->GetStringUTFChars(env, input_jstr, NULL); const char *output_cstr = (*env)->GetStringUTFChars(env, output_jstr, NULL);
//1.注册所有组件 av_register_all();
//封装格式上下文,统领全局的结构体,保存了视频文件封装格式的相关信息 AVFormatContext *pFormatCtx = avformat_alloc_context();
//2.打开输入视频文件 if (avformat_open_input(&pFormatCtx, input_cstr, NULL, NULL) != 0) { LOGE("%s", "can't open input file"); return; }
//3.获取视频文件信息 if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { LOGE("%s", "can't get input video info"); return; }
//获取视频流的索引位置 //遍历所有类型的流(音频流、视频流、字幕流),找到视频流 int v_stream_idx = -1; int i = 0; //number of streams for (; i < pFormatCtx->nb_streams; i++) { //流的类型 if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { v_stream_idx = i; break; } }
if (v_stream_idx == -1) { LOGE("%s","can't get input stream\n"); return; }
//只有知道视频的编码方式,才能够根据编码方式去找到解码器 //获取视频流中的编解码上下文 AVCodecContext *pCodecCtx = pFormatCtx->streams[v_stream_idx]->codec; //4.根据编解码上下文中的编码id查找对应的解码 AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //(迅雷看看,找不到解码器,临时下载一个解码器) if (pCodec == NULL) { LOGE("%s","not find Decoder!\n"); return; }
//5.打开解码器 if (avcodec_open2(pCodecCtx,pCodec,NULL)<0) { LOGE("%s","can't open Decoder!\n"); return; }
//输出视频信息 LOGI("视频的文件格式:%s",pFormatCtx->iformat->name); LOGI("视频时长:%d", (pFormatCtx->duration)/1000000); LOGI("视频的宽高:%d,%d",pCodecCtx->width,pCodecCtx->height); LOGI("解码器的名称:%s",pCodec->name);
//准备读取 //AVPacket用于存储一帧一帧的压缩数据(H264) //缓冲区,开辟空间 AVPacket *packet = (AVPacket*)av_malloc(sizeof(AVPacket));
//AVFrame用于存储解码后的像素数据(YUV) //内存分配 AVFrame *pFrame = av_frame_alloc(); //YUV420 AVFrame *pFrameYUV = av_frame_alloc(); //只有指定了AVFrame的像素格式、画面大小才能真正分配内存 //缓冲区分配内存 uint8_t *out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); //初始化缓冲区 avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
//用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等 struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
int got_picture, ret;
FILE *fp_yuv = fopen(output_cstr, "wb+");
int frame_count = 0;
//6.一帧一帧的读取压缩数据 while (av_read_frame(pFormatCtx, packet) >= 0) { //只要视频压缩数据(根据流的索引位置判断) if (packet->stream_index == v_stream_idx) { //7.解码一帧视频压缩数据,得到视频像素数据 ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if (ret < 0) { LOGE("%s","解码错误"); return; }
//为0说明解码完成,非0正在解码 if (got_picture) { //AVFrame转为像素格式YUV420,宽高 //2 6输入、输出数据 //3 7输入、输出画面一行的数据的大小 AVFrame 转换是一行一行转换的 //4 输入数据第一列要转码的位置 从0开始 //5 输入画面的高度 sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
//输出到YUV文件 //AVFrame像素帧写入文件 //data解码后的图像像素数据(音频采样数据) //Y 亮度 UV 色度(压缩了) 人对亮度更加敏感 //U V 个数是Y的1/4 int y_size = pCodecCtx->width * pCodecCtx->height; fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv); fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv); fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);
frame_count++; LOGI("解码第%d帧",frame_count); } }
//释放资源 av_free_packet(packet); }
//释放资源 fclose(fp_yuv); (*env)->ReleaseStringUTFChars(env,input_jstr,input_cstr); (*env)->ReleaseStringUTFChars(env,output_jstr,output_cstr); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_free_context(pFormatCtx); }
|