FFMEPG 平台移植,接口简化和外部模块接入 (四)ffmpeg android移植(ffmpeg 视频编码)

Stella981
• 阅读 721

FFMPEG 视频编码最常见的H264,H265需要X264,X265外部模块支持,可以从我们开源平台的FFMPEG编译项目里面获取代码和配置进行一键式编译:https://github.com/Car-eye-team/Car-eye-FFMPEG,我们下面的代码主要是为了简化代码调用结构。只需要配置参数,输入数据就可以进行视频编码,不多说,贴上代码:

[cpp] view plain copy

  1. /* 

  2.  * Car eye 车辆管理平台: www.car-eye.cn 

  3.  * Car eye 开源网址: https://github.com/Car-eye-team 

  4.  * CarEyeEncoderAPI.h 

  5.  * 

  6.  * Author: Wgj 

  7.  * Date: 2018-04-29 20:01 

  8.  * Copyright 2018 

  9.  * 

  10.  * CarEye 媒体编码库接口声明 

  11.  */  

  12. #ifndef __CAREYE_ENCODER_H__  

  13. #define __CAREYE_ENCODER_H__  

  14. #include "public.h"  

  15.  // 编码器对象句柄定义  

  16. #define CarEye_Encoder_Handle void*  

  17.  // 最大音频帧大小 1 second of 48khz 32bit audio  

  18. #define MAX_AUDIO_FRAME_SIZE 192000  

  19.  // 媒体编码类型定义 与FFMPEG中一一对应,H265定义与其他库定义需要转换  

  20.   enum CarEye_CodecType  

  21. {  

  22.     // 不进行编码  

  23.     CAREYE_CODEC_NONE = 0,  

  24.     // H264编码  

  25.     CAREYE_CODEC_H264 = 0x1C,  

  26.     // H265编码  

  27.     CAREYE_CODEC_H265 = 0xAE,  

  28.     // MJPEG编码  

  29.     CAREYE_CODEC_MJPEG = 0x08,  

  30.     // MPEG4编码  

  31.     CAREYE_CODEC_MPEG4 = 0x0D,  

  32.     // AAC编码  

  33.     CAREYE_CODEC_AAC = 0x15002,  

  34.     // G711 Ulaw编码 对应FFMPEG中的AV_CODEC_ID_PCM_MULAW定义  

  35.     CAREYE_CODEC_G711U = 0x10006,  

  36.     // G711 Alaw编码 对应FFMPEG中的AV_CODEC_ID_PCM_ALAW定义  

  37.     CAREYE_CODEC_G711A = 0x10007,  

  38.     // G726编码 对应FFMPEG中的AV_CODEC_ID_ADPCM_G726定义  

  39.     CAREYE_CODEC_G726 = 0x1100B,  

  40. };  

  41. // YUV视频流格式定义,与FFMPEG中一一对应  

  42. enum CarEye_AVType  

  43. {  

  44.     CAREYE_FMT_YUV420P = 0,  

  45.     CAREYE_FMT_YUV422P = 4,  

  46.     CAREYE_FMT_YUV444P = 5,  

  47.     CAREYE_FMT_YUV410P = 6,  

  48.     CAREYE_FMT_YUV411P = 7,  

  49. };  

  50. // 原始流结构定义  

  51. typedef struct CarEye_OriginalStream  

  52. {  

  53.     // 视频输入流格式  

  54.     enum CarEye_AVType InVideoType;  

  55.     // 期望输出的视频流格式,不期望输出可设置为CAREYE_CODEC_NONE  

  56.     enum CarEye_CodecType OutVideoType;  

  57.     // 期望输出的音频流格式,不期望输出可设置为CAREYE_CODEC_NONE  

  58.     enum CarEye_CodecType OutAudioType;  

  59.     // 视频帧率(FPS),推荐值:25  

  60.     unsigned char   FramesPerSecond;  

  61.     // 视频宽度像素  

  62.     unsigned short  Width;  

  63.     // 视频的高度像素  

  64.     unsigned short  Height;  

  65.     // 一组图片中的图片数量,推荐值:10  

  66.     int             GopSize;  

  67.     // 非B帧之间的B帧的最大数量,推荐值:1  

  68.     int             MaxBFrames;  

  69.     // 视频码率,越高视频越清楚,相应体积也越大 如:4000000  

  70.     float           VideoBitrate;  

  71.     // 音频采样率 如:44100  

  72.     unsigned int    SampleRate;  

  73.     // 音频比特率 如:64000,越高声音越清楚,相应体积也越大  

  74.     float           AudioBitrate;  

  75. }CarEye_OriginalStream;  

  76. // YUV媒体流结构定义  

  77. #ifdef __cplusplus  

  78. extern "C"  

  79. {  

  80. #endif  

  81.     /* 

  82.     * Comments: 创建一个编码器对象 

  83.     * Param aInfo: 要编码的媒体信息 

  84.     * @Return CarEye_Encoder_Handle 成功返回编码器对象,否则返回NULL 

  85.     */  

  86.     CE_API CarEye_Encoder_Handle CE_APICALL CarEye_EncoderCreate( CarEye_OriginalStream aInfo);  

  87.     /* 

  88.     * Comments: 释放编码器资源 

  89.     * Param aEncoder: 要释放的编码器 

  90.     * @Return None 

  91.     */  

  92.     CE_API void CE_APICALL CarEye_EncoderRelease(CarEye_Encoder_Handle aEncoder);  

  93.     /* 

  94.     * Comments: 将输入YUV视频编码为设置好的格式数据输出 

  95.     * Param aEncoder: 申请到的有效编码器 

  96.     * Param aYuv: 要编码的YUV数据 

  97.     * Param aPts: 当前视频帧序号 

  98.     * Param aBytes: [输出]编码后的视频流 

  99.     * @Return int < 0编码失败,> 0为编码后数据字节个数 ==0表示参数无效 

  100.     */  

  101.     CE_API int CE_APICALL CarEye_EncoderYUV(CarEye_Encoder_Handle aEncoder,  

  102.                                     CarEye_YUVFrame *aYuv, int aPts,  

  103.                                     unsigned char *aBytes);  

  104.     /* 

  105.     * Comments: 获取PCM编码时接受的最大字节数 

  106.     * Param aEncoder: 申请到的有效编码器 

  107.     * @Return PCM编码缓冲区最大字节数 

  108.     */  

  109.     CE_API int CE_APICALL CarEye_GetPcmMaxSize(CarEye_Encoder_Handle aEncoder);  

  110.     /* 

  111.     * Comments: 将输入的PCM音频编码为指定数据格式输出 

  112.     * Param aEncoder: 申请到的有效编码器 

  113.     * Param aPcm: 要编码的PCM数据 

  114.     * Param aSize: 要编码音频流字节数 

  115.     * Param aBytes: [输出] 编码后的音频流 

  116.     * Param aPts: 当前编码帧的序号 

  117.     * @Return int < 0编码失败,> 0为编码后PCM的字节个数 ==0表示参数无效 

  118.     */  

  119.     CE_API int CE_APICALL CarEye_EncoderPCM(CarEye_Encoder_Handle aEncoder,  

  120.                                     unsigned char *aPcm, int aSize, int aPts,  

  121.                                     unsigned char *aBytes);  

  122. #ifdef __cplusplus  

  123. }  

  124. #endif  

  125. #endif

  

[cpp] view plain copy

  1. /* 

  2.  * Car eye 车辆管理平台: www.car-eye.cn 

  3.  * Car eye 开源网址: https://github.com/Car-eye-team 

  4.  * CarEyeEncoderAPI.cpp 

  5.  * 

  6.  * Author: Wgj 

  7.  * Date: 2018-04-29 20:02 

  8.  * Copyright 2018 

  9.  * 

  10.  * CarEye 媒体编码库接口实现 

  11.  */  

  12. #include "CarEyeEncoderAPI.h"  

  13. #include "FFVideoFilter.h"  

  14. #ifdef _WIN32  

  15.  //Windows  

  16. extern "C"  

  17. {  

  18. #include "libavutil/opt.h"  

  19. #include "libavcodec/avcodec.h"  

  20. #include "libavutil/imgutils.h"  

  21. #include "libavformat/avformat.h"  

  22. #include "libswresample/swresample.h"  

  23. #include "libavfilter/avfilter.h"  

  24. };  

  25. #else  

  26.  //Linux...  

  27. #ifdef __cplusplus  

  28. extern "C"  

  29. {  

  30. #endif  

  31. #include <libavutil/opt.h>  

  32. #include <libavcodec/avcodec.h>  

  33. #include <libavutil/imgutils.h>  

  34. #include <libavformat/avformat.h>  

  35. #include <libswresample/swresample.h>  

  36. #include <libavfilter/avfilter.h>  

  37. #ifdef __cplusplus  

  38. };  

  39. #endif  

  40. #endif  

  41. // 编码器结构体定义  

  42. typedef struct  

  43. {  

  44.     // 视频编码器  

  45.     AVCodecContext *VEncoder;  

  46.     // 音频编码器  

  47.     AVCodecContext *AEncoder;  

  48.     // 编码后的视频帧 音视频帧对象分别定义,防止多线程分别编码音视频造成读写冲突  

  49.     AVFrame *VFrame;  

  50.     // 编码后的音频帧  

  51.     AVFrame *AFrame;  

  52.     // 音频转码器  

  53.     struct SwrContext *AConverter;  

  54.     // 存储PCM数据缓冲区  

  55.     unsigned char *PcmBuffer;  

  56.     // 接收PCM字节个数上限  

  57.     int PcmSize;  

  58.     // 每组PCM数据的字节数  

  59.     int PerPcmSize;  

  60.     // 视频字幕对象  

  61. }CarEyeEncoder;  

  62. /* 

  63. * Comments: 利用编码器对媒体包进行编码并输出编码后的数据 

  64. * Param aEncoder: 有效的编码器 

  65. * Param aFrame: 要编码的媒体数据包 

  66. * Param aPacket: [输出] 编码后的数据 

  67. * @Return int 小于0失败,等于0成功 

  68. */  

  69. static int Encode(AVCodecContext *aEncoder, AVFrame *aFrame, AVPacket *aPacket)  

  70. {  

  71.     int ret;  

  72.     ret = avcodec_send_frame(aEncoder, aFrame);  

  73.     if (ret < 0)  

  74.     {  

  75.         printf("Error sending a packet for encoding\n");  

  76.         return ret;  

  77.     }  

  78.     ret = avcodec_receive_packet(aEncoder, aPacket);  

  79.     if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)  

  80.     {  

  81.         return 0;  

  82.     }  

  83.     else if (ret < 0)  

  84.     {  

  85.         //error during encoding  

  86.         return -1;  

  87.     }  

  88.     return ret;  

  89. }  

  90. /* 

  91. * Comments: 创建一个编码器对象 

  92. * Param aInfo: 要编码的媒体信息 

  93. * @Return CarEye_Encoder_Handle 成功返回编码器对象,否则返回NULL 

  94. */  

  95. CE_API CarEye_Encoder_Handle CE_APICALL CarEye_EncoderCreate(CarEye_OriginalStream aInfo)  

  96. {  

  97.     if (aInfo.OutVideoType == CAREYE_CODEC_NONE  

  98.         && aInfo.OutAudioType == CAREYE_CODEC_NONE)  

  99.     {  

  100.         CarEyeLog("null paramter\n");  

  101.         // 至少包含一项编码需求  

  102.         return NULL;  

  103.     }  

  104.     CarEyeEncoder *encoder = new CarEyeEncoder;  

  105.     if (encoder == NULL)  

  106.     {  

  107.         CarEyeLog("alloc encoder fail\n");  

  108.         return NULL;  

  109.     }  

  110.     memset(encoder, 0x00, sizeof(CarEyeEncoder));  

  111.     // 注册编码器  

  112.     av_register_all();  

  113.     // 媒体编码器  

  114.     AVCodec *pCodec;  

  115.     if (aInfo.OutVideoType != CAREYE_CODEC_NONE)  

  116.     {  

  117.         // 请求视频编码器  

  118.         pCodec = avcodec_find_encoder((AVCodecID)aInfo.OutVideoType);  

  119.         if (pCodec == NULL)  

  120.         {  

  121.             CarEyeLog("Could not find video encoder.\n");  

  122.             CarEye_EncoderRelease(encoder);  

  123.             return NULL;  

  124.         }  

  125.         // 申请编码器上下文  

  126.         encoder->VEncoder = avcodec_alloc_context3(pCodec);  

  127.         if (encoder->VEncoder == NULL)  

  128.         {  

  129.             CarEyeLog("Could not alloc video encoder.\n");  

  130.             CarEye_EncoderRelease(encoder);  

  131.             return NULL;  

  132.         }  

  133.         encoder->VEncoder->codec_id = (AVCodecID)aInfo.OutVideoType;  

  134.         encoder->VEncoder->time_base.num = 1;  

  135.         // 帧率  

  136.         encoder->VEncoder->time_base.den = aInfo.FramesPerSecond;  

  137.         // 每包一个视频帧  

  138.         encoder->VEncoder->frame_number = 1;  

  139.         // 媒体类型为视频  

  140.         encoder->VEncoder->codec_type = AVMEDIA_TYPE_VIDEO;  

  141.         encoder->VEncoder->bit_rate = aInfo.VideoBitrate;  

  142.         // 视频分辨率  

  143.         encoder->VEncoder->width = aInfo.Width;  

  144.         encoder->VEncoder->height = aInfo.Height;  

  145.         encoder->VEncoder->gop_size = aInfo.GopSize;  

  146.         encoder->VEncoder->max_b_frames = aInfo.MaxBFrames;  

  147.         encoder->VEncoder->pix_fmt = (AVPixelFormat)aInfo.InVideoType;  

  148.         AVDictionary *param = NULL;  

  149.         //H.264  

  150.         if (aInfo.OutVideoType == CAREYE_CODEC_H264)  

  151.         {  

  152.             av_dict_set(¶m, "preset", "slow", 0);  

  153.             av_dict_set(¶m, "tune", "zerolatency", 0);  

  154.         }  

  155.         //H.265  

  156.         if (aInfo.OutVideoType == CAREYE_CODEC_H265)  

  157.         {  

  158.             av_dict_set(¶m, "preset", "ultrafast", 0);  

  159.             av_dict_set(¶m, "tune", "zero-latency", 0);  

  160.         }  

  161.         if (avcodec_open2(encoder->VEncoder, pCodec, ¶m) < 0)  

  162.         {  

  163.             CarEyeLog("Could not open video encoder.\n");  

  164.             CarEye_EncoderRelease(encoder);  

  165.             return NULL;  

  166.         }  

  167.         encoder->VFrame = av_frame_alloc();  

  168.         if (encoder->VFrame == NULL)  

  169.         {  

  170.             CarEyeLog("Alloc video frame faile!\n");  

  171.             CarEye_EncoderRelease(encoder);  

  172.             return NULL;  

  173.         }  

  174.         encoder->VFrame->format = encoder->VEncoder->pix_fmt;  

  175.         encoder->VFrame->width = encoder->VEncoder->width;  

  176.         encoder->VFrame->height = encoder->VEncoder->height;  

  177.         if (av_image_alloc(encoder->VFrame->data, encoder->VFrame->linesize,  

  178.             encoder->VEncoder->width, encoder->VEncoder->height,  

  179.             encoder->VEncoder->pix_fmt, 16) < 0)  

  180.         {  

  181.             CarEyeLog("Could not allocate raw picture buffer!\n");  

  182.             CarEye_EncoderRelease(encoder);  

  183.             return NULL;  

  184.         }  

  185.     }  

  186.     if (aInfo.OutAudioType != CAREYE_CODEC_NONE)  

  187.     {  

  188.         // 请求音频编码器  

  189.         pCodec = avcodec_find_encoder((AVCodecID)aInfo.OutAudioType);  

  190.         if (pCodec == NULL)  

  191.         {  

  192.             CarEyeLog("Could not find audio encoder.\n");  

  193.             CarEye_EncoderRelease(encoder);  

  194.             return NULL;  

  195.         }  

  196.         // 申请编码器上下文  

  197.         encoder->AEncoder = avcodec_alloc_context3(pCodec);  

  198.         if (encoder->AEncoder == NULL)  

  199.         {  

  200.             CarEyeLog("Could not alloc audio encoder.\n");  

  201.             CarEye_EncoderRelease(encoder);  

  202.             return NULL;  

  203.         }  

  204.         // 参数赋值  

  205.         encoder->AEncoder->codec_id = (AVCodecID)aInfo.OutAudioType;  

  206.         encoder->AEncoder->codec_type = AVMEDIA_TYPE_AUDIO;  

  207.         encoder->AEncoder->sample_fmt = AV_SAMPLE_FMT_S16P; //AV_SAMPLE_FMT_FLTP;  

  208.         encoder->AEncoder->sample_rate = aInfo.SampleRate;  

  209.         encoder->AEncoder->bit_rate = aInfo.AudioBitrate;  

  210.         encoder->AEncoder->channel_layout = AV_CH_LAYOUT_STEREO; //AV_CH_LAYOUT_STEREO;  

  211.         encoder->AEncoder->channels = av_get_channel_layout_nb_channels(encoder->AEncoder->channel_layout);  

  212.         int ret = avcodec_open2(encoder->AEncoder, pCodec, NULL);  

  213.         if (ret < 0)  

  214.         {  

  215.             CarEyeLog("Could not open audio encoder.\n");  

  216.             CarEye_EncoderRelease(encoder);  

  217.             return NULL;  

  218.         }  

  219.         encoder->AFrame = av_frame_alloc();  

  220.         if (encoder->AFrame == NULL)  

  221.         {  

  222.             printf("Alloc audio frame fail!\n");  

  223.             CarEye_EncoderRelease(encoder);  

  224.             return NULL;  

  225.         }  

  226.         encoder->AFrame->nb_samples = encoder->AEncoder->frame_size;  

  227.         encoder->AFrame->format = encoder->AEncoder->sample_fmt;  

  228.         encoder->AFrame->channel_layout = encoder->AEncoder->channel_layout;  

  229.         if (av_frame_get_buffer(encoder->AFrame, 0) < 0)  

  230.         {  

  231.             CarEyeLog("Failed to allocate the audio frame data\n");  

  232.             CarEye_EncoderRelease(encoder);  

  233.             return NULL;  

  234.         }  

  235.         encoder->PerPcmSize = av_get_bytes_per_sample(encoder->AEncoder->sample_fmt);  

  236.         encoder->PcmSize = encoder->PerPcmSize * encoder->AEncoder->channels * encoder->AFrame->nb_samples;  

  237. //      encoder->PcmBuffer = (uint8_t *)av_malloc(encoder->PcmSize);  

  238. //      avcodec_fill_audio_frame(encoder->AFrame, encoder->AEncoder->channels, encoder->AEncoder->sample_fmt, (const uint8_t *)encoder->PcmBuffer, encoder->PcmSize, 1);  

  239.         encoder->AConverter = swr_alloc();  

  240.         if (encoder->AConverter == NULL)  

  241.         {  

  242.             CarEyeLog("Allock audio converter fail!\n");  

  243.             CarEye_EncoderRelease(encoder);  

  244.             return NULL;  

  245.         }  

  246.         int out_channels = av_get_default_channel_layout(encoder->AEncoder->channels);  

  247.         encoder->AConverter = swr_alloc_set_opts(encoder->AConverter, encoder->AEncoder->channel_layout,  

  248.             encoder->AEncoder->sample_fmt, encoder->AEncoder->sample_rate,  

  249.             out_channels, encoder->AEncoder->sample_fmt, encoder->AEncoder->sample_rate, 0, NULL);  

  250.         if (swr_init(encoder->AConverter) < 0)  

  251.         {  

  252.             CarEyeLog("Init audio converter fail!\n");  

  253.             CarEye_EncoderRelease(encoder);  

  254.             return NULL;  

  255.         }  

  256.     }  

  257.     return encoder;  

  258. }  

  259. /* 

  260. * Comments: 释放编码器资源 

  261. * Param aEncoder: 要释放的编码器 

  262. * @Return None 

  263. */  

  264. CE_API void CE_APICALL CarEye_EncoderRelease(CarEye_Encoder_Handle aEncoder)  

  265. {  

  266.     CarEyeEncoder *encoder = (CarEyeEncoder *)aEncoder;  

  267.     if (encoder == NULL)  

  268.     {  

  269.         return;  

  270.     }  

  271.     if (encoder->VEncoder != NULL)  

  272.     {  

  273.         avcodec_close(encoder->VEncoder);  

  274.         av_free(encoder->VEncoder);  

  275.         encoder->VEncoder = NULL;  

  276.     }  

  277.     if (encoder->AEncoder != NULL)  

  278.     {  

  279.         avcodec_close(encoder->AEncoder);  

  280.         av_free(encoder->AEncoder);  

  281.         encoder->AEncoder = NULL;  

  282.     }  

  283.     if (encoder->VFrame != NULL)  

  284.     {  

  285.         av_frame_free(&encoder->VFrame);  

  286.         encoder->VFrame = NULL;  

  287.     }  

  288.     if (encoder->PcmBuffer != NULL)  

  289.     {  

  290.         av_freep(encoder->PcmBuffer);  

  291.         encoder->PcmBuffer = NULL;  

  292.     }  

  293.     if (encoder->AFrame != NULL)  

  294.     {  

  295.         av_frame_free(&encoder->AFrame);  

  296.         encoder->AFrame = NULL;  

  297.     }  

  298.     if (encoder->AConverter != NULL)  

  299.     {  

  300.         swr_free(&encoder->AConverter);  

  301.         encoder->AConverter = NULL;  

  302.     }  

  303.     delete encoder;  

  304.     encoder = NULL;  

  305. }  

  306. /* 

  307. * Comments: 将输入YUV视频编码为设置好的格式数据输出 

  308. * Param aEncoder: 申请到的有效编码器 

  309. * Param aYuv: 要编码的YUV数据 

  310. * Param aPts: 当前视频帧序号 

  311. * Param aBytes: [输出]编码后的视频流 

  312. * @Return int < 0编码失败,> 0为编码后数据字节个数 ==0表示参数无效 

  313. */  

  314. CE_API int CE_APICALL CarEye_EncoderYUV(CarEye_Encoder_Handle aEncoder,  

  315.                                         CarEye_YUVFrame *aYuv, int aPts,  

  316.                                         unsigned char *aBytes)  

  317. {  

  318.     CarEyeEncoder *encoder = (CarEyeEncoder *)aEncoder;  

  319.     if (encoder == NULL || encoder->VEncoder == NULL)  

  320.     {  

  321.         return 0;  

  322.     }  

  323.     if (aBytes == NULL)  

  324.     {  

  325.         return 0;  

  326.     }  

  327.     int ret;  

  328.     int out_size = 0;  

  329.     AVPacket packet = { 0 };  

  330.     av_init_packet(&packet);  

  331.     packet.data = NULL;  

  332.     packet.size = 0;  

  333.     // 赋值Y值  

  334.     memcpy(encoder->VFrame->data[0], aYuv->Y, aYuv->YSize);  

  335.     memcpy(encoder->VFrame->data[1], aYuv->U, aYuv->USize);  

  336.     memcpy(encoder->VFrame->data[2], aYuv->V, aYuv->VSize);  

  337.     encoder->VFrame->pts = aPts;  

  338.     ret = Encode(encoder->VEncoder, encoder->VFrame, &packet);  

  339.     if (ret < 0)  

  340.     {  

  341.         CarEyeLog("Encode video error.\n");  

  342.         av_packet_unref(&packet);  

  343.         return ret;  

  344.     }  

  345.     out_size = packet.size;  

  346.     if (out_size > 0)  

  347.     {  

  348.         memcpy(aBytes, packet.data, packet.size);  

  349.     }  

  350.     av_packet_unref(&packet);  

  351.     return out_size;  

  352. }  

  353. /* 

  354. * Comments: 获取PCM编码时接受的最大字节数 

  355. * Param aEncoder: 申请到的有效编码器 

  356. * @Return PCM编码缓冲区最大字节数 

  357. */  

  358. CE_API int CE_APICALL CarEye_GetPcmMaxSize(CarEye_Encoder_Handle aEncoder)  

  359. {  

  360.     CarEyeEncoder *encoder = (CarEyeEncoder *)aEncoder;  

  361.     if (encoder == NULL || encoder->AEncoder == NULL)  

  362.     {  

  363.         return -1;  

  364.     }  

  365.     return encoder->PcmSize;  

  366. }  

  367. /* 

  368. * Comments: 将输入的PCM音频编码为指定数据格式输出 

  369. * Param aEncoder: 申请到的有效编码器 

  370. * Param aPcm: 要编码的PCM数据 

  371. * Param aSize: 要编码音频流字节数 

  372. * Param aBytes: [输出] 编码后的音频流 

  373. * Param aPts: 当前编码帧的序号 

  374. * @Return int < 0编码失败,> 0为编码后PCM的字节个数 ==0表示参数无效 

  375. */  

  376. CE_API int CE_APICALL CarEye_EncoderPCM(CarEye_Encoder_Handle aEncoder,  

  377.                                 unsigned char *aPcm, int aSize, int aPts,  

  378.                                 unsigned char *aBytes)  

  379. {  

  380.     CarEyeEncoder *encoder = (CarEyeEncoder *)aEncoder;  

  381.     if (encoder == NULL || encoder->AEncoder == NULL)  

  382.     {  

  383.         return 0;  

  384.     }  

  385.     if (aBytes == NULL || aSize < 1 || aPcm == NULL)  

  386.     {  

  387.         return 0;  

  388.     }  

  389.     int ret;  

  390.     int out_size = 0;  

  391.     int i = 0, j = 0;  

  392.     int cp_count = 0;  

  393.     AVPacket packet = { 0 };  

  394.     av_init_packet(&packet);  

  395.     packet.data = NULL;  

  396.     packet.size = 0;  

  397.     for (i = 0; i < encoder->AFrame->nb_samples; i++)  

  398.     {  

  399.         for (j = 0; j < encoder->AEncoder->channels; j++)  

  400.         {  

  401.             memcpy(encoder->AFrame->data[j] + i * encoder->PerPcmSize, aPcm, encoder->PerPcmSize);  

  402.             cp_count += encoder->PerPcmSize;  

  403.             if (cp_count >= aSize)  

  404.             {  

  405.                 break;  

  406.             }  

  407.         }  

  408.     }  

  409.     encoder->AFrame->pts = aPts;  

  410.     ret = Encode(encoder->AEncoder, encoder->AFrame, &packet);  

  411.     if (ret < 0)  

  412.     {  

  413.         printf("Decode audio error.\n");  

  414.         av_packet_unref(&packet);  

  415.         return ret;  

  416.     }  

  417.     out_size = packet.size;  

  418.     if (out_size > 0)  

  419.     {  

  420.         memcpy(aBytes, packet.data, packet.size);  

  421.     }  

  422.     av_packet_unref(&packet);  

  423.     return out_size;  

  424. }

  

以上库是支持音视频编码的。我们本节只说明视频编码部分。给出视频编码的JNI接口

[cpp] view plain copy

  1. typedef struct{  

  2.     int  InVedioType;  

  3.     int  OutVedioType;  

  4.     int  fps;  

  5.     int  width;  

  6.     int  height;  

  7.     int  VideoBitrate;  

  8.     int  InputAuidoType;  

  9.     int  OutAudioType;  

  10.     int  SampleRate;  

  11.     int  AudioBitrate;  

  12.     int  Encodetype;  

  13. }ParamInfo;  

  14. JNIEXPORT jlong JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_CreateEncode(JNIEnv* env, jobject obj, jobject para) {  

  15.     void*  ret;  

  16.     CarEye_OriginalStream param;  

  17.     jclass jcInfo = (*env)->GetObjectClass(env, para);  

  18.     if (0 == jcInfo) {  

  19.         CarEyeLog("GetObjectClass returned 0\n");  

  20.         return 0;  

  21.     }  

  22.     int fps = (*env)->GetIntField(env, para, (*env)->GetFieldID(env, jcInfo, "fps", "I"));  

  23.     int InVedioType = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "InVedioType", "I"));  

  24.     int OutVedioType =(*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "OutVedioType", "I"));  

  25.     int width = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "width", "I"));  

  26.     int height =  (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "height", "I"));  

  27.     int VideoBitrate = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "VideoBitrate", "I"));  

  28.     int InputAuidoType = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "InputAuidoType", "I"));  

  29.     int OutAudioType = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "OutAudioType", "I"));  

  30.     int SampleRate = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "SampleRate", "I"));  

  31.     int AudioBitrate=(*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "AudioBitrate", "I"));  

  32.     CarEyeLog("fps:%d", fps);  

  33.     CarEyeLog("InVedioType:%d", InVedioType);  

  34.     CarEyeLog("width:%d,VideoBitrate:%d,OutVedioType:%d ", width,VideoBitrate,OutVedioType);  

  35.     param.AudioBitrate = AudioBitrate;  

  36.     param.InVideoType = InVedioType;  

  37.     param.OutAudioType = OutAudioType;  

  38.     param.OutVideoType = OutVedioType;  

  39.     param.FramesPerSecond = fps;  

  40.     param.GopSize = 10;  

  41.     param.MaxBFrames =1;  

  42.     param.Width = width;  

  43.     param.Height = height;  

  44.     param.VideoBitrate =VideoBitrate;  

  45.     param.SampleRate = SampleRate;  

  46.     ret = CarEye_EncoderCreate(param);  

  47.     if(  ret  == NULL) {  

  48.         return 0;  

  49.     }else  

  50.     {  

  51.         return (long)ret;  

  52.     }  

  53. }  

  54. JNIEXPORT jint JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_encode(JNIEnv* env, jobject obj, jlong handle,jint index, jbyteArray frame, jbyteArray OutFrame) {  

  55.     void* pHandle;  

  56.     int ret;  

  57.     unsigned char* in_data;  

  58.     unsigned char* out_data;  

  59.     CarEye_YUVFrame yuv_frame;  

  60.     if(handle==0)  

  61.         return -1;  

  62.     pHandle = (void*)handle;  

  63.     in_data = (*env)->GetByteArrayElements(env,frame, 0 );  

  64.     int len = (*env)->GetArrayLength(env,frame);  

  65.     out_data = (*env)->GetByteArrayElements(env,OutFrame, 0 );  

  66.     yuv_frame.Y = in_data;  

  67.     yuv_frame.YSize = len*2/3;  

  68.     yuv_frame.U = &in_data[len*2/3];  

  69.     yuv_frame.USize = len/6;  

  70.     yuv_frame.V = &in_data[len*5/6];  

  71.     yuv_frame.VSize = len/6;  

  72.     ret =  CarEye_EncoderYUV(pHandle,  &yuv_frame, index,out_data );  

  73.     (*env)->ReleaseByteArrayElements(env,frame,in_data,0);  

  74.     (*env)->ReleaseByteArrayElements(env,OutFrame,out_data,0);  

  75.     return ret;  

  76. }  

  77. JNIEXPORT jint JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_ReleaseEncode(JNIEnv* env, jobject obj, jlong handle) {  

  78.     void* pHandle;  

  79.     if(handle==0)  

  80.         return -1;  

  81.     pHandle = (void*)handle;  

  82.     CarEye_EncoderRelease(pHandle);  

  83.     return 0;  

  84. }

  

注意的是,参数传递了一个结构体进来,对应JAVA层需要传递一个类:给出java的类,和调用代码

public class EncodeParamInfo { int InVedioType; int OutVedioType; int fps; int width; int height; int VideoBitrate; int InputAuidoType; int OutAudioType; int SampleRate; int AudioBitrate; int Encodetype; };

[java] view plain copy

  1. void TestEncode() {  

  2.       new Thread(new Runnable() {  

  3.           @Override  

  4.           public void run() {  

  5.               int loop = 0;  

  6.               FileOutputStream out;  

  7.               FileInputStream in;  

  8.               FFmpegNative ffmpeg = new FFmpegNative();  

  9.               EncodeParamInfo info = new EncodeParamInfo();  

  10.               info.fps = 25;  

  11.               info.width = 1280;  

  12.               info.height = 720;  

  13.               info.InVedioType = 0;  

  14.               info.OutVedioType = 0x1C;  

  15.               info.InputAuidoType = 0;  

  16.               info.VideoBitrate = 3000000;  

  17.               long handle = ffmpeg.InitEncode(info);  

  18.               if (handle == 0) {  

  19.                   Log.d(TAG, "init encoder fail");  

  20.               }  

  21.               Log.d(TAG, "init encoder success");  

  22.               byte[] data = new byte[1280 * 720 * 3 / 2];  

  23.               byte[] out_data = new byte[1280 * 720 * 3 / 2];  

  24.               try {  

  25.                   File f = new File("/mnt/sdcard/out.h264");  

  26.                   if (f.exists()) f.delete();  

  27.                   f.createNewFile();  

  28.                   out = new FileOutputStream(f);  

  29.                   File input = new File("/mnt/sdcard/input.yuv");  

  30.                   in = new FileInputStream(input);  

  31.                   int len;  

  32.                   while (true) {  

  33.                       if (in.read(data, 0, 1280 * 720 * 3 / 2) < 0) {  

  34.                           Log.d(TAG, "read fail:");  

  35.                           break;  

  36.                       } else {  

  37.                           int result = ffmpeg.EncodeData(handle, loop++, data, out_data);  

  38.                           if (result > 0) {  

  39.                               out.write(out_data, 0, result);  

  40.                               Log.d(TAG, "encoder sucessful:"+result);  

  41.                           }else {  

  42.                               Log.d(TAG, "encoder fail:"+result);  

  43.                           }  

  44.                       }  

  45.                   }  

  46.                   in.close();  

  47.                   out.close();  

  48.                   ffmpeg.DestroyEncode(handle);  

  49.               } catch (Exception e) {  

  50.                   e.printStackTrace();  

  51.               }  

  52.           }  

  53.       }).start();  

  54.   }

  

相关代码请参考car-eye 开源网站和github为准

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

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

car-eye 技术官方邮箱: support@car-eye.cn    
car-eye技术交流QQ群: 590411159     

FFMEPG 平台移植,接口简化和外部模块接入 (四)ffmpeg android移植(ffmpeg 视频编码)

CopyRight©  car-eye 开源团队 2018

点赞
收藏
评论区
推荐文章
Karen110 Karen110
3年前
Python音视频剪辑库MoviePy中文教程导览
一、简介MoviePy是一个用于视频编辑的Python模块,可用于进行视频的基本操作(如剪切、拼接、标题插入)、视频合成(也称非线性编辑)、视频处理或创建高级效果。它可以读写最常见的视频格式,MoviePy能处理的视频是ffmpeg格式的,老猿理解支持的文件类型至少包括:\.mp4\.wmv\.rm\.avi\.flv\.webm\.wav\.
GoCoding GoCoding
3年前
FFmpeg 播放 RTSP/Webcam 流
本文将介绍FFmpeg如何播放RTSP/Webcam/File流。流程如下:bashRTSP/Webcam/FileFFmpegopenanddecodetoBGR/YUVOpenCV/OpenGLdisplay代码:https://github.com/ikuokuo/rtspwasmplayer,子模块rtsploca
Stella981 Stella981
3年前
FFmpeg转封装rtsp到rtmp(无需转码,低资源消耗)
FFmpeg转封装rtsp到rtmp(无需转码,低资源消耗)发表于20191231|分类于FFmpeg(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.banmajio.com%2Fcategori
Stella981 Stella981
3年前
FFmpeg命令行工具学习(四):FFmpeg 采集设备
在使用FFmpeg作为编码器时,可以使用FFmpeg采集本地的音视频采集设备的数据,然后进行编码、封装、传输等操作。例如,我们可以采集摄像头的图像作为视频,采集麦克风的数据作为音频,然后对采集的音视频数据进行编码,最后将编码后的数据封装成多媒体文件或者作为音视频流发送到服务器上(流媒体)。出于硬件环境和篇幅的限制,本文主要讲的时Mac平台下通过F
Wesley13 Wesley13
3年前
Linux 下完整安装ffmpeg(包括各种解码器)
FFmpeg是什么?FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用LGPL或GPL许可证(依据你选择的组件)。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多codec都是从头开发的。FFmpeg在Linux平
Stella981 Stella981
3年前
FFMEPG 平台移植,接口简化和外部模块接入 (二)ffmpeg android移植(JNI 开发环境建立)
工欲善其事情,必先利其器。在android下使用ffmpeg必须使用JNI。先创建一个好的编译工具为先:按照下面步骤在androidstdio下创建JNI的编译环境1.配置AnroidStudio(这步是关键)选中项目,单击File菜单,选择Setting进入设置界面。或者按快捷键CtrlAltS。:!(https://img
Stella981 Stella981
3年前
FFmpeg + OpenGLES 实现视频解码播放和视频滤镜
该原创文章首发于微信公众号:字节流动FFmpeg开发系列连载:FFmpeg开发(01):FFmpeg编译和集成(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.jianshu.com%2Fp%2F0792f6bbc9f6)FFmpeg开发(02):FFm
Stella981 Stella981
3年前
FFmpeg使用教程(一)
ffmpeg是一个开源的音视频转码工具,它提供了录制、转换以及流化音视频的完整解决方案,可以转码、压制、提取、截取、合并、录屏等。一、下载FFmpeg下载地址:http://ffmpeg.zeranoe.com/builds/(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F
Stella981 Stella981
3年前
FFMEPG 平台移植,接口简化和外部模块接入 (三)ffmpeg android移植(ffmpeg实现水印文字显示)
首先来看一副图,用来纪念对视频领域做出贡献的雷神:!(https://imgblog.csdn.net/20180518102442303)当然在这个图片里面的decode不是必须的,Filter模块本身是一个非常独立的模块,但因为相关的程序,给人造成了他必须要依赖于decoder或者encoder来工作。OK不多说,先看看内部实现代码
Stella981 Stella981
3年前
FFMEPG 平台移植,接口简化和外部模块接入 (一)ffmpeg android移植(ndk 编译)
CareyeFFMPEG项目是Careye开源平台的一个新项目,目的是在FFMPEG项目和其外部模块,如freetype,X264的基础上建立一个多平台编译,接口调用简单的工具集。本博客是一个连载,首先从android开始写吧。因为FFMPEG本身对android编译的支持就不是特别好。本文开发环境是ubuntu分步来做:1\.建立