版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/byhook/article/details/84648755
音视频实践学习
- android全平台编译ffmpeg以及x264与fdk-aac实践
- ubuntu下使用nginx和nginx-rtmp-module配置直播推流服务器
- android全平台编译ffmpeg合并为单个库实践
- android-studio使用cmake编译ffmpeg实践
- android全平台下基于ffmpeg解码MP4视频文件为YUV文件
- android全平台编译ffmpeg支持命令行实践
- android全平台基于ffmpeg解码本地MP4视频推流到RTMP服务器
- android平台下音频编码之编译LAME库转码PCM为MP3
- ubuntu平台下编译vlc-android视频播放器实践
- 图解YU12、I420、YV12、NV12、NV21、YUV420P、YUV420SP、YUV422P、YUV444P的区别
- 图解RGB565、RGB555、RGB16、RGB24、RGB32、ARGB32等格式的区别
- YUV420P、YUV420SP、NV12、NV21和RGB互相转换并存储为JPEG以及PNG图片
- android全平台编译libyuv库实现YUV和RGB的转换
- android平台下基于ffmpeg对相机采集的NV21数据编码为MP4视频文件
- android平台下基于ffmpeg采集Camera数据编码成H.264推流到RTMP服务器
- android平台下基于ffmpeg和ANativeWindow实现简单的视频播放器
概述
本节内容旨在了解本地视频的解码播放过程
,暂时不设计音频的解码同步播放
操作。
流程分析
工程实践
新建工程ffmpeg-single-play
定义java层的播放类:
package com.onzhou.ffmpeg.player;
public class NativePlayer {
static {
System.loadLibrary("native-player");
}
public native int playVideo(String videoPath, Object surface);
}
定义好本地的播放器头文件:native_play.h
class NativePlayer {
private:
int width = 0;
int height = 0;
int bufferSize = 0;
int videoIndex = -1;
AVPacket *vPacket = NULL;
AVFrame *vFrame = NULL, *pFrameRGBA = NULL;
AVCodecContext *vCodecCtx = NULL;
SwsContext *sws_ctx = NULL;
AVFormatContext *pFormatCtx = NULL;
uint8_t *out_buffer = NULL;
ANativeWindow_Buffer windowBuffer;
AVCodec *vCodec = NULL;
public:
int PlayVideo(const char *input_str, ANativeWindow *nativeWindow);
};
定义相关的实现类文件:native_play.cpp
int NativePlayer::PlayVideo(const char *input_str, ANativeWindow *nativeWindow) {
//1.初始化所有组件
av_register_all();
//分配一个AVFormatContext结构
pFormatCtx = avformat_alloc_context();
//2.打开输入文件
if (avformat_open_input(&pFormatCtx, input_str, NULL, NULL) != 0) {
LOGE("Could not open input stream");
goto end_line;
}
//3.查找文件的流信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
LOGE("Could not find stream information");
goto end_line;
}
//4.查找视频轨
for (int index = 0; index < pFormatCtx->nb_streams; index++) {
if (pFormatCtx->streams[index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoIndex = index;
break;
}
}
if (videoIndex == -1) {
LOGE("Could not find a video stream");
goto end_line;
}
//5.查找解码器
vCodec = avcodec_find_decoder(pFormatCtx->streams[videoIndex]->codecpar->codec_id);
if (vCodec == NULL) {
LOGE("could not find codec");
goto end_line;
}
//6.配置解码器
vCodecCtx = avcodec_alloc_context3(vCodec);
avcodec_parameters_to_context(vCodecCtx, pFormatCtx->streams[videoIndex]->codecpar);
//7.打开解码器
if (avcodec_open2(vCodecCtx, vCodec, NULL) < 0) {
LOGE("Could not open codec");
goto end_line;
}
width = vCodecCtx->width;
height = vCodecCtx->height;
//分配一个帧指针,指向解码后的原始帧
vFrame = av_frame_alloc();
vPacket = (AVPacket *) av_malloc(sizeof(AVPacket));
pFrameRGBA = av_frame_alloc();
//绑定输出buffer
bufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 1);
out_buffer = (uint8_t *) av_malloc(bufferSize * sizeof(uint8_t));
av_image_fill_arrays(pFrameRGBA->data, pFrameRGBA->linesize, out_buffer, AV_PIX_FMT_RGBA,
width, height, 1);
sws_ctx = sws_getContext(width, height, vCodecCtx->pix_fmt,
width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL);
//默认为RGB565
if (ANativeWindow_setBuffersGeometry(nativeWindow, width, height, WINDOW_FORMAT_RGBA_8888) <
0) {
LOGE("Could not set buffers geometry");
ANativeWindow_release(nativeWindow);
goto end_line;
}
//读取帧
while (av_read_frame(pFormatCtx, vPacket) >= 0) {
if (vPacket->stream_index == videoIndex) {
//视频解码
if (avcodec_send_packet(vCodecCtx, vPacket) != 0) {
return -1;
}
while (avcodec_receive_frame(vCodecCtx, vFrame) == 0) {
//转化格式
sws_scale(sws_ctx, (const uint8_t *const *) vFrame->data, vFrame->linesize,
0,
vCodecCtx->height,
pFrameRGBA->data, pFrameRGBA->linesize);
if (ANativeWindow_lock(nativeWindow, &windowBuffer, NULL) < 0) {
LOGE("cannot lock window");
} else {
//逐行复制
uint8_t *bufferBits = (uint8_t *) windowBuffer.bits;
for (int h = 0; h < height; h++) {
memcpy(bufferBits + h * windowBuffer.stride * 4,
out_buffer + h * pFrameRGBA->linesize[0],
pFrameRGBA->linesize[0]);
}
ANativeWindow_unlockAndPost(nativeWindow);
}
}
}
av_packet_unref(vPacket);
}
//释放内存
sws_freeContext(sws_ctx);
end_line:
av_free(vPacket);
av_free(pFrameRGBA);
avcodec_close(vCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
最终播放效果如下:
项目地址:ffmpeg-single-play
https://github.com/byhook/ffmpeg4android