将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/.