基于am335x平台 mjpeg转码h264
来源:互联网 发布:sql数据库管理下载 编辑:程序博客网 时间:2024/05/19 17:57
简单介绍下:公司am335x平台谈了一个安防方向的应用,基本功能差不多实现,客户提出在特定场景采集视频,然后转码为h264,通过局域网传输到服务器。采集视频采用uvc摄像头,采集格式支持mjpeg,yuv。考虑到两者采集文件都偏大,如果客户端较多,这样造成服务器端网络风暴,因此需要转码为h264.
uv视频格式,相同条件下文件过大,以及一个很现实的问题(am335x平台usb dma存在bug,高速率传输会丢包,因此限定分辨率上限是320x240)因此确定采集mjpeg视频(这个问题很烦人,我还花了2周时间追usb、uvc、cppi41驱动代码,追ti官方usb的buglist。。)。查阅资料,最终确定方案为:采集到mjpeg视频文件,通过ffmpeg+x264转码,最终以h264形式保存。文件显著变小:5s 25fps 640x480分辨率 文件大小为8.5M mjpeg视频,转码后390k。
下面分2方面介绍这里的工作:完成转码基本功能、转码优化。建议刚接触音视频转码的童鞋先了解一下ffmpeg编解码的基本流程,以及一些基本概念:封装格式、编码格式、未经过压缩格式RGB、YUV422P、YUV420P,以及之间的转换:http://blog.csdn.net/leixiaohua1020/article/details/15811977。
一、实现转码基本功能
1.移植ffmepg+x264+yasm:
从官网上下载最新的源码,交叉编译,应该比较简单。这里只说明一下编译选项。
yasm:./configure --enable-shared --prefix=/usr/local/cross-ffmpeg --host=arm-linux CC=/opt/arm-2014.05/bin/arm-none-linux-gnueabi-gcc
x264:./configure --enable-shared --host=arm-linux --prefix=/usr/local/cross-ffmpeg --cross-prefix=/opt/arm-2014.05/bin/arm-none-linux-gnueabi- --disable-asm
ffmpeg:./configure --enable-cross-compile --arch=armv7 --target-os=linux --cross-prefix=/opt/arm-2014.05/bin/arm-none-linux-gnueabi- --enable-shared --disable-static --enable-gpl --enable-libx264 --prefix=/usr/local/cross-ffmpeg --extra-cflags=-I/usr/local/cross-ffmpeg/include --extra-ldflags=-L/usr/local/cross-ffmpeg/lib/ --extra-libs=-ldl
ffmpeg是转码工具,x264是h264格式编解码器,yasm汇编级别的优化。
2.转码有两种方式:直接调用ffmpeg 或者编写code。考虑到转码工具不够灵活、可操作性不好使用code方式,并且mjpeg解码得到yuv422p,h264解码得到yuv420p,中间格式转码无法实现(这个猜想证明是错的,后来测试直接调用ffmepg是可以的,但是代码中yuv422转h264,转码后文件很大,不知道其中的差别在哪里)。
首先考虑直接在网上寻找成熟代码,然而真是没有。。查看ffmpeg 官方demo(/share/ffmpeg/example),使用decode_video.c 以及encode_video.c,但是怎么都运行不起来,总是报错退出(demo中是stream流形式,解码的是mjpeg1,编码的源文件的自己构造的数据。。反正种种不一致,加上自己好多东西不了解。)。参考了这个博客:http://blog.csdn.net/u011913612/article/details/53419986,完成了基本代码。
另外这里要说明的是:v4l2接口采集并保存的文件偏大,vim查看文件发现文件很大一部分为0,看了下采集的demo,发现v4l2接口的大小并不准确,改为通过寻找0xff 0xd9(jpeg结束码)获得数据长度,然后再保存。
二、优化
网络上优化的方法基本上是:编译时enable-yasm,enable-neon,自己实现yuv、rgb格式转换(官方提供的sws_scale效率低)或者在io操作优化。依次尝试后,发现有一定改善,不过cpu转码仍然需要转码好长时间,比如:5s 25fps 640x480分辨率 文件大小为8.5M mjpeg视频,转码后390k,图像质量基本一致,转码时间2min50s。
针对这个现象,网络上方案基本是在转码过程中加sleep,来降低cpu占有率,也有通过cgroup进行资源分配。我采用了降低解码进程的优先级的方式,这样既能提高cpu对其他任务的相应,也能在空闲时,最大化利用cpu。
另一个问题是ffmpeg转码占用了%20内存。。。没有发现内存泄露,也没有发现可以优化的部分,各位童鞋能给个建议吗?
附录:code
#include <math.h>#include <libavutil/opt.h>#include <libavcodec/avcodec.h>#include <libavutil/channel_layout.h>#include <libavutil/common.h>#include <libavutil/imgutils.h>#include <libavutil/mathematics.h>#include <libavutil/samplefmt.h>#include <libavformat/avformat.h>#include <libswscale/swscale.h>#define uinit8_t unsigned char#include <sys/time.h>#include <sys/resource.h>#include <sched.h>#include <sys/types.h>#include <unistd.h>static int video_decode_example(const char *filename,const char *outfilename){av_log_set_level(AV_LOG_ERROR); /* close part of ffmepg prints *//* register all the codecs */av_register_all();FILE* out = fopen(outfilename,"wb");AVFormatContext* pFormatCtx = NULL;//step 1:open file,get format info from file headerif (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0){fprintf(stderr,"avformat_open_input");return;}//step 2:get stread infoif (avformat_find_stream_info(pFormatCtx, NULL) < 0){fprintf(stderr,"avformat_find_stream_info");return; }//just output format info of input fileav_dump_format(pFormatCtx, 0, filename, 0);int videoStream = -1;int i;//step 3:find vido streamfor ( i = 0; i < pFormatCtx->nb_streams; i++){if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){videoStream = i;break;}}if (videoStream == -1){fprintf(stderr,"find video stream error");return;}AVCodecContext* pCodecCtxOrg = NULL;AVCodecContext* pCodecCtx = NULL;AVCodec* pCodec = NULL;AVCodec* enc = avcodec_find_encoder(AV_CODEC_ID_H264);AVCodecContext* enc_ctx = avcodec_alloc_context3(enc);pCodecCtxOrg = pFormatCtx->streams[videoStream]->codec; // codec context //step 4:find decoderpCodec = avcodec_find_decoder(pCodecCtxOrg->codec_id);if (!pCodec){fprintf(stderr,"avcodec_find_decoder error");return;}//step 5:get one instance of AVCodecContext,decode need it.pCodecCtx = avcodec_alloc_context3(pCodec);if (avcodec_copy_context(pCodecCtx, pCodecCtxOrg) != 0){fprintf(stderr,"avcodec_copy_context error");return;}//step 6: open codecif (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){fprintf(stderr,"avcodec_open2 error");return;}AVFrame* pFrame = NULL;AVFrame* pFrameYUV = NULL;pFrame = av_frame_alloc();pFrameYUV = av_frame_alloc();int numBytes = 0;uint8_t* buffer = NULL; enc_ctx->width = pCodecCtx->width;enc_ctx->height = pCodecCtx->height;//enc_ctx->bit_rate = 500000;enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;pCodecCtx->framerate = (AVRational){1,15};pCodecCtx->time_base = (AVRational){1,15};enc_ctx->time_base = pCodecCtx->time_base;enc_ctx->framerate = pCodecCtx->framerate;enc_ctx->gop_size = 12;enc_ctx->max_b_frames = 3;av_opt_set(enc_ctx->priv_data, "preset", "slow", 0);if (avcodec_open2(enc_ctx,enc,NULL)<0){perror("open encodec");return -1;}buffer=(uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)*sizeof(uinit8_t)); avpicture_fill((AVPicture *)pFrameYUV, buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);struct SwsContext* sws_ctx = NULL;AVPacket packet;AVPacket dst_packet;av_init_packet(&dst_packet);dst_packet.data = NULL;dst_packet.size = 0;int cnt0=0;int cnt1=0; i = 0;int frameFinished = 0;//step 7:read framewhile (av_read_frame(pFormatCtx, &packet) >= 0){cnt0++; frameFinished = 0;avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);if (frameFinished){#if 0 // trancode between raw data(yuv420p yuv422p rgb and so on) // using sws_ctx take more time,so do it ourselfsws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);//pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0,pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);#elsememset(pFrameYUV->data[0],'\0',pCodecCtx->width*pCodecCtx->height);memset(pFrameYUV->data[1],'\0',pCodecCtx->width*pCodecCtx->height/4);memset(pFrameYUV->data[2],'\0',pCodecCtx->width*pCodecCtx->height/4);memcpy(pFrameYUV->data[0],pFrame->data[0],pCodecCtx->width*pCodecCtx->height);for(i=0;i<pCodecCtx->height;i++){if(i%2){memcpy(pFrameYUV->data[1]+i/2*pCodecCtx->width/2,pFrame->data[1]+i/2*2*pCodecCtx->width/2,pCodecCtx->width/2);}else{memcpy(pFrameYUV->data[2]+i/2*pCodecCtx->width/2,pFrame->data[2]+i/2*2*pCodecCtx->width/2,pCodecCtx->width/2);}}#endifif(avcodec_encode_video2(enc_ctx,&dst_packet,pFrameYUV,&frameFinished)<0){perror("encode video ");return -1;}if(frameFinished){cnt1++;int ret = fwrite(dst_packet.data,1,dst_packet.size, out);//fflush(out);dst_packet.data = NULL;dst_packet.size = 0;}}}/* get the delayed frames */for ( frameFinished= 1;frameFinished; i++) {//fflush(stdout);if(avcodec_encode_video2(enc_ctx,&dst_packet,NULL,&frameFinished)<0){perror("encode video ");return -1;}if(frameFinished){cnt1++;int ret = fwrite(dst_packet.data,1,dst_packet.size, out);//fflush(out);dst_packet.data = NULL;dst_packet.size = 0;}} //release resourceav_free_packet(&packet);av_free(buffer);av_frame_free(&pFrameYUV);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avcodec_close(pCodecCtxOrg);avformat_close_input(&pFormatCtx);printf("total=%d,ok=%d\n",cnt0,cnt1);fclose(out);}int main(int argc, char **argv){/* set current process priority low to let the app run smooth */if (setpriority(PRIO_PROCESS,getpid(), 19) <0){perror("fail to setpriority");exit(-1);}if (argc < 2) {printf("usage: %s input_file\n""transcode video from mjpeg to h264 to save memory\n""example: ./transcode 3.avi out.h264",argv[0]);return 1;}if(video_decode_example(argv[1], argv[2])<0){printf("transcode fail\n");return -1;}return 0;}
- 基于am335x平台 mjpeg转码h264
- H265/H264/Mjpeg/mpeg
- ffmpeg yuv转h264 (mjpeg)流
- MJPEG
- 嵌入式linux------ffmpeg移植 编码H264(am335x编码H264)
- 嵌入式linux------ffmpeg移植 解码H264(am335x解码H264到yuv420并通过SDL显示)
- 嵌入式linux------ffmpeg移植 解码H264(am335x解码H264到yuv420并通过SDL显示)
- linux 创建initramfs-基于 TI AM335X
- 基于AM335X NAND FLASH 驱动调试总结
- 基于linux 的 AM335X GPIO 调试日志
- 基于AM335X的EDMA 驱动程序开发
- 基于ffmpeg解码h264视频
- 基于JMVC的H264开始-------->
- 基于嵌入式Linux的视频采集系统5--基于MJPEG编码方式的视频采集实现
- 飞凌AM335x平台实时操作系统LinuxRT性能测试
- 基于AM335x的U-Boot/SPL 的CCS 调试
- 基于AM335x的u-bootSPL 的CCS 调试
- 基于AM335x的U-Boot/SPL 的CCS 调试
- 软件测试面试题个人归纳总结
- Swift 4.0 新特性
- TCP协议三次握手与四次握手流程解析
- FTP协议分析
- JavaScript 添加本地图片路径img scr
- 基于am335x平台 mjpeg转码h264
- mybatis 的源码分析
- 算法系列——Add Two Numbers
- Solidworks小技巧
- jsp中计算时间(年月日 时分秒)
- LeetCode 648. Replace Words 字典树练习
- portaudio使用笔记
- recompiled header file is from a previous version of the compiler, or the precompiled header is C++
- 函数名,函数名取地址,函数名取值之间的区别