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.   
  13. #ifndef __CAREYE_ENCODER_H__  
  14. #define __CAREYE_ENCODER_H__  
  15.   
  16.   
  17. #include "public.h"  
  18.   
  19.   
  20.  // 编码器对象句柄定义  
  21. #define CarEye_Encoder_Handle void*  
  22.   
  23.  // 最大音频帧大小 1 second of 48khz 32bit audio  
  24. #define MAX_AUDIO_FRAME_SIZE 192000  
  25.   
  26.  // 媒体编码类型定义 与FFMPEG中一一对应,H265定义与其他库定义需要转换  
  27.   enum CarEye_CodecType  
  28. {  
  29.     // 不进行编码  
  30.     CAREYE_CODEC_NONE = 0,  
  31.     // H264编码  
  32.     CAREYE_CODEC_H264 = 0x1C,  
  33.     // H265编码  
  34.     CAREYE_CODEC_H265 = 0xAE,  
  35.     // MJPEG编码  
  36.     CAREYE_CODEC_MJPEG = 0x08,  
  37.     // MPEG4编码  
  38.     CAREYE_CODEC_MPEG4 = 0x0D,  
  39.     // AAC编码  
  40.     CAREYE_CODEC_AAC = 0x15002,  
  41.     // G711 Ulaw编码 对应FFMPEG中的AV_CODEC_ID_PCM_MULAW定义  
  42.     CAREYE_CODEC_G711U = 0x10006,  
  43.     // G711 Alaw编码 对应FFMPEG中的AV_CODEC_ID_PCM_ALAW定义  
  44.     CAREYE_CODEC_G711A = 0x10007,  
  45.     // G726编码 对应FFMPEG中的AV_CODEC_ID_ADPCM_G726定义  
  46.     CAREYE_CODEC_G726 = 0x1100B,  
  47. };  
  48.   
  49. // YUV视频流格式定义,与FFMPEG中一一对应  
  50. enum CarEye_AVType  
  51. {  
  52.     CAREYE_FMT_YUV420P = 0,  
  53.     CAREYE_FMT_YUV422P = 4,  
  54.     CAREYE_FMT_YUV444P = 5,  
  55.     CAREYE_FMT_YUV410P = 6,  
  56.     CAREYE_FMT_YUV411P = 7,  
  57. };  
  58.   
  59. // 原始流结构定义  
  60. typedef struct CarEye_OriginalStream  
  61. {  
  62.     // 视频输入流格式  
  63.     enum CarEye_AVType InVideoType;  
  64.     // 期望输出的视频流格式,不期望输出可设置为CAREYE_CODEC_NONE  
  65.     enum CarEye_CodecType OutVideoType;  
  66.     // 期望输出的音频流格式,不期望输出可设置为CAREYE_CODEC_NONE  
  67.     enum CarEye_CodecType OutAudioType;  
  68.     // 视频帧率(FPS),推荐值:25  
  69.     unsigned char   FramesPerSecond;  
  70.     // 视频宽度像素  
  71.     unsigned short  Width;  
  72.     // 视频的高度像素  
  73.     unsigned short  Height;  
  74.     // 一组图片中的图片数量,推荐值:10  
  75.     int             GopSize;  
  76.     // 非B帧之间的B帧的最大数量,推荐值:1  
  77.     int             MaxBFrames;  
  78.     // 视频码率,越高视频越清楚,相应体积也越大 如:4000000  
  79.     float           VideoBitrate;  
  80.   
  81.     // 音频采样率 如:44100  
  82.     unsigned int    SampleRate;  
  83.     // 音频比特率 如:64000,越高声音越清楚,相应体积也越大  
  84.     float           AudioBitrate;  
  85. }CarEye_OriginalStream;  
  86.   
  87. // YUV媒体流结构定义  
  88.   
  89.   
  90. #ifdef __cplusplus  
  91. extern "C"  
  92. {  
  93. #endif  
  94.     /* 
  95.     * Comments: 创建一个编码器对象 
  96.     * Param aInfo: 要编码的媒体信息 
  97.     * @Return CarEye_Encoder_Handle 成功返回编码器对象,否则返回NULL 
  98.     */  
  99.     CE_API CarEye_Encoder_Handle CE_APICALL CarEye_EncoderCreate( CarEye_OriginalStream aInfo);  
  100.   
  101.     /* 
  102.     * Comments: 释放编码器资源 
  103.     * Param aEncoder: 要释放的编码器 
  104.     * @Return None 
  105.     */  
  106.     CE_API void CE_APICALL CarEye_EncoderRelease(CarEye_Encoder_Handle aEncoder);  
  107.   
  108.   
  109.     /* 
  110.     * Comments: 将输入YUV视频编码为设置好的格式数据输出 
  111.     * Param aEncoder: 申请到的有效编码器 
  112.     * Param aYuv: 要编码的YUV数据 
  113.     * Param aPts: 当前视频帧序号 
  114.     * Param aBytes: [输出]编码后的视频流 
  115.     * @Return int < 0编码失败,> 0为编码后数据字节个数 ==0表示参数无效 
  116.     */  
  117.     CE_API int CE_APICALL CarEye_EncoderYUV(CarEye_Encoder_Handle aEncoder,  
  118.                                     CarEye_YUVFrame *aYuv, int aPts,  
  119.                                     unsigned char *aBytes);  
  120.   
  121.     /* 
  122.     * Comments: 获取PCM编码时接受的最大字节数 
  123.     * Param aEncoder: 申请到的有效编码器 
  124.     * @Return PCM编码缓冲区最大字节数 
  125.     */  
  126.     CE_API int CE_APICALL CarEye_GetPcmMaxSize(CarEye_Encoder_Handle aEncoder);  
  127.   
  128.     /* 
  129.     * Comments: 将输入的PCM音频编码为指定数据格式输出 
  130.     * Param aEncoder: 申请到的有效编码器 
  131.     * Param aPcm: 要编码的PCM数据 
  132.     * Param aSize: 要编码音频流字节数 
  133.     * Param aBytes: [输出] 编码后的音频流 
  134.     * Param aPts: 当前编码帧的序号 
  135.     * @Return int < 0编码失败,> 0为编码后PCM的字节个数 ==0表示参数无效 
  136.     */  
  137.     CE_API int CE_APICALL CarEye_EncoderPCM(CarEye_Encoder_Handle aEncoder,  
  138.                                     unsigned char *aPcm, int aSize, int aPts,  
  139.                                     unsigned char *aBytes);  
  140.   
  141. #ifdef __cplusplus  
  142. }  
  143. #endif  
  144.   
  145. #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.   
  13. #include "CarEyeEncoderAPI.h"  
  14. #include "FFVideoFilter.h"  
  15.   
  16. #ifdef _WIN32  
  17.  //Windows  
  18. extern "C"  
  19. {  
  20. #include "libavutil/opt.h"  
  21. #include "libavcodec/avcodec.h"  
  22. #include "libavutil/imgutils.h"  
  23. #include "libavformat/avformat.h"  
  24. #include "libswresample/swresample.h"  
  25. #include "libavfilter/avfilter.h"  
  26. };  
  27. #else  
  28.  //Linux...  
  29. #ifdef __cplusplus  
  30. extern "C"  
  31. {  
  32. #endif  
  33. #include <libavutil/opt.h>  
  34. #include <libavcodec/avcodec.h>  
  35. #include <libavutil/imgutils.h>  
  36. #include <libavformat/avformat.h>  
  37. #include <libswresample/swresample.h>  
  38. #include <libavfilter/avfilter.h>  
  39. #ifdef __cplusplus  
  40. };  
  41. #endif  
  42. #endif  
  43.   
  44.   
  45. // 编码器结构体定义  
  46. typedef struct  
  47. {  
  48.     // 视频编码器  
  49.     AVCodecContext *VEncoder;  
  50.     // 音频编码器  
  51.     AVCodecContext *AEncoder;  
  52.     // 编码后的视频帧 音视频帧对象分别定义,防止多线程分别编码音视频造成读写冲突  
  53.     AVFrame *VFrame;  
  54.     // 编码后的音频帧  
  55.     AVFrame *AFrame;  
  56.     // 音频转码器  
  57.     struct SwrContext *AConverter;  
  58.     // 存储PCM数据缓冲区  
  59.     unsigned char *PcmBuffer;  
  60.     // 接收PCM字节个数上限  
  61.     int PcmSize;  
  62.     // 每组PCM数据的字节数  
  63.     int PerPcmSize;  
  64.     // 视频字幕对象  
  65. }CarEyeEncoder;  
  66.   
  67.   
  68. /* 
  69. * Comments: 利用编码器对媒体包进行编码并输出编码后的数据 
  70. * Param aEncoder: 有效的编码器 
  71. * Param aFrame: 要编码的媒体数据包 
  72. * Param aPacket: [输出] 编码后的数据 
  73. * @Return int 小于0失败,等于0成功 
  74. */  
  75. static int Encode(AVCodecContext *aEncoder, AVFrame *aFrame, AVPacket *aPacket)  
  76. {  
  77.     int ret;  
  78.   
  79.     ret = avcodec_send_frame(aEncoder, aFrame);  
  80.     if (ret < 0)  
  81.     {  
  82.         printf("Error sending a packet for encoding\n");  
  83.         return ret;  
  84.     }  
  85.   
  86.     ret = avcodec_receive_packet(aEncoder, aPacket);  
  87.     if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)  
  88.     {  
  89.         return 0;  
  90.     }  
  91.     else if (ret < 0)  
  92.     {  
  93.         //error during encoding  
  94.         return -1;  
  95.     }  
  96.     return ret;  
  97. }  
  98.   
  99. /* 
  100. * Comments: 创建一个编码器对象 
  101. * Param aInfo: 要编码的媒体信息 
  102. * @Return CarEye_Encoder_Handle 成功返回编码器对象,否则返回NULL 
  103. */  
  104. CE_API CarEye_Encoder_Handle CE_APICALL CarEye_EncoderCreate(CarEye_OriginalStream aInfo)  
  105. {  
  106.     if (aInfo.OutVideoType == CAREYE_CODEC_NONE  
  107.         && aInfo.OutAudioType == CAREYE_CODEC_NONE)  
  108.     {  
  109.         CarEyeLog("null paramter\n");  
  110.         // 至少包含一项编码需求  
  111.         return NULL;  
  112.     }  
  113.   
  114.     CarEyeEncoder *encoder = new CarEyeEncoder;  
  115.     if (encoder == NULL)  
  116.     {  
  117.         CarEyeLog("alloc encoder fail\n");  
  118.         return NULL;  
  119.     }  
  120.   
  121.     memset(encoder, 0x00, sizeof(CarEyeEncoder));  
  122.   
  123.     // 注册编码器  
  124.     av_register_all();  
  125.   
  126.     // 媒体编码器  
  127.     AVCodec *pCodec;  
  128.     if (aInfo.OutVideoType != CAREYE_CODEC_NONE)  
  129.     {  
  130.         // 请求视频编码器  
  131.         pCodec = avcodec_find_encoder((AVCodecID)aInfo.OutVideoType);  
  132.         if (pCodec == NULL)  
  133.         {  
  134.             CarEyeLog("Could not find video encoder.\n");  
  135.             CarEye_EncoderRelease(encoder);  
  136.             return NULL;  
  137.         }  
  138.         // 申请编码器上下文  
  139.         encoder->VEncoder = avcodec_alloc_context3(pCodec);  
  140.         if (encoder->VEncoder == NULL)  
  141.         {  
  142.             CarEyeLog("Could not alloc video encoder.\n");  
  143.             CarEye_EncoderRelease(encoder);  
  144.             return NULL;  
  145.         }  
  146.         encoder->VEncoder->codec_id = (AVCodecID)aInfo.OutVideoType;  
  147.         encoder->VEncoder->time_base.num = 1;  
  148.         // 帧率  
  149.         encoder->VEncoder->time_base.den = aInfo.FramesPerSecond;  
  150.         // 每包一个视频帧  
  151.         encoder->VEncoder->frame_number = 1;  
  152.         // 媒体类型为视频  
  153.         encoder->VEncoder->codec_type = AVMEDIA_TYPE_VIDEO;  
  154.         encoder->VEncoder->bit_rate = aInfo.VideoBitrate;  
  155.         // 视频分辨率  
  156.         encoder->VEncoder->width = aInfo.Width;  
  157.         encoder->VEncoder->height = aInfo.Height;  
  158.         encoder->VEncoder->gop_size = aInfo.GopSize;  
  159.         encoder->VEncoder->max_b_frames = aInfo.MaxBFrames;  
  160.         encoder->VEncoder->pix_fmt = (AVPixelFormat)aInfo.InVideoType;  
  161.   
  162.         AVDictionary *param = NULL;  
  163.         //H.264  
  164.         if (aInfo.OutVideoType == CAREYE_CODEC_H264)  
  165.         {  
  166.             av_dict_set(¶m, "preset", "slow", 0);  
  167.             av_dict_set(¶m, "tune", "zerolatency", 0);  
  168.         }  
  169.         //H.265  
  170.         if (aInfo.OutVideoType == CAREYE_CODEC_H265)  
  171.         {  
  172.             av_dict_set(¶m, "preset", "ultrafast", 0);  
  173.             av_dict_set(¶m, "tune", "zero-latency", 0);  
  174.         }  
  175.   
  176.         if (avcodec_open2(encoder->VEncoder, pCodec, ¶m) < 0)  
  177.         {  
  178.             CarEyeLog("Could not open video encoder.\n");  
  179.             CarEye_EncoderRelease(encoder);  
  180.             return NULL;  
  181.         }  
  182.   
  183.         encoder->VFrame = av_frame_alloc();  
  184.         if (encoder->VFrame == NULL)  
  185.         {  
  186.             CarEyeLog("Alloc video frame faile!\n");  
  187.             CarEye_EncoderRelease(encoder);  
  188.             return NULL;  
  189.         }  
  190.         encoder->VFrame->format = encoder->VEncoder->pix_fmt;  
  191.         encoder->VFrame->width = encoder->VEncoder->width;  
  192.         encoder->VFrame->height = encoder->VEncoder->height;  
  193.   
  194.         if (av_image_alloc(encoder->VFrame->data, encoder->VFrame->linesize,  
  195.             encoder->VEncoder->width, encoder->VEncoder->height,  
  196.             encoder->VEncoder->pix_fmt, 16) < 0)  
  197.         {  
  198.             CarEyeLog("Could not allocate raw picture buffer!\n");  
  199.             CarEye_EncoderRelease(encoder);  
  200.             return NULL;  
  201.         }  
  202.     }  
  203.     if (aInfo.OutAudioType != CAREYE_CODEC_NONE)  
  204.     {  
  205.         // 请求音频编码器  
  206.         pCodec = avcodec_find_encoder((AVCodecID)aInfo.OutAudioType);  
  207.         if (pCodec == NULL)  
  208.         {  
  209.             CarEyeLog("Could not find audio encoder.\n");  
  210.             CarEye_EncoderRelease(encoder);  
  211.             return NULL;  
  212.         }  
  213.         // 申请编码器上下文  
  214.         encoder->AEncoder = avcodec_alloc_context3(pCodec);  
  215.         if (encoder->AEncoder == NULL)  
  216.         {  
  217.             CarEyeLog("Could not alloc audio encoder.\n");  
  218.             CarEye_EncoderRelease(encoder);  
  219.             return NULL;  
  220.         }  
  221.   
  222.         // 参数赋值  
  223.         encoder->AEncoder->codec_id = (AVCodecID)aInfo.OutAudioType;  
  224.         encoder->AEncoder->codec_type = AVMEDIA_TYPE_AUDIO;  
  225.         encoder->AEncoder->sample_fmt = AV_SAMPLE_FMT_S16P; //AV_SAMPLE_FMT_FLTP;  
  226.         encoder->AEncoder->sample_rate = aInfo.SampleRate;  
  227.         encoder->AEncoder->bit_rate = aInfo.AudioBitrate;  
  228.         encoder->AEncoder->channel_layout = AV_CH_LAYOUT_STEREO; //AV_CH_LAYOUT_STEREO;  
  229.         encoder->AEncoder->channels = av_get_channel_layout_nb_channels(encoder->AEncoder->channel_layout);  
  230.         int ret = avcodec_open2(encoder->AEncoder, pCodec, NULL);  
  231.         if (ret < 0)  
  232.         {  
  233.             CarEyeLog("Could not open audio encoder.\n");  
  234.             CarEye_EncoderRelease(encoder);  
  235.             return NULL;  
  236.         }  
  237.   
  238.         encoder->AFrame = av_frame_alloc();  
  239.         if (encoder->AFrame == NULL)  
  240.         {  
  241.             printf("Alloc audio frame fail!\n");  
  242.             CarEye_EncoderRelease(encoder);  
  243.             return NULL;  
  244.         }  
  245.         encoder->AFrame->nb_samples = encoder->AEncoder->frame_size;  
  246.         encoder->AFrame->format = encoder->AEncoder->sample_fmt;  
  247.         encoder->AFrame->channel_layout = encoder->AEncoder->channel_layout;  
  248.         if (av_frame_get_buffer(encoder->AFrame, 0) < 0)  
  249.         {  
  250.             CarEyeLog("Failed to allocate the audio frame data\n");  
  251.             CarEye_EncoderRelease(encoder);  
  252.             return NULL;  
  253.         }  
  254.         encoder->PerPcmSize = av_get_bytes_per_sample(encoder->AEncoder->sample_fmt);  
  255.         encoder->PcmSize = encoder->PerPcmSize * encoder->AEncoder->channels * encoder->AFrame->nb_samples;  
  256. //      encoder->PcmBuffer = (uint8_t *)av_malloc(encoder->PcmSize);  
  257. //      avcodec_fill_audio_frame(encoder->AFrame, encoder->AEncoder->channels, encoder->AEncoder->sample_fmt, (const uint8_t *)encoder->PcmBuffer, encoder->PcmSize, 1);  
  258.         encoder->AConverter = swr_alloc();  
  259.         if (encoder->AConverter == NULL)  
  260.         {  
  261.             CarEyeLog("Allock audio converter fail!\n");  
  262.             CarEye_EncoderRelease(encoder);  
  263.             return NULL;  
  264.         }  
  265.         int out_channels = av_get_default_channel_layout(encoder->AEncoder->channels);  
  266.         encoder->AConverter = swr_alloc_set_opts(encoder->AConverter, encoder->AEncoder->channel_layout,  
  267.             encoder->AEncoder->sample_fmt, encoder->AEncoder->sample_rate,  
  268.             out_channels, encoder->AEncoder->sample_fmt, encoder->AEncoder->sample_rate, 0, NULL);  
  269.         if (swr_init(encoder->AConverter) < 0)  
  270.         {  
  271.             CarEyeLog("Init audio converter fail!\n");  
  272.             CarEye_EncoderRelease(encoder);  
  273.             return NULL;  
  274.         }  
  275.     }  
  276.   
  277.     return encoder;  
  278. }  
  279.   
  280. /* 
  281. * Comments: 释放编码器资源 
  282. * Param aEncoder: 要释放的编码器 
  283. * @Return None 
  284. */  
  285. CE_API void CE_APICALL CarEye_EncoderRelease(CarEye_Encoder_Handle aEncoder)  
  286. {  
  287.     CarEyeEncoder *encoder = (CarEyeEncoder *)aEncoder;  
  288.   
  289.     if (encoder == NULL)  
  290.     {  
  291.         return;  
  292.     }  
  293.     if (encoder->VEncoder != NULL)  
  294.     {  
  295.         avcodec_close(encoder->VEncoder);  
  296.         av_free(encoder->VEncoder);  
  297.         encoder->VEncoder = NULL;  
  298.     }  
  299.     if (encoder->AEncoder != NULL)  
  300.     {  
  301.         avcodec_close(encoder->AEncoder);  
  302.         av_free(encoder->AEncoder);  
  303.         encoder->AEncoder = NULL;  
  304.     }  
  305.     if (encoder->VFrame != NULL)  
  306.     {  
  307.         av_frame_free(&encoder->VFrame);  
  308.         encoder->VFrame = NULL;  
  309.     }  
  310.     if (encoder->PcmBuffer != NULL)  
  311.     {  
  312.         av_freep(encoder->PcmBuffer);  
  313.         encoder->PcmBuffer = NULL;  
  314.     }  
  315.     if (encoder->AFrame != NULL)  
  316.     {  
  317.         av_frame_free(&encoder->AFrame);  
  318.         encoder->AFrame = NULL;  
  319.     }  
  320.     if (encoder->AConverter != NULL)  
  321.     {  
  322.         swr_free(&encoder->AConverter);  
  323.         encoder->AConverter = NULL;  
  324.     }  
  325.     delete encoder;  
  326.     encoder = NULL;  
  327. }  
  328.   
  329. /* 
  330. * Comments: 将输入YUV视频编码为设置好的格式数据输出 
  331. * Param aEncoder: 申请到的有效编码器 
  332. * Param aYuv: 要编码的YUV数据 
  333. * Param aPts: 当前视频帧序号 
  334. * Param aBytes: [输出]编码后的视频流 
  335. * @Return int < 0编码失败,> 0为编码后数据字节个数 ==0表示参数无效 
  336. */  
  337. CE_API int CE_APICALL CarEye_EncoderYUV(CarEye_Encoder_Handle aEncoder,  
  338.                                         CarEye_YUVFrame *aYuv, int aPts,  
  339.                                         unsigned char *aBytes)  
  340. {  
  341.     CarEyeEncoder *encoder = (CarEyeEncoder *)aEncoder;  
  342.     if (encoder == NULL || encoder->VEncoder == NULL)  
  343.     {  
  344.         return 0;  
  345.     }  
  346.     if (aBytes == NULL)  
  347.     {  
  348.         return 0;  
  349.     }  
  350.     int ret;  
  351.     int out_size = 0;  
  352.     AVPacket packet = { 0 };  
  353.     av_init_packet(&packet);  
  354.     packet.data = NULL;  
  355.     packet.size = 0;  
  356.     // 赋值Y值  
  357.     memcpy(encoder->VFrame->data[0], aYuv->Y, aYuv->YSize);  
  358.     memcpy(encoder->VFrame->data[1], aYuv->U, aYuv->USize);  
  359.     memcpy(encoder->VFrame->data[2], aYuv->V, aYuv->VSize);  
  360.     encoder->VFrame->pts = aPts;  
  361.     ret = Encode(encoder->VEncoder, encoder->VFrame, &packet);  
  362.     if (ret < 0)  
  363.     {  
  364.         CarEyeLog("Encode video error.\n");  
  365.         av_packet_unref(&packet);  
  366.         return ret;  
  367.     }  
  368.   
  369.     out_size = packet.size;  
  370.     if (out_size > 0)  
  371.     {  
  372.         memcpy(aBytes, packet.data, packet.size);  
  373.     }  
  374.     av_packet_unref(&packet);  
  375.     return out_size;  
  376. }  
  377.   
  378. /* 
  379. * Comments: 获取PCM编码时接受的最大字节数 
  380. * Param aEncoder: 申请到的有效编码器 
  381. * @Return PCM编码缓冲区最大字节数 
  382. */  
  383. CE_API int CE_APICALL CarEye_GetPcmMaxSize(CarEye_Encoder_Handle aEncoder)  
  384. {  
  385.     CarEyeEncoder *encoder = (CarEyeEncoder *)aEncoder;  
  386.     if (encoder == NULL || encoder->AEncoder == NULL)  
  387.     {  
  388.         return -1;  
  389.     }  
  390.   
  391.     return encoder->PcmSize;  
  392. }  
  393.   
  394. /* 
  395. * Comments: 将输入的PCM音频编码为指定数据格式输出 
  396. * Param aEncoder: 申请到的有效编码器 
  397. * Param aPcm: 要编码的PCM数据 
  398. * Param aSize: 要编码音频流字节数 
  399. * Param aBytes: [输出] 编码后的音频流 
  400. * Param aPts: 当前编码帧的序号 
  401. * @Return int < 0编码失败,> 0为编码后PCM的字节个数 ==0表示参数无效 
  402. */  
  403. CE_API int CE_APICALL CarEye_EncoderPCM(CarEye_Encoder_Handle aEncoder,  
  404.                                 unsigned char *aPcm, int aSize, int aPts,  
  405.                                 unsigned char *aBytes)  
  406. {  
  407.     CarEyeEncoder *encoder = (CarEyeEncoder *)aEncoder;  
  408.     if (encoder == NULL || encoder->AEncoder == NULL)  
  409.     {  
  410.         return 0;  
  411.     }  
  412.     if (aBytes == NULL || aSize < 1 || aPcm == NULL)  
  413.     {  
  414.         return 0;  
  415.     }  
  416.   
  417.     int ret;  
  418.     int out_size = 0;  
  419.     int i = 0, j = 0;  
  420.     int cp_count = 0;  
  421.     AVPacket packet = { 0 };  
  422.   
  423.     av_init_packet(&packet);  
  424.     packet.data = NULL;  
  425.     packet.size = 0;  
  426.   
  427.     for (i = 0; i < encoder->AFrame->nb_samples; i++)  
  428.     {  
  429.         for (j = 0; j < encoder->AEncoder->channels; j++)  
  430.         {  
  431.             memcpy(encoder->AFrame->data[j] + i * encoder->PerPcmSize, aPcm, encoder->PerPcmSize);  
  432.             cp_count += encoder->PerPcmSize;  
  433.             if (cp_count >= aSize)  
  434.             {  
  435.                 break;  
  436.             }  
  437.         }  
  438.     }  
  439.   
  440.     encoder->AFrame->pts = aPts;  
  441.     ret = Encode(encoder->AEncoder, encoder->AFrame, &packet);  
  442.     if (ret < 0)  
  443.     {  
  444.         printf("Decode audio error.\n");  
  445.         av_packet_unref(&packet);  
  446.         return ret;  
  447.     }  
  448.   
  449.     out_size = packet.size;  
  450.     if (out_size > 0)  
  451.     {  
  452.         memcpy(aBytes, packet.data, packet.size);  
  453.     }  
  454.     av_packet_unref(&packet);  
  455.   
  456.     return out_size;  
  457. }  

以上库是支持音视频编码的。我们本节只说明视频编码部分。给出视频编码的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.   
  15. JNIEXPORT jlong JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_CreateEncode(JNIEnv* env, jobject obj, jobject para) {  
  16.     void*  ret;  
  17.     CarEye_OriginalStream param;  
  18.     jclass jcInfo = (*env)->GetObjectClass(env, para);  
  19.     if (0 == jcInfo) {  
  20.         CarEyeLog("GetObjectClass returned 0\n");  
  21.         return 0;  
  22.     }  
  23.     int fps = (*env)->GetIntField(env, para, (*env)->GetFieldID(env, jcInfo, "fps", "I"));  
  24.     int InVedioType = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "InVedioType", "I"));  
  25.     int OutVedioType =(*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "OutVedioType", "I"));  
  26.     int width = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "width", "I"));  
  27.     int height =  (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "height", "I"));  
  28.     int VideoBitrate = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "VideoBitrate", "I"));  
  29.     int InputAuidoType = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "InputAuidoType", "I"));  
  30.     int OutAudioType = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "OutAudioType", "I"));  
  31.     int SampleRate = (*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "SampleRate", "I"));  
  32.     int AudioBitrate=(*env)->GetIntField(env, para,(*env)->GetFieldID(env, jcInfo, "AudioBitrate", "I"));  
  33.     CarEyeLog("fps:%d", fps);  
  34.     CarEyeLog("InVedioType:%d", InVedioType);  
  35.     CarEyeLog("width:%d,VideoBitrate:%d,OutVedioType:%d ", width,VideoBitrate,OutVedioType);  
  36.     param.AudioBitrate = AudioBitrate;  
  37.     param.InVideoType = InVedioType;  
  38.     param.OutAudioType = OutAudioType;  
  39.     param.OutVideoType = OutVedioType;  
  40.     param.FramesPerSecond = fps;  
  41.     param.GopSize = 10;  
  42.     param.MaxBFrames =1;  
  43.     param.Width = width;  
  44.     param.Height = height;  
  45.     param.VideoBitrate =VideoBitrate;  
  46.     param.SampleRate = SampleRate;  
  47.     ret = CarEye_EncoderCreate(param);  
  48.     if(  ret  == NULL) {  
  49.         return 0;  
  50.     }else  
  51.     {  
  52.         return (long)ret;  
  53.     }  
  54. }  
  55. JNIEXPORT jint JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_encode(JNIEnv* env, jobject obj, jlong handle,jint index, jbyteArray frame, jbyteArray OutFrame) {  
  56.     void* pHandle;  
  57.     int ret;  
  58.     unsigned char* in_data;  
  59.     unsigned char* out_data;  
  60.     CarEye_YUVFrame yuv_frame;  
  61.     if(handle==0)  
  62.         return -1;  
  63.     pHandle = (void*)handle;  
  64.     in_data = (*env)->GetByteArrayElements(env,frame, 0 );  
  65.     int len = (*env)->GetArrayLength(env,frame);  
  66.     out_data = (*env)->GetByteArrayElements(env,OutFrame, 0 );  
  67.     yuv_frame.Y = in_data;  
  68.     yuv_frame.YSize = len*2/3;  
  69.     yuv_frame.U = &in_data[len*2/3];  
  70.     yuv_frame.USize = len/6;  
  71.     yuv_frame.V = &in_data[len*5/6];  
  72.     yuv_frame.VSize = len/6;  
  73.     ret =  CarEye_EncoderYUV(pHandle,  &yuv_frame, index,out_data );  
  74.     (*env)->ReleaseByteArrayElements(env,frame,in_data,0);  
  75.     (*env)->ReleaseByteArrayElements(env,OutFrame,out_data,0);  
  76.     return ret;  
  77. }  
  78.   
  79. JNIEXPORT jint JNICALL Java_com_CarEye_CarEyelib_ffmpegandroid_FFmpegNative_ReleaseEncode(JNIEnv* env, jobject obj, jlong handle) {  
  80.     void* pHandle;  
  81.     if(handle==0)  
  82.         return -1;  
  83.     pHandle = (void*)handle;  
  84.     CarEye_EncoderRelease(pHandle);  
  85.     return 0;  
  86. }  

注意的是,参数传递了一个结构体进来,对应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.   
  38.                           int result = ffmpeg.EncodeData(handle, loop++, data, out_data);  
  39.                           if (result > 0) {  
  40.                               out.write(out_data, 0, result);  
  41.                               Log.d(TAG, "encoder sucessful:"+result);  
  42.                           }else {  
  43.                               Log.d(TAG, "encoder fail:"+result);  
  44.                           }  
  45.                       }  
  46.                   }  
  47.                   in.close();  
  48.                   out.close();  
  49.                   ffmpeg.DestroyEncode(handle);  
  50.   
  51.               } catch (Exception e) {  
  52.                   e.printStackTrace();  
  53.               }  
  54.           }  
  55.       }).start();  
  56.   
  57.   }  

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

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

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

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

CopyRight©  car-eye 开源团队 2018

04-04 04:54