前面谈了android下NDK编译,编码器,水印,等。我们再看下解码器的接口。直接上代码:
- /*
- * Car eye 车辆管理平台: www.car-eye.cn
- * Car eye 开源网址: https://github.com/Car-eye-team
- * CarEyeDecoderAPI.h
- *
- * Author: Wgj
- * Date: 2018-05-16 22:54
- * Copyright 2018
- *
- * Car eye基于FFMPEG的音视频解码接口声明
- */
- #ifndef __CarEyeDecoderAPI_H_
- #define __CarEyeDecoderAPI_H_
- #include "CarEyeTypes.h"
- // 媒体帧信息定义
- typedef struct
- {
- // 视频编码格式
- CarEye_CodecType VCodec;
- // 音频解码格式,无音频则置为CAREYE_CODEC_NONE
- CarEye_CodecType ACodec;
- // 视频帧率(FPS)
- unsigned char FramesPerSecond;
- // 视频宽度像素
- unsigned short Width;
- // 视频的高度像素
- unsigned short Height;
- // 视频码率,越高视频越清楚,相应体积也越大 如:4000000
- unsigned int VideoBitrate;
- // 音频采样率
- unsigned int SampleRate;
- // 音频声道数
- unsigned int Channels;
- // 音频采样精度 16位 8位等,库内部固定为16位
- unsigned int BitsPerSample;
- // 音频比特率 如:64000,越高声音越清楚,相应体积也越大
- unsigned int AudioBitrate;
- }CarEye_FrameInfo;
- #ifdef __cplusplus
- extern "C"
- {
- #endif
- /*
- * Comments: 创建一个解码器对象
- * Param aInfo: 要解码的媒体信息
- * @Return CarEye_Decoder_Handle 成功返回解码器对象,否则返回NULL
- */
- CE_API CarEye_Decoder_Handle CE_APICALL CarEye_DecoderCreate(CarEye_FrameInfo aInfo);
- /*
- * Comments: 释放解码器资源
- * Param aDecoder: 要释放的解码器
- * @Return None
- */
- CE_API void CE_APICALL CarEye_DecoderRelease(CarEye_Decoder_Handle aDecoder);
- /*
- * Comments: 获取YUV输出缓冲区的字节大小
- * Param aDecoder: 解码器
- * @Return int 输出缓冲区大小 < 0失败
- */
- CE_API int CE_APICALL CarEye_GetYUVSize(CarEye_Decoder_Handle aDecoder);
- /*
- * Comments: 将输入视频解码为YUV420格式数据输出
- * Param aDecoder: 申请到的有效解码器
- * Param aFilter: 如需添加水印,则传入已创建的水印编码器对象
- * Param aBytes: 要进行解码的视频流
- * Param aSize: 要解码视频流字节数
- * Param aYuv: [输出] 解码成功后输出的YUV420数据
- * @Return int < 0解码失败,> 0为解码后YUV420的字节个数 ==0表示参数无效
- */
- CE_API int CE_APICALL CarEye_DecoderYUV420(CarEye_Decoder_Handle aDecoder,
- unsigned char *aBytes, int aSize,
- unsigned char *aYuv);
- /*
- * Comments: 将输入音频解码为PCM格式数据输出
- * Param aDecoder: 申请到的有效解码器
- * Param aBytes: 要进行解码的音频流
- * Param aSize: 要解码音频流字节数
- * Param aYuv: [输出] 解码成功后输出的PCM数据
- * @Return int < 0解码失败,> 0为解码后PCM的字节个数 ==0表示参数无效
- */
- CE_API int CE_APICALL CarEye_DecoderPCM(CarEye_Decoder_Handle aDecoder,
- unsigned char *aBytes, int aSize,
- unsigned char *aPcm);
- #ifdef __cplusplus
- }
- #endif
- #endif // __CarEyeDecoderAPI_H_
- /*
- * Car eye 车辆管理平台: www.car-eye.cn
- * Car eye 开源网址: https://github.com/Car-eye-team
- * CarEyeDecoderAPI.cpp
- *
- * Author: Wgj
- * Date: 2018-05-16 22:52
- * Copyright 2018
- *
- * Car eye基于FFMPEG的音视频解码接口实现
- */
- #include <stdio.h>
- #include "CarEyeDecoderAPI.h"
- #include "CarEyeOSDAPI.h"
- extern "C"
- {
- #include "libavcodec/avcodec.h"
- #include "libavformat/avformat.h"
- #include "libswresample/swresample.h"
- };
- #include "public.h"
- /*
- * Comments: 对视频帧添加水印,非对外接口函数
- * Param : aFrame: [输入/输出] 要添加水印的视频帧
- * @Return void
- */
- int CarEye_OSD_Add(CarEye_OSD_Handle aFilter, AVFrame *aFrame);
- // 解码器结构体定义
- typedef struct
- {
- // 视频解码器
- AVCodecContext *VDecoder;
- // 音频解码器
- AVCodecContext *ADecoder;
- // 解码后的视频帧 音视频帧对象分别定义,防止多线程分别解码音视频造成读写冲突
- AVFrame *VFrame;
- // 解码后的音频帧
- AVFrame *AFrame;
- // 视频的像素大小
- int PixelSize;
- }CarEyeDecoder;
- /*
- * Comments: 利用解码器对媒体包进行解码并输出解码后的数据
- * Param aDecoder: 有效的解码器
- * Param aPacket: 要解码的媒体数据包
- * Param aFrame: [输出] 解码后的数据
- * @Return int 小于0失败,等于0成功
- */
- static int Decode(AVCodecContext *aDecoder, AVPacket *aPacket, AVFrame *aFrame)
- {
- int ret;
- ret = avcodec_send_packet(aDecoder, aPacket);
- if (ret < 0)
- {
- printf("Error sending a packet for decoding\n");
- return ret;
- }
- return avcodec_receive_frame(aDecoder, aFrame);
- }
- /*
- * Comments: 创建一个解码器对象
- * Param aInfo: 要解码的媒体信息
- * @Return CarEye_Decoder_Handle 成功返回解码器对象,否则返回NULL
- */
- CE_API CarEye_Decoder_Handle CE_APICALL CarEye_DecoderCreate(CarEye_FrameInfo aInfo)
- {
- if (aInfo.ACodec == CAREYE_CODEC_NONE
- && aInfo.VCodec == CAREYE_CODEC_NONE)
- {
- // 至少包含一项解码需求
- return NULL;
- }
- CarEyeDecoder *decoder = new CarEyeDecoder;
- if (decoder == NULL)
- {
- return NULL;
- }
- memset(decoder, 0x00, sizeof(CarEyeDecoder));
- // 媒体解码器
- AVCodec *pCodec;
- if (aInfo.VCodec != CAREYE_CODEC_NONE)
- {
- // 请求视频解码器
- pCodec = avcodec_find_decoder((AVCodecID)aInfo.VCodec);
- if (pCodec == NULL)
- {
- printf("Could not find video decoder.\n");
- CarEye_DecoderRelease(decoder);
- return NULL;
- }
- // 申请解码器上下文
- decoder->VDecoder = avcodec_alloc_context3(pCodec);
- if (decoder->VDecoder == NULL)
- {
- printf("Could not alloc video decoder.\n");
- CarEye_DecoderRelease(decoder);
- return NULL;
- }
- decoder->VDecoder->time_base.num = 1;
- // 帧率
- decoder->VDecoder->time_base.den = aInfo.FramesPerSecond;
- // 每包一个视频帧
- decoder->VDecoder->frame_number = 1;
- // 媒体类型为视频
- decoder->VDecoder->codec_type = AVMEDIA_TYPE_VIDEO;
- decoder->VDecoder->bit_rate = aInfo.VideoBitrate;
- // 视频分辨率
- decoder->VDecoder->width = aInfo.Width;
- decoder->VDecoder->height = aInfo.Height;
- decoder->VDecoder->pix_fmt = AV_PIX_FMT_YUV420P;
- decoder->PixelSize = decoder->VDecoder->width * decoder->VDecoder->height;
- if (avcodec_open2(decoder->VDecoder, pCodec, NULL) < 0)
- {
- printf("Could not open video decoder.\n");
- CarEye_DecoderRelease(decoder);
- return NULL;
- }
- decoder->VFrame = av_frame_alloc();
- if (decoder->VFrame == NULL)
- {
- printf("Alloc video frame faile!\n");
- CarEye_DecoderRelease(decoder);
- return NULL;
- }
- }
- if (aInfo.ACodec != CAREYE_CODEC_NONE)
- {
- // 请求音频解码器
- pCodec = avcodec_find_decoder((AVCodecID)aInfo.ACodec);
- if (pCodec == NULL)
- {
- printf("Could not find audio decoder.\n");
- CarEye_DecoderRelease(decoder);
- return NULL;
- }
- // 申请解码器上下文
- decoder->ADecoder = avcodec_alloc_context3(pCodec);
- if (decoder->ADecoder == NULL)
- {
- printf("Could not alloc audio decoder.\n");
- CarEye_DecoderRelease(decoder);
- return NULL;
- }
- // 参数赋值
- decoder->ADecoder->codec_type = AVMEDIA_TYPE_AUDIO;
- decoder->ADecoder->sample_rate = aInfo.SampleRate;
- decoder->ADecoder->channels = aInfo.Channels;
- decoder->ADecoder->bit_rate = aInfo.AudioBitrate;
- decoder->ADecoder->channel_layout = AV_CH_LAYOUT_STEREO;
- if (avcodec_open2(decoder->ADecoder, pCodec, NULL) < 0)
- {
- printf("Could not open audio decoder.\n");
- CarEye_DecoderRelease(decoder);
- return NULL;
- }
- decoder->AFrame = av_frame_alloc();
- if (decoder->AFrame == NULL)
- {
- printf("Alloc audio frame fail!\n");
- CarEye_DecoderRelease(decoder);
- return NULL;
- }
- }
- return decoder;
- }
- /*
- * Comments: 释放解码器资源
- * Param aDecoder: 要释放的解码器
- * @Return None
- */
- CE_API void CE_APICALL CarEye_DecoderRelease(CarEye_Decoder_Handle aDecoder)
- {
- CarEyeDecoder *decoder = (CarEyeDecoder *)aDecoder;
- if (decoder == NULL)
- {
- return;
- }
- if (decoder->VDecoder != NULL)
- {
- avcodec_close(decoder->VDecoder);
- decoder->VDecoder = NULL;
- }
- if (decoder->ADecoder != NULL)
- {
- avcodec_close(decoder->ADecoder);
- decoder->ADecoder = NULL;
- }
- if (decoder->VFrame != NULL)
- {
- av_frame_free(&decoder->VFrame);
- decoder->VFrame = NULL;
- }
- if (decoder->AFrame != NULL)
- {
- av_frame_free(&decoder->AFrame);
- decoder->AFrame = NULL;
- }
- delete decoder;
- decoder = NULL;
- }
- /*
- * Comments: 获取YUV输出缓冲区的字节大小
- * Param aDecoder: 解码器
- * @Return int 输出缓冲区大小 < 0失败
- */
- CE_API int CE_APICALL CarEye_GetYUVSize(CarEye_Decoder_Handle aDecoder)
- {
- CarEyeDecoder *decoder = (CarEyeDecoder *)aDecoder;
- if (decoder == NULL || decoder->VDecoder == NULL)
- {
- return -1;
- }
- int y_size = decoder->PixelSize;
- return y_size + y_size / 2;
- }
- /*
- * Comments: 将输入视频解码为YUV420格式数据输出
- * Param aDecoder: 申请到的有效解码器
- * Param aFilter: 如需添加水印,则传入已创建的水印编码器对象
- * Param aBytes: 要进行解码的视频流
- * Param aSize: 要解码视频流字节数
- * Param aYuv: [输出] 解码成功后输出的YUV420数据
- * @Return int < 0解码失败,> 0为解码后YUV420的字节个数 ==0表示参数无效
- */
- CE_API int CE_APICALL CarEye_DecoderYUV420(CarEye_Decoder_Handle aDecoder,
- unsigned char *aBytes, int aSize,
- unsigned char *aYuv)
- {
- CarEyeDecoder *decoder = (CarEyeDecoder *)aDecoder;
- if (decoder == NULL || decoder->VDecoder == NULL)
- {
- return 0;
- }
- if (aBytes == NULL || aSize < 1 || aYuv == NULL)
- {
- return 0;
- }
- int ret;
- int y_size;
- int out_size = 0;
- AVPacket packet = { 0 };
- packet.data = aBytes;
- packet.size = aSize;
- ret = Decode(decoder->VDecoder, &packet, decoder->VFrame);
- if (ret < 0)
- {
- printf("Decode video error.\n");
- av_packet_unref(&packet);
- return ret;
- }
- y_size = decoder->VDecoder->width * decoder->VDecoder->height;
- // 赋值Y值
- memcpy(aYuv, decoder->VFrame->data[0], y_size);
- out_size += y_size;
- memcpy(aYuv + out_size, decoder->VFrame->data[1], y_size / 4);
- out_size += (y_size / 4);
- memcpy(aYuv + out_size, decoder->VFrame->data[2], y_size / 4);
- out_size += (y_size / 4);
- av_packet_unref(&packet);
- return out_size;
- }
- /*
- * Comments: 将输入音频解码为PCM格式数据输出
- * Param aDecoder: 申请到的有效解码器
- * Param aBytes: 要进行解码的音频流
- * Param aSize: 要解码音频流字节数
- * Param aYuv: [输出] 解码成功后输出的PCM数据
- * @Return int < 0解码失败,> 0为解码后PCM的字节个数 ==0表示参数无效
- */
- CE_API int CE_APICALL CarEye_DecoderPCM(CarEye_Decoder_Handle aDecoder,
- unsigned char *aBytes, int aSize,
- unsigned char *aPcm)
- {
- CarEyeDecoder *decoder = (CarEyeDecoder *)aDecoder;
- if (decoder == NULL || decoder->ADecoder == NULL)
- {
- return 0;
- }
- if (aBytes == NULL || aSize < 1 || aPcm == NULL)
- {
- return 0;
- }
- int ret;
- int out_size = 0;
- AVPacket packet = { 0 };
- packet.data = aBytes;
- packet.size = aSize;
- ret = Decode(decoder->ADecoder, &packet, decoder->AFrame);
- if (ret < 0)
- {
- printf("Decode audio error.\n");
- av_packet_unref(&packet);
- return ret;
- }
- int data_size = av_get_bytes_per_sample(decoder->ADecoder->sample_fmt);
- for (int i = 0; i < decoder->AFrame->nb_samples; i++)
- {
- for (int ch = 0; ch < decoder->ADecoder->channels; ch++)
- {
- memcpy(aPcm, decoder->AFrame->data[ch] + data_size * i, data_size);
- aPcm += data_size;
- out_size += data_size;
- }
- }
- av_packet_unref(&packet);
- return out_size;
- }
- JNIEXPORT jlong JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_CreateDecode(JNIEnv* env, jobject obj, jobject para) {
- void* ret;
- CarEye_FrameInfo param;
- jclass jcInfo = (*env)->GetObjectClass(env, para);
- if (0 == jcInfo) {
- CarEyeLog("GetObjectClass returned 0\n");
- return 0;
- }
- int VCodec = (*env)->GetIntField(env, para, (*env)->GetFieldID(env, jcInfo, "VCodec", "I"));
- int ACodec = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "ACodec", "I"));
- int FramesPerSecond =(*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "FramesPerSecond", "I"));
- int Width = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "width", "I"));
- int Height = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "height", "I"));
- int VideoBitrate = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "VideoBitrate", "I"));
- int SampleRate = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "SampleRate", "I"));
- int Channels = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "Channels", "I"));
- int BitsPerSample = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "BitsPerSample", "I"));
- int AudioBitrate=(*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "AudioBitrate", "I"));
- param.VCodec = VCodec;
- param.ACodec = ACodec;
- param.Width = Width;
- param.Height = Height;
- param.VideoBitrate = VideoBitrate;
- param.SampleRate = SampleRate;
- param.Channels = Channels;
- param.BitsPerSample = BitsPerSample;
- param.AudioBitrate = AudioBitrate;
- ret = CarEye_DecoderCreate( param);
- if( ret == NULL) {
- return 0;
- }else
- {
- return (long)ret;
- }
- }
- JNIEXPORT jint JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_Decode(JNIEnv* env, jobject obj, jlong handle,jint flag, jbyteArray frame, jbyteArray OutFrame) {
- void* pHandle;
- int ret;
- unsigned char* in_data;
- unsigned char* out_data;
- CarEye_YUVFrame yuv_frame;
- if(handle==0)
- return -1;
- pHandle = (void*)handle;
- in_data = (*env)->GetByteArrayElements(env,frame, 0 );
- int len = (*env)->GetArrayLength(env,frame);
- out_data = (*env)->GetByteArrayElements(env,OutFrame, 0 );
- if( flag == 0)
- {
- ret = CarEye_DecoderYUV420(pHandle, in_data, len, out_data);
- }else
- {
- CarEye_DecoderPCM(pHandle, in_data, len, out_data);
- }
- (*env)->ReleaseByteArrayElements(env,frame,in_data,0);
- (*env)->ReleaseByteArrayElements(env,OutFrame,out_data,0);
- return ret;
- }
- JNIEXPORT jint JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_ReleaseDecode(JNIEnv* env, jobject obj, jlong handle) {
- void* pHandle;
- if(handle==0)
- return -1;
- pHandle = (void*)handle;
- CarEye_DecoderRelease(pHandle);
- return 0;
- }
几个简单接口实现了视频,音频解码。上层调用。
至此我们完成了基本的FFMPEG 主要android接口,包括编码器,解码器,水印文字。满足了绝大部分的用户的需求。
car-eye开源官方网址:www.car-eye.cn
car-eye 流媒体平台网址:www.liveoss.com
car-eye 技术官方邮箱: support@car-eye.cn
car-eye技术交流QQ群: 590411159
CopyRight© car-eye 开源团队 2018
上一篇: FFMEPG 平台移植,接口简化和外部模块接入 (五)ffmpeg android移植(ffmpeg android studio 静态编译)