Android FFmpeg + OpenGL ES YUV Player
xsobi 2024-12-25 16:14 1 浏览
1、FFmpeg解出YUV帧数据
1.1 方法介绍
打开封装格式上下文
1)[avformat.h] avformat_open_input 打开媒体流(本地或者网络资源)得到封装格式上下文AVFormatContext AVFormatContext包含了媒体的基本信息,如时长等
初始化解码器
2)[avformat.h] avformat_find_stream_info 从封装格式上下文中找到所有的媒体数据流AVStream,如音频流、视频流、字幕流等 AVStream中则包含了这些数据流的解码信息,如分辨率、采样率、解码器参数、帧率等等
3)[avcodec.h] avcodec_find_decoder 根据AVStream中的解码器id找到对应的解码器AVCodec,FFmpeg可以指定编译编解码器,因此实际编译的FFmpeg包中可能不包含流所对应的解码器;注意此时解码器还未打开,缺少相关的上下文信息
4)[avcodec.h] avcodec_alloc_context3、avcodec_parameters_to_context avcodec_alloc_context3创建一个空的codec上下文结构体AVCodecContext avcodec_parameters_to_context将AVStream中的codec参数信息导入到创建出来的AVCodecContext中
5)[avcodec.h] avcodec_open2 根据解码器上下文中所描述的解码器参数信息打开指定的解码器
6)[avformat.h] av_read_frame 从媒体上下文中读取一个数据包,这是一个压缩后的数据包AVPacket,需要经过解码器解码才能得到原始的媒体数据
解码
6)[avcodec.h] avcodec_send_packet 将av_read_frame读取到的数据压缩包送入解码器进行解码
7)[avcodec.h] avcodec_receive_frame 从解码去读取一帧媒体原始数据,注意(6)、(7)并不是一一对应的调用关系
重裁剪
8)[swscale.h] sws_getContext 由于我们送显时一般都是固定像素格式或者分辨率的,因此需要对解码出的视频原始数据进行重裁剪,将分辨率或者像素格式统一,这个方法主要是初始化重裁剪相关的上下文
9)[swscale.h] sws_scale 将(7)中的AVFrame视频原始数据进行重裁剪
2、Demo实现
2.1 流程简介
C++音视频学习资料免费获取方法:关注音视频开发T哥,点击「链接」即可免费获取2023年最新C++音视频开发进阶独家免费学习大礼包!
2.2 代码示例
1)打开封装格式上下文
// 1、创建1个空的上下文对象
AVFormatContext* formatCtx = avformat_alloc_context();
// 2、打开指定资源地址的封装格式上下文
avformat_open_input(&formatCtx, sourcePath.c_str(), nullptr, nullptr);
2)找到视频流
// 3、从封装格式中解析出所有的流信息
avformat_find_stream_info(formatCtx, nullptr);
// 4、遍历流找到视频流
AVStream *videoStream = nullptr;
for (int i = 0; i < formatCtx->nb_streams; i++) {
if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = formatCtx->streams[i];
videoStreamIndex = i;
}
}
3)根据流信息打开对应的解码器
// 5、找到解码器,这个时候解码器对象还没有打开,不能用
AVCodec *codec = avcodec_find_decoder(videoStream->codecpar->codec_id);
// 6、为该codec创建1个对应的空上下文
AVCodecContext* codecCtx = avcodec_alloc_context3(codec);
// 7、将指定流中限定的解码器参数输入解码器上下文中
avcodec_parameters_to_context(codecCtx, videoStream->codecpar);
// 8、通过解码器上下文打开解码器
avcodec_open2(codecCtx, codec, NULL);
// 以下为视频相关的信息
// 帧率
// 优先通过 avg_frame_rate 计算帧率,若 avg_frame_rate 为{x, 0} or {0, 1} 那么就使用 r_frame_rate
// av_q2d 就是 avg_frame_rate.num(分子) / avg_frame_rate.den(分母)
if (stream->avg_frame_rate.den == 0 ||
(stream->avg_frame_rate.num == 0 && stream->avg_frame_rate.den == 1)) {
frameRate = av_q2d(stream->r_frame_rate);
} else {
frameRate = av_q2d(stream->avg_frame_rate);
}
// 每一帧的时间(毫秒)
frameMillions = 1000 / frameRate;
4)创建重采样上下文
swsContext = sws_getContext(
decoder->Width(), // 输入源宽度
decoder->Height(), // 输入源高度
decoder->PixelFormat(), // 输入源格式 YUV42 NV12 NV32...
decoder->Width(), // 输出源宽度
decoder->Height(), // 输出源高度
AV_PIX_FMT_YUV420P, // 输出源格式
SWS_BICUBIC, nullptr, nullptr, nullptr);
if (!swsContext) {
return false;
}
// 分配重采样的输出Frame
swsAVFrame = av_frame_alloc();
swsAVFrame->width = decoder->Width();
swsAVFrame->height = decoder->Height();
swsAVFrame->format = AV_PIX_FMT_YUV420P;
// 按照1字节对齐,给AVFrame创建缓冲区,重采样的数据送入此swsAVFrame中
int ret = av_frame_get_buffer(swsAVFrame, 1);
5)从AVFormatContext中读取数据包,放入到缓存队列中
while (1) {
AVPacket *packet = av_packet_alloc();
int ret = av_read_frame(formatCtx, packet);
if (ret == AVERROR_EOF) {
break;
} else if (ret) {
LOGE("%s av_read_frame error", __func__);
av_packet_free(&packet);
av_free(packet);
packet = nullptr;
break;
}
// 读取到一个视频数据包,方璐packet队列中
if (packet->stream_index == videoStreamIndex) {
packetQueue->PushPacket(packet);
}
}
6)从缓存队列中取出AVPacket,送入解码器解码
AVFrame * Decode() {
// 先从缓存中获取,avcodec_receive_frame 与 avcodec_send_packet 不是一一对应的
AVFrame *avFrame = av_frame_alloc();
if (avcodec_receive_frame(codecCtx, av_frame) == 0) {
return avFrame;
}
while (1) {
AVPacket *packet = packetQueue->GetTopOne();
if (!packet) {
av_frame_free(&avFrame);
av_free(avFrame);
avFrame = NULL;
break;
}
// 向解码器发送压缩数据包
int ret = avcodec_send_packet(codecCtx, packet);
if (ret) {
char buffer[128];
av_make_error_string(buffer, 128, ret);
LOGE("%s avcodec_send_packet error %s", __func__, buffer);
av_frame_free(&avFrame);
av_free(avFrame);
avFrame = NULL;
av_packet_free(&packet);
av_free(packet);
break;
}
// 从解码器获取解压缩后的原始包
int code = avcodec_receive_frame(codecCtx, avFrame);
av_packet_free(&packet);
av_free(packet);
if (code == 0) {
break;
} else if (code == AVERROR_EOF) {
av_frame_free(&avFrame);
av_free(avFrame);
avFrame = NULL;
break;
}
}
return avFrame;
}
7)对解出的AVFrame进行重裁剪
sws_scale(swsContext, avFrame->data, avFrame->linesize, 0,
avFrame->height, swsAVFrame->data, swsAVFrame->linesize);
8)将重采样后的AVFrame数据送入OpenGL显示
// 送显示速度控制,按照视频的帧率进行控制,确保流畅播放
long currentMillions = base::CurrentMillions();
long diff = currentMillions - lastMillions;
if (diff >= frameMillions) {
lastMillions = currentMillions;
} else {
long sleepMillions = frameMillions - diff;
sleepMillions = sleepMillions > frameMillions ? frameMillions : sleepMillions;
usleep(sleepMillions * 1000); // usleep 时微秒 1ms = 1000微秒
lastMillions = currentMillions + sleepMillions;
}
// 送入OpenGL显示
(*functor)(swsAVFrame->width, swsAVFrame->height, swsAVFrame->data[0],
swsAVFrame->data[1], swsAVFrame->data[2]);
相关推荐
- 斗鱼针针成旻云个人资料 针针年龄身高演艺经历介绍
-
[闽南网]针针成旻云曾是七煌旗下签约艺人,经常在斗鱼进行直播身高超过170cm的她更因为有一双大长腿而被称为“斗鱼第一美腿”、“电竞第一腿”。本文这就来对针针的个人资料进行详细介绍,想知道她的年龄身高...
- 轻量级RTSP服务模块和RTSP推流模块适用场景区别
-
好多开发者一直搞不清轻量级RTSP服务SDK和RTSP推流SDK的区别,以下是相关区别:1.轻量级RTSP服务模块:轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服...
- 《新·奥特曼》11月18日国内视频平台上线
-
《新·奥特曼》海报。新京报讯11月14日,由上海新创华文化发展有限公司授权引进电影《新·奥特曼》宣布正式定档11月18日(周五)00:00上线视频平台,上线版本为日语配音中文字幕版。影片由庵野秀明(...
- 剑指Apple Watch!Android Wear也将支持视频功能
-
想必智能手表发展到现在,大家最期待的还是视频功能,近日AndroidWear就实现了这一功能,以后就能在手表上看视频了,想想就挺激动的,快来看看吧!其实早在WWDC大会上,老对手AppleWatc...
- QT应用编程:基于VLC开发音视频播放器(句柄方式)
-
一、环境介绍操作系统:win1064位QT版本:QT5.12.6编译器:MinGW32VLC版本:...
- OBS 源码分析 obs开发
-
本文将按照数据源的获取、渲染、推送的直播流程来让大家深入了解一下。1、直播源数据获取obs在启动时会优先加载libobs核心库,这个库初始化obs很多内容,包括crash模块、com、性能监...
- Android和iOS端Moments更新:支持视频分享功能
-
Moments是社交网络巨头Facebook推出的一款私人照片分享应用,今天公司宣布对Android端和iOS端应用同时升级,新增对视频分享功能的支持。事实上早在数周之前,Facebook就曾表示Mo...
- 您很快就可以在Android Galaxy设备之间传输视频通话
-
在阅读此文之前,辛苦点击右上角的“关注”,既方便您进行讨论与分享,又能给您带来不一样的参与感,感谢您的支持!导语:在科技领域,每时每刻都有新的发展,令人兴奋的创新不断涌现。早在八月份,Android系...
- 一篇文章带你FFmpeg到流媒体服务器开发
-
安装ffmpeg:下载FFmpeg和libx264的包ffmpeg-2.4.1.tar.bz2last_x264.tar.bz2libx264需要yasm,所以先安装yasmapt-getinst...
- YouTube 为 Android 平台提供 1440P 视频
-
安锋网8月10日消息,Android从起初的480P的屏幕分辨率发展到2014年的1440P花了将近六年的时间,一般认为1080P的屏幕分辨率已经是人眼可以识别的极限,但是...
- FFmpeg 调用 Android MediaCodec 进行硬解码(附源码)
-
FFmpeg在3.1版本之后支持调用平台硬件进行解码,也就是说可以通过FFmpeg的C代码去调用Android上的MediaCodec了。在官网上有对应说明,地址如下:trac....
- Android FFmpeg + OpenGL ES YUV Player
-
1、FFmpeg解出YUV帧数据1.1方法介绍打开封装格式上下文...
- 基于WebRTC的Android移动端无线视频传输
-
摘要:视频传输技术在现代社会广泛应用,人们对其的要求也越来越高,其发展的趋势是方便、快捷、随时随地。传统的视频传输过于依赖线路,线路的走向限制了传输的很多可能,所以无线传输才是发展的方向。本文提出...
- 使用python爬取抖音app视频 python爬取抖音视频数据
-
记录一下如何用python爬取app数据,本文以爬取抖音视频app为例。编程工具:pycharm...
- Android IOS WebRTC 音视频开发总结(七七)-- WebRTC的架构和协议栈
-
本文主要介绍WebRTC的架构和协议栈(我们翻译和整理的,译者:litie),最早发表在【编风网】为了便于理解,我们来看一个最基本的三角形WebRTC架构(见下图)。在这个架构中,移动电话用“浏览器M...
- 一周热门
- 最近发表
- 标签列表
-
- grid 设置 (58)
- 移位运算 (48)
- not specified (45)
- patch补丁 (31)
- strcat (25)
- 导航栏 (58)
- context xml (46)
- scroll (43)
- element style (30)
- dedecms模版 (53)
- vs打不开 (29)
- nmap (30)
- c 视频教程下载 (33)
- paddleocr (28)
- listview排序 (33)
- firebug 使用 (31)
- transactionmanager (30)
- characterencodingfilter (33)
- getmonth (34)
- commandtimeout (30)
- hibernate教程 (31)
- label换行 (33)
- curlpost (31)
- android studio 3 0 (34)
- android开发视频 (30)