FFMEPG 平台移植,接口简化和外部模块接入 (六)ffmpeg android移植(ffmpeg android 下解码器)

前面谈了android下NDK编译,编码器,水印,等。我们再看下解码器的接口。直接上代码:

  1. /* 
  2.  * Car eye 车辆管理平台: www.car-eye.cn 
  3.  * Car eye 开源网址: https://github.com/Car-eye-team 
  4.  * CarEyeDecoderAPI.h 
  5.  * 
  6.  * Author: Wgj 
  7.  * Date: 2018-05-16 22:54 
  8.  * Copyright 2018 
  9.  * 
  10.  * Car eye基于FFMPEG的音视频解码接口声明 
  11.  */  
  12. #ifndef __CarEyeDecoderAPI_H_  
  13. #define __CarEyeDecoderAPI_H_  
  14.   
  15. #include "CarEyeTypes.h"  
  16.   
  17. // 媒体帧信息定义  
  18. typedef struct  
  19. {  
  20.     // 视频编码格式  
  21.     CarEye_CodecType VCodec;  
  22.     // 音频解码格式,无音频则置为CAREYE_CODEC_NONE  
  23.     CarEye_CodecType ACodec;  
  24.     // 视频帧率(FPS)  
  25.     unsigned char   FramesPerSecond;  
  26.     // 视频宽度像素  
  27.     unsigned short  Width;  
  28.     // 视频的高度像素  
  29.     unsigned short  Height;  
  30.     // 视频码率,越高视频越清楚,相应体积也越大 如:4000000  
  31.     unsigned int    VideoBitrate;  
  32.   
  33.     // 音频采样率  
  34.     unsigned int    SampleRate;  
  35.     // 音频声道数  
  36.     unsigned int    Channels;  
  37.     // 音频采样精度 16位 8位等,库内部固定为16位  
  38.     unsigned int    BitsPerSample;  
  39.     // 音频比特率 如:64000,越高声音越清楚,相应体积也越大  
  40.     unsigned int    AudioBitrate;  
  41. }CarEye_FrameInfo;  
  42.   
  43. #ifdef __cplusplus  
  44. extern "C"  
  45. {  
  46. #endif  
  47.   
  48.     /* 
  49.     * Comments: 创建一个解码器对象 
  50.     * Param aInfo: 要解码的媒体信息 
  51.     * @Return CarEye_Decoder_Handle 成功返回解码器对象,否则返回NULL 
  52.     */  
  53.     CE_API CarEye_Decoder_Handle CE_APICALL CarEye_DecoderCreate(CarEye_FrameInfo aInfo);  
  54.   
  55.     /* 
  56.     * Comments: 释放解码器资源 
  57.     * Param aDecoder: 要释放的解码器 
  58.     * @Return None 
  59.     */  
  60.     CE_API void CE_APICALL CarEye_DecoderRelease(CarEye_Decoder_Handle aDecoder);  
  61.   
  62.     /* 
  63.     * Comments: 获取YUV输出缓冲区的字节大小 
  64.     * Param aDecoder: 解码器 
  65.     * @Return int 输出缓冲区大小 < 0失败 
  66.     */  
  67.     CE_API int CE_APICALL CarEye_GetYUVSize(CarEye_Decoder_Handle aDecoder);  
  68.   
  69.     /* 
  70.     * Comments: 将输入视频解码为YUV420格式数据输出 
  71.     * Param aDecoder: 申请到的有效解码器 
  72.     * Param aFilter: 如需添加水印,则传入已创建的水印编码器对象 
  73.     * Param aBytes: 要进行解码的视频流 
  74.     * Param aSize: 要解码视频流字节数 
  75.     * Param aYuv: [输出] 解码成功后输出的YUV420数据 
  76.     * @Return int < 0解码失败,> 0为解码后YUV420的字节个数 ==0表示参数无效 
  77.     */  
  78.     CE_API int CE_APICALL CarEye_DecoderYUV420(CarEye_Decoder_Handle aDecoder,   
  79.         unsigned char *aBytes, int aSize,  
  80.         unsigned char *aYuv);  
  81.   
  82.     /* 
  83.     * Comments: 将输入音频解码为PCM格式数据输出 
  84.     * Param aDecoder: 申请到的有效解码器 
  85.     * Param aBytes: 要进行解码的音频流 
  86.     * Param aSize: 要解码音频流字节数 
  87.     * Param aYuv: [输出] 解码成功后输出的PCM数据 
  88.     * @Return int < 0解码失败,> 0为解码后PCM的字节个数 ==0表示参数无效 
  89.     */  
  90.     CE_API int CE_APICALL CarEye_DecoderPCM(CarEye_Decoder_Handle aDecoder,  
  91.         unsigned char *aBytes, int aSize,  
  92.         unsigned char *aPcm);  
  93.   
  94. #ifdef __cplusplus  
  95. }  
  96. #endif  
  97.   
  98.   
  99. #endif // __CarEyeDecoderAPI_H_  
  1. /* 
  2.  * Car eye 车辆管理平台: www.car-eye.cn 
  3.  * Car eye 开源网址: https://github.com/Car-eye-team 
  4.  * CarEyeDecoderAPI.cpp 
  5.  * 
  6.  * Author: Wgj 
  7.  * Date: 2018-05-16 22:52 
  8.  * Copyright 2018 
  9.  * 
  10.  * Car eye基于FFMPEG的音视频解码接口实现 
  11.  */  
  12.   
  13. #include <stdio.h>  
  14. #include "CarEyeDecoderAPI.h"  
  15. #include "CarEyeOSDAPI.h"  
  16.   
  17. extern "C"  
  18. {  
  19. #include "libavcodec/avcodec.h"  
  20. #include "libavformat/avformat.h"  
  21. #include "libswresample/swresample.h"  
  22. };  
  23. #include "public.h"  
  24.   
  25.   
  26. /* 
  27. * Comments: 对视频帧添加水印,非对外接口函数 
  28. * Param : aFrame: [输入/输出] 要添加水印的视频帧 
  29. * @Return void 
  30. */  
  31. int CarEye_OSD_Add(CarEye_OSD_Handle aFilter, AVFrame *aFrame);  
  32.   
  33.   
  34. // 解码器结构体定义  
  35. typedef struct  
  36. {  
  37.     // 视频解码器  
  38.     AVCodecContext *VDecoder;  
  39.     // 音频解码器  
  40.     AVCodecContext *ADecoder;  
  41.     // 解码后的视频帧 音视频帧对象分别定义,防止多线程分别解码音视频造成读写冲突  
  42.     AVFrame *VFrame;  
  43.     // 解码后的音频帧  
  44.     AVFrame *AFrame;  
  45.     // 视频的像素大小  
  46.     int PixelSize;  
  47. }CarEyeDecoder;  
  48.   
  49. /* 
  50. * Comments: 利用解码器对媒体包进行解码并输出解码后的数据 
  51. * Param aDecoder: 有效的解码器 
  52. * Param aPacket: 要解码的媒体数据包 
  53. * Param aFrame: [输出] 解码后的数据 
  54. * @Return int 小于0失败,等于0成功 
  55. */  
  56. static int Decode(AVCodecContext *aDecoder, AVPacket *aPacket, AVFrame *aFrame)  
  57. {  
  58.     int ret;  
  59.   
  60.     ret = avcodec_send_packet(aDecoder, aPacket);  
  61.     if (ret < 0)  
  62.     {  
  63.         printf("Error sending a packet for decoding\n");  
  64.         return ret;  
  65.     }  
  66.   
  67.     return avcodec_receive_frame(aDecoder, aFrame);  
  68. }  
  69.   
  70. /* 
  71. * Comments: 创建一个解码器对象 
  72. * Param aInfo: 要解码的媒体信息 
  73. * @Return CarEye_Decoder_Handle 成功返回解码器对象,否则返回NULL 
  74. */  
  75. CE_API CarEye_Decoder_Handle CE_APICALL CarEye_DecoderCreate(CarEye_FrameInfo aInfo)  
  76. {  
  77.     if (aInfo.ACodec == CAREYE_CODEC_NONE  
  78.         && aInfo.VCodec == CAREYE_CODEC_NONE)  
  79.     {  
  80.         // 至少包含一项解码需求  
  81.         return NULL;  
  82.     }  
  83.   
  84.     CarEyeDecoder *decoder = new CarEyeDecoder;  
  85.     if (decoder == NULL)  
  86.     {  
  87.         return NULL;  
  88.     }  
  89.   
  90.     memset(decoder, 0x00, sizeof(CarEyeDecoder));  
  91.   
  92.     // 媒体解码器  
  93.     AVCodec *pCodec;  
  94.     if (aInfo.VCodec != CAREYE_CODEC_NONE)  
  95.     {  
  96.         // 请求视频解码器  
  97.         pCodec = avcodec_find_decoder((AVCodecID)aInfo.VCodec);  
  98.         if (pCodec == NULL)  
  99.         {  
  100.             printf("Could not find video decoder.\n");  
  101.             CarEye_DecoderRelease(decoder);  
  102.             return NULL;  
  103.         }  
  104.         // 申请解码器上下文  
  105.         decoder->VDecoder = avcodec_alloc_context3(pCodec);  
  106.         if (decoder->VDecoder == NULL)  
  107.         {  
  108.             printf("Could not alloc video decoder.\n");  
  109.             CarEye_DecoderRelease(decoder);  
  110.             return NULL;  
  111.         }  
  112.         decoder->VDecoder->time_base.num = 1;  
  113.         // 帧率  
  114.         decoder->VDecoder->time_base.den = aInfo.FramesPerSecond;  
  115.         // 每包一个视频帧  
  116.         decoder->VDecoder->frame_number = 1;  
  117.         // 媒体类型为视频  
  118.         decoder->VDecoder->codec_type = AVMEDIA_TYPE_VIDEO;  
  119.         decoder->VDecoder->bit_rate = aInfo.VideoBitrate;  
  120.         // 视频分辨率  
  121.         decoder->VDecoder->width = aInfo.Width;  
  122.         decoder->VDecoder->height = aInfo.Height;  
  123.         decoder->VDecoder->pix_fmt = AV_PIX_FMT_YUV420P;  
  124.         decoder->PixelSize = decoder->VDecoder->width * decoder->VDecoder->height;  
  125.         if (avcodec_open2(decoder->VDecoder, pCodec, NULL) < 0)  
  126.         {  
  127.             printf("Could not open video decoder.\n");  
  128.             CarEye_DecoderRelease(decoder);  
  129.             return NULL;  
  130.         }  
  131.   
  132.         decoder->VFrame = av_frame_alloc();  
  133.         if (decoder->VFrame == NULL)  
  134.         {  
  135.             printf("Alloc video frame faile!\n");  
  136.             CarEye_DecoderRelease(decoder);  
  137.             return NULL;  
  138.         }  
  139.     }  
  140.     if (aInfo.ACodec != CAREYE_CODEC_NONE)  
  141.     {  
  142.         // 请求音频解码器  
  143.         pCodec = avcodec_find_decoder((AVCodecID)aInfo.ACodec);  
  144.         if (pCodec == NULL)  
  145.         {  
  146.             printf("Could not find audio decoder.\n");  
  147.             CarEye_DecoderRelease(decoder);  
  148.             return NULL;  
  149.         }  
  150.         // 申请解码器上下文  
  151.         decoder->ADecoder = avcodec_alloc_context3(pCodec);  
  152.         if (decoder->ADecoder == NULL)  
  153.         {  
  154.             printf("Could not alloc audio decoder.\n");  
  155.             CarEye_DecoderRelease(decoder);  
  156.             return NULL;  
  157.         }  
  158.   
  159.         // 参数赋值  
  160.         decoder->ADecoder->codec_type = AVMEDIA_TYPE_AUDIO;  
  161.         decoder->ADecoder->sample_rate = aInfo.SampleRate;  
  162.         decoder->ADecoder->channels = aInfo.Channels;  
  163.         decoder->ADecoder->bit_rate = aInfo.AudioBitrate;  
  164.         decoder->ADecoder->channel_layout = AV_CH_LAYOUT_STEREO;  
  165.         if (avcodec_open2(decoder->ADecoder, pCodec, NULL) < 0)  
  166.         {  
  167.             printf("Could not open audio decoder.\n");  
  168.             CarEye_DecoderRelease(decoder);  
  169.             return NULL;  
  170.         }  
  171.   
  172.         decoder->AFrame = av_frame_alloc();  
  173.         if (decoder->AFrame == NULL)  
  174.         {  
  175.             printf("Alloc audio frame fail!\n");  
  176.             CarEye_DecoderRelease(decoder);  
  177.             return NULL;  
  178.         }  
  179.     }  
  180.   
  181.     return decoder;  
  182. }  
  183.   
  184. /* 
  185. * Comments: 释放解码器资源 
  186. * Param aDecoder: 要释放的解码器 
  187. * @Return None 
  188. */  
  189. CE_API void CE_APICALL CarEye_DecoderRelease(CarEye_Decoder_Handle aDecoder)  
  190. {  
  191.     CarEyeDecoder *decoder = (CarEyeDecoder *)aDecoder;  
  192.   
  193.     if (decoder == NULL)  
  194.     {  
  195.         return;  
  196.     }  
  197.     if (decoder->VDecoder != NULL)  
  198.     {  
  199.         avcodec_close(decoder->VDecoder);  
  200.         decoder->VDecoder = NULL;  
  201.     }  
  202.     if (decoder->ADecoder != NULL)  
  203.     {  
  204.         avcodec_close(decoder->ADecoder);  
  205.         decoder->ADecoder = NULL;  
  206.     }  
  207.     if (decoder->VFrame != NULL)  
  208.     {  
  209.         av_frame_free(&decoder->VFrame);  
  210.         decoder->VFrame = NULL;  
  211.     }  
  212.     if (decoder->AFrame != NULL)  
  213.     {  
  214.         av_frame_free(&decoder->AFrame);  
  215.         decoder->AFrame = NULL;  
  216.     }  
  217.   
  218.     delete decoder;  
  219.     decoder = NULL;  
  220. }  
  221.   
  222. /* 
  223. * Comments: 获取YUV输出缓冲区的字节大小 
  224. * Param aDecoder: 解码器 
  225. * @Return int 输出缓冲区大小 < 0失败 
  226. */  
  227. CE_API int CE_APICALL CarEye_GetYUVSize(CarEye_Decoder_Handle aDecoder)  
  228. {  
  229.     CarEyeDecoder *decoder = (CarEyeDecoder *)aDecoder;  
  230.     if (decoder == NULL || decoder->VDecoder == NULL)  
  231.     {  
  232.         return -1;  
  233.     }  
  234.   
  235.     int y_size = decoder->PixelSize;  
  236.   
  237.     return y_size + y_size / 2;  
  238. }  
  239.   
  240. /* 
  241. * Comments: 将输入视频解码为YUV420格式数据输出 
  242. * Param aDecoder: 申请到的有效解码器 
  243. * Param aFilter: 如需添加水印,则传入已创建的水印编码器对象 
  244. * Param aBytes: 要进行解码的视频流 
  245. * Param aSize: 要解码视频流字节数 
  246. * Param aYuv: [输出] 解码成功后输出的YUV420数据 
  247. * @Return int < 0解码失败,> 0为解码后YUV420的字节个数 ==0表示参数无效 
  248. */  
  249. CE_API int CE_APICALL CarEye_DecoderYUV420(CarEye_Decoder_Handle aDecoder,   
  250.     unsigned char *aBytes, int aSize,  
  251.     unsigned char *aYuv)  
  252. {  
  253.     CarEyeDecoder *decoder = (CarEyeDecoder *)aDecoder;  
  254.     if (decoder == NULL || decoder->VDecoder == NULL)  
  255.     {  
  256.         return 0;  
  257.     }  
  258.     if (aBytes == NULL || aSize < 1 || aYuv == NULL)  
  259.     {  
  260.         return 0;  
  261.     }  
  262.   
  263.     int ret;  
  264.     int y_size;  
  265.     int out_size = 0;  
  266.     AVPacket packet = { 0 };  
  267.   
  268.     packet.data = aBytes;  
  269.     packet.size = aSize;  
  270.     ret = Decode(decoder->VDecoder, &packet, decoder->VFrame);  
  271.     if (ret < 0)  
  272.     {  
  273.         printf("Decode video error.\n");  
  274.         av_packet_unref(&packet);  
  275.         return ret;  
  276.     }  
  277.   
  278.   
  279.     y_size = decoder->VDecoder->width * decoder->VDecoder->height;  
  280.   
  281.     // 赋值Y值  
  282.     memcpy(aYuv, decoder->VFrame->data[0], y_size);  
  283.     out_size += y_size;  
  284.     memcpy(aYuv + out_size, decoder->VFrame->data[1], y_size / 4);  
  285.     out_size += (y_size / 4);  
  286.     memcpy(aYuv + out_size, decoder->VFrame->data[2], y_size / 4);  
  287.     out_size += (y_size / 4);  
  288.   
  289.     av_packet_unref(&packet);  
  290.   
  291.     return out_size;  
  292. }  
  293.   
  294. /* 
  295. * Comments: 将输入音频解码为PCM格式数据输出 
  296. * Param aDecoder: 申请到的有效解码器 
  297. * Param aBytes: 要进行解码的音频流 
  298. * Param aSize: 要解码音频流字节数 
  299. * Param aYuv: [输出] 解码成功后输出的PCM数据 
  300. * @Return int < 0解码失败,> 0为解码后PCM的字节个数 ==0表示参数无效 
  301. */  
  302. CE_API int CE_APICALL CarEye_DecoderPCM(CarEye_Decoder_Handle aDecoder,  
  303.     unsigned char *aBytes, int aSize,  
  304.     unsigned char *aPcm)  
  305. {  
  306.     CarEyeDecoder *decoder = (CarEyeDecoder *)aDecoder;  
  307.     if (decoder == NULL || decoder->ADecoder == NULL)  
  308.     {  
  309.         return 0;  
  310.     }  
  311.     if (aBytes == NULL || aSize < 1 || aPcm == NULL)  
  312.     {  
  313.         return 0;  
  314.     }  
  315.   
  316.     int ret;  
  317.     int out_size = 0;  
  318.     AVPacket packet = { 0 };  
  319.   
  320.     packet.data = aBytes;  
  321.     packet.size = aSize;  
  322.   
  323.     ret = Decode(decoder->ADecoder, &packet, decoder->AFrame);  
  324.     if (ret < 0)  
  325.     {  
  326.         printf("Decode audio error.\n");  
  327.         av_packet_unref(&packet);  
  328.         return ret;  
  329.     }  
  330.   
  331.     int data_size = av_get_bytes_per_sample(decoder->ADecoder->sample_fmt);  
  332.   
  333.     for (int i = 0; i < decoder->AFrame->nb_samples; i++)  
  334.     {  
  335.         for (int ch = 0; ch < decoder->ADecoder->channels; ch++)  
  336.         {  
  337.             memcpy(aPcm, decoder->AFrame->data[ch] + data_size * i, data_size);  
  338.             aPcm += data_size;  
  339.             out_size += data_size;  
  340.         }  
  341.     }  
  342.   
  343.     av_packet_unref(&packet);  
  344.   
  345.     return out_size;  
  346. }  
  1. JNIEXPORT jlong JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_CreateDecode(JNIEnv* env, jobject obj, jobject para) {  
  2.       
  3.      void*  ret;  
  4.    CarEye_FrameInfo param;  
  5.    jclass jcInfo = (*env)->GetObjectClass(env, para);  
  6.    if (0 == jcInfo) {  
  7.         CarEyeLog("GetObjectClass returned 0\n");  
  8.         return 0;  
  9.    }  
  10.   int VCodec = (*env)->GetIntField(env, para, (*env)->GetFieldID(env, jcInfo, "VCodec""I"));  
  11.   int ACodec = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "ACodec""I"));  
  12.   int FramesPerSecond =(*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "FramesPerSecond""I"));  
  13.   int Width = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "width""I"));  
  14.   int Height =  (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "height""I"));  
  15.   int VideoBitrate = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "VideoBitrate""I"));  
  16.   int SampleRate = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "SampleRate""I"));  
  17.   int Channels = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "Channels""I"));  
  18.   int BitsPerSample = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "BitsPerSample""I"));  
  19.   int AudioBitrate=(*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "AudioBitrate""I"));    
  20.   param.VCodec = VCodec;  
  21.   param.ACodec = ACodec;  
  22.   param.Width = Width;  
  23.   param.Height = Height;  
  24.   param.VideoBitrate = VideoBitrate;  
  25.   param.SampleRate = SampleRate;  
  26.   param.Channels = Channels;  
  27.   param.BitsPerSample = BitsPerSample;  
  28.   param.AudioBitrate = AudioBitrate;  
  29.   ret = CarEye_DecoderCreate( param);  
  30.   if(  ret  == NULL) {  
  31.       return 0;  
  32.   }else  
  33.   {  
  34.       return (long)ret;  
  35.   }   
  36.       
  37. }  
  1. JNIEXPORT jint JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_Decode(JNIEnv* env, jobject obj, jlong handle,jint flag, jbyteArray frame, jbyteArray OutFrame) {  
  2.     void* pHandle;  
  3.     int ret;  
  4.     unsigned char* in_data;  
  5.     unsigned char* out_data;  
  6.     CarEye_YUVFrame yuv_frame;  
  7.     if(handle==0)  
  8.         return -1;  
  9.     pHandle = (void*)handle;  
  10.     in_data = (*env)->GetByteArrayElements(env,frame, 0 );  
  11.     int len = (*env)->GetArrayLength(env,frame);  
  12.     out_data = (*env)->GetByteArrayElements(env,OutFrame, 0 );      
  13.     if( flag == 0)  
  14.         {  
  15.             ret = CarEye_DecoderYUV420(pHandle, in_data, len, out_data);  
  16.         }else  
  17.         {  
  18.             CarEye_DecoderPCM(pHandle, in_data, len, out_data);  
  19.         }   
  20.         (*env)->ReleaseByteArrayElements(env,frame,in_data,0);  
  21.     (*env)->ReleaseByteArrayElements(env,OutFrame,out_data,0);  
  22.       
  23.     return ret;  
  24. }  

  1. JNIEXPORT jint JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_ReleaseDecode(JNIEnv* env, jobject obj, jlong handle) {  
  2.     void* pHandle;  
  3.     if(handle==0)  
  4.         return -1;  
  5.     pHandle = (void*)handle;  
  6.     CarEye_DecoderRelease(pHandle);  
  7.     return 0;  
  8. }  

几个简单接口实现了视频,音频解码。上层调用。

至此我们完成了基本的FFMPEG 主要android接口,包括编码器,解码器,水印文字。满足了绝大部分的用户的需求。


car-eye开源官方网址:www.car-eye.cn  

car-eye 流媒体平台网址:www.liveoss.com   

car-eye 技术官方邮箱: [email protected]    
car-eye技术交流QQ群: 590411159     


CopyRight©  car-eye 开源团队 2018

猜你喜欢

转载自blog.csdn.net/xinxinsky/article/details/80475860