1.在android上用ffmpeg. 肯定要先 交叉编译好,ffmpeg的so库.在这,假设你已经编译好库
2. 在用AS (android studio) 写c/c++代码时,也就是所谓的解码 视频代码.在这里不做特殊说明.
if (filename != nullptr){
filenameStr = filename;
LOG(ERROR) << TAG << "file path:" << filename;
}else{
LOG(ERROR) << TAG << "file empty";
return -1;
}
pFormatCtx = avformat_alloc_context();
int result = 0;
result = avformat_open_input(&pFormatCtx, filename, NULL, NULL);
if (result != 0)
{
LOG(ERROR) << TAG << " open file fail";
return -2;
}
result = avformat_find_stream_info(pFormatCtx, NULL);
if (result != 0)
{
LOG(ERROR) << TAG << " find stream fail";
return -3;
}
//find videostream data
for (int i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
break;
}
}
if (videoStream == -1)
{
LOG(ERROR)<< TAG << " Couldn't find a video stream";//
return -4;
}
//pCodecCtx = pFormatCtx->streams[videoStream]->codec;//deprecated
pCodecCtx = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar);
//声明位于libavcodec/avcodec.h
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);//用于查找FFmpeg的解码器。
//函数的参数是一个解码器的ID,返回查找到的解码器(没有找到就返回NULL)
parser = av_parser_init(pCodec->id);
if (!pCodecCtx)
{
LOG(ERROR)<< TAG << "Could not allocate video codec context";
exit(1);
}
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
LOG(ERROR) << TAG << " Could not open codec";
exit(1);
}
以上代码片段,主要是打开了视频。一切运行正常后,就可以进行下面的工作了。
接下来,格式 转化函数
convert_NV21
来啦.
nv21_pSwsCtx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
AV_PIX_FMT_NV21,
SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(nv21_pSwsCtx,
pFrame->data,
pFrame->linesize,
0,pCodecCtx->height,
pFrameNV21->data,
pFrameNV21->linesize);//swscale主要用于在2个AVFrame之间进行转换
下面是循环解码流程:
while(av_read_frame(pFormatCtx,pkt) >= 0)
{
/* use the parser to split the data into frames */
if (pkt->stream_index == videoStream)
{
int ret = avcodec_send_packet(pCodecCtx, pkt);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
return -1;
while(avcodec_receive_frame(pCodecCtx, pFrame) == 0)//解码出来的frame?
{
convert(pFrame,pFrameRGBA);
convert_NV21(pFrame,pFrameYuv_Nv21);
if(ANativeWindow_lock(m_nativeWindow,&windowBuffer,NULL) >= 0)
{
uint8_t * dst = (uint8_t*)windowBuffer.bits;
for (int h = 0; h < pCodecCtx->height; ++h)
{
memcpy(dst+h*windowBuffer.stride*4,
out_buffer+h*pFrameRGBA->linesize[0],
pFrameRGBA->linesize[0]
);
}
ANativeWindow_unlockAndPost(m_nativeWindow);
}
m_callbackfuntion(pFrameYuv_Nv21);//此处将得到的图像回调出去....
}
if (ret < 0 && ret != AVERROR_EOF)
return -1;
}
av_packet_unref(pkt);
}
最后,是在jni 层 的 回调函数:
unsigned char* pYuvBuf= nullptr;
static int revice(AVFrame *pFrameNV21){//AVFrame *pFrameYuv
if(mjlxff != nullptr){
const int framelength = getFrameSize();//得到图像的长×宽
const int yuvNv21Framesize = framelength*3/2;//图像所需内存大小
if(pYuvBuf == nullptr)//避免重复分配内存
{
pYuvBuf = new unsigned char[yuvNv21Framesize];
}
size_t Ystep = framelength* sizeof(uint8_t);
memcpy(pYuvBuf,pFrameNV21->data[0],Ystep);//拷贝Y分量
memcpy(pYuvBuf + Ystep ,pFrameNV21->data[1], Ystep /2);//调试到这里,data[2]没有值
}
LOG(ERROR) << "This is callback ... ";
return 0;
}
---->
写在最后的话,鸣谢:
1..jni 使用规范:http://www.cnblogs.com/nuliniaoboke/archive/2012/10/31/2747715.html
2.anroid上使用ffmpeg https://www.jianshu.com/p/fb19cfb93548
3.android ffmpeg 使用ffmpeg 解码h264 https://blog.csdn.net/junzia/article/details/68952183
4. android 上 ,在文件中保存 byte[] 为 图像. https://ask.csdn.net/questions/10824
5.校验解码出来的yuv 数据 ,用ffmpeg 查看YUV图片/视频.
校验解码出来的yuv 数据(nv21) https://blog.csdn.net/matrix_laboratory/article/details/49470689
ffplay -video_size 852x480 -i ./save.yuv -pixel_format nv21
//大小 //文件名 / /文件格式
6.【视频处理】 YUV 格式说明: http://www.cnblogs.com/dwdxdy/p/3713968.html