当前位置: 首页 > news >正文

ffmpeg在android上输出滑屏问题处理

ffmpeg部分机器上有花屏的问题


原代码例如以下:

while(av_read_frame(formatCtx, &packet)>=0 && !_stop && NULL!=window && bInit) {
		// Is this a packet from the video stream?
		if(packet.stream_index==videoStream) {
			// Decode video frame
			avcodec_decode_video2(codecCtx, decodedFrame, &frameFinished,
			   &packet);
			// Did we get a video frame?
			if(frameFinished) {
				// Convert the image from its native format to RGBA
				sws_scale
				(
					sws_ctx,
					(uint8_t const * const *)decodedFrame->data,
					decodedFrame->linesize,
					0,
					codecCtx->height,
					frameRGBA->data,
					frameRGBA->linesize
				);

				if(packet.dts == AV_NOPTS_VALUE
						 && decodedFrame->opaque && *(uint64_t*)decodedFrame->opaque != AV_NOPTS_VALUE)
				{
					pts = *(uint64_t *)decodedFrame->opaque;
					LOGD("pst1: %d",pts);
				}
				else if(packet.dts != AV_NOPTS_VALUE) {
				  pts = packet.dts;
				  LOGD("pst2: %d",pts);
				} else {
				  pts = 0;
				  LOGD("pst3: %d",pts);
				}
				//pts = av_q2d(codecCtx->time_base) * 1000000.0 * i * 2;
				pts *= 1000;
				//LOGD("debug %d,%d,%f",pts, (long)(av_q2d(codecCtx->time_base) * 1000000.0 * i * 2), av_q2d(codecCtx->time_base));
				if(0 == pts || 0 == baseTime)
				{
					baseTime = av_gettime() - pts;
					LOGD("BASETIME: %d",baseTime);
				}else{
					waitTime = (baseTime + pts) - av_gettime();
					LOGD("WAITTIME: %d, %d",waitTime,pts);
				}

				//waitTime = (av_q2d(codecCtx->time_base) * 1000.0 - 0.0) * 1000;
				if(waitTime>0)
					usleep(waitTime);
				if(!_stop)
				{
					synchronized(lockWindow)
					{
						if(!_stop && NULL!=window)
						{
							// lock the window buffer
							if (ANativeWindow_lock(pWin, &windowBuffer, NULL) < 0) {
								LOGE("cannot lock window");
							} else {
								// draw the frame on buffer
								//LOGD("copy buffer %d:%d:%d", width, height, width*height*RGB_SIZE);
								//LOGD("window buffer: %d:%d:%d", windowBuffer.width, windowBuffer.height, windowBuffer.stride);
								memcpy(windowBuffer.bits, buffer,  width * height * RGB_SIZE);
								// unlock the window buffer and post it to display
								ANativeWindow_unlockAndPost(pWin);
								// count number of frames
								++i;
							}
						}
					}
				}
			}
		}
细致分析后发现 部分分辨率又可以正常展示,感觉是宽度错位导致的,分析例如以下:

ORG: 176  * 144   F
X2:  352 288   O
X3:  528 432   F
X4:  704 576   O
X6:  1056 *   O


X1.1 193 158   F
X1.2 211 172   F
X1.5 264 216   F


X0.5 88 72    F




X2?

: 352 290   O
X2?: 352 600   O
X2?: 352 720   O
X4?: 704 720   O
X6?: 1056 720   O
   


1280 ---1312
        1056
1184
1248 ok

发现分辨率依照%64+32对齐, 感觉是内存对齐造成的, 查看ANativeWindow_Buffer例如以下

typedef struct ANativeWindow_Buffer {
    // The number of pixels that are show horizontally.
    int32_t width;

    // The number of pixels that are shown vertically.
    int32_t height;

    // The number of *pixels* that a line in the buffer takes in
    // memory.  This may be >= width.
    int32_t stride;

    // The format of the buffer.  One of WINDOW_FORMAT_*
    int32_t format;

    // The actual bits.
    void* bits;
    
    // Do not touch.
    uint32_t reserved[6];
} ANativeWindow_Buffer;


输出stride和width的日志发现,假设正常显示则stride==width, 通过凝视能够看出应该是内存对齐问题导致的,调整代码:

if(packet.stream_index==videoStream) {
			// Decode video frame
			avcodec_decode_video2(codecCtx, decodedFrame, &frameFinished,
			   &packet);
			// Did we get a video frame?

if(frameFinished) { // Convert the image from its native format to RGBA sws_scale ( sws_ctx, (uint8_t const * const *)decodedFrame->data, decodedFrame->linesize, 0, codecCtx->height, frameRGBA->data, frameRGBA->linesize ); if(packet.dts == AV_NOPTS_VALUE && decodedFrame->opaque && *(uint64_t*)decodedFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)decodedFrame->opaque; LOGD("pst1: %d",pts); } else if(packet.dts != AV_NOPTS_VALUE) { pts = packet.dts; LOGD("pst2: %d",pts); } else { pts = 0; LOGD("pst3: %d",pts); } //pts = av_q2d(codecCtx->time_base) * 1000000.0 * i * 2; pts *= 1000; //LOGD("debug %d,%d,%f",pts, (long)(av_q2d(codecCtx->time_base) * 1000000.0 * i * 2), av_q2d(codecCtx->time_base)); if(0 == pts || 0 == baseTime) { baseTime = av_gettime() - pts; LOGD("BASETIME: %d",baseTime); }else{ waitTime = (baseTime + pts) - av_gettime(); LOGD("WAITTIME: %d, %d",waitTime,pts); } //waitTime = (av_q2d(codecCtx->time_base) * 1000.0 - 0.0) * 1000; if(waitTime>0) usleep(waitTime); if(!_stop) { synchronized(lockWindow) { if(!_stop && NULL!=window) { // lock the window buffer if (ANativeWindow_lock(pWin, &windowBuffer, NULL) < 0) { LOGE("cannot lock window"); } else { // draw the frame on buffer //LOGD("copy buffer %d:%d:%d", width, height, width*height*RGB_SIZE); //LOGD("window buffer: %d:%d:%d", windowBuffer.width, windowBuffer.height, windowBuffer.stride); //memcpy(windowBuffer.bits, buffer, width * height * RGB_SIZE); if(windowBuffer.width >= windowBuffer.stride){ memcpy(windowBuffer.bits, buffer, width * height * RGB_SIZE); }else{ //skip stride-width 跳过padding部分内存 for(int i=0;i<height;++i) memcpy(windowBuffer.bits + windowBuffer.stride * i * RGB_SIZE , buffer + width * i * RGB_SIZE , width * RGB_SIZE); } // unlock the window buffer and post it to display ANativeWindow_unlockAndPost(pWin); // count number of frames ++i; } } } } } }


通过行拷贝方式,跳过后面对齐部分的内存, 

解决这个问题,


相关文章:

  • 浅谈hibernate、ibatis、myibatis之间的区别?
  • U-Mail邮件系统何以誉为信息整合中转枢纽
  • 编译使用tensorflow c版本动态链接库
  • Curator的使用
  • 开创学习的四核时代-iTOP-4412开发板开源硬件平台
  • nj04---事件回调函数
  • Oracle锁表查询和解锁方法
  • Java 中的事件监听机制
  • sed的使用
  • 第四章 利用函数实现指定的功能
  • Nginx虚拟主机配置实践(一)
  • 硬件设计中,总有一种想法让你冲动
  • Rokid开发者社区skill之【历史上的今天】
  • layer常用方法
  • Cehp-学习1
  • [nginx文档翻译系列] 控制nginx
  • [笔记] php常见简单功能及函数
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • crontab执行失败的多种原因
  • HTTP中GET与POST的区别 99%的错误认识
  • IP路由与转发
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • JavaScript新鲜事·第5期
  • java多线程
  • Making An Indicator With Pure CSS
  • nodejs:开发并发布一个nodejs包
  • Nodejs和JavaWeb协助开发
  • Node项目之评分系统(二)- 数据库设计
  • php中curl和soap方式请求服务超时问题
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • 从tcpdump抓包看TCP/IP协议
  • 动态规划入门(以爬楼梯为例)
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 浅谈Golang中select的用法
  • 如何用vue打造一个移动端音乐播放器
  • 新手搭建网站的主要流程
  • 新书推荐|Windows黑客编程技术详解
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • #### go map 底层结构 ####
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • #前后端分离# 头条发布系统
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (3)STL算法之搜索
  • (C语言)fgets与fputs函数详解
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (四)模仿学习-完成后台管理页面查询
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .net CHARTING图表控件下载地址
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .net core 连接数据库,通过数据库生成Modell