音视频实践学习
- 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实现简单的视频播放器
- android平台下基于ffmpeg实现对相机预览截图的功能(NV21数据编码为JPEG文件)
- android平台下基于ffmpeg的swscale模块实现对YUV和RGB数据进行转换
概述
其实如果对YUV和RGB数据
进行转换,更推荐使用libyuv库
进行转换,效率更高一些,今天记录一下ffmpeg库
自带的swscale模块
进行转码工作。
环境配置
操作系统:ubuntu 16.05
ndk版本:android-ndk-r16b
ffmpeg版本:ffmpeg-3.3.8
流程分析
工程实践
核心的转码实现:Transform
#include "native_swscale.h"
#include "logger.h"int NativeSwscale::Transform(const char *videoPath, const char *outputPath) {av_register_all();pFormatCtx &#61; avformat_alloc_context();if (avformat_open_input(&pFormatCtx, videoPath, NULL, NULL) !&#61; 0) {LOGE("Could not open input stream");goto end_line;}if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {LOGE("Could not find stream information");goto end_line;}for (int i &#61; 0; i < pFormatCtx->nb_streams; i&#43;&#43;)if (pFormatCtx->streams[i]->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_VIDEO) {videoIndex &#61; i;break;}if (videoIndex &#61;&#61; -1) {LOGE("Could not find a video stream");goto end_line;}pCodec &#61; avcodec_find_decoder(pFormatCtx->streams[videoIndex]->codecpar->codec_id);if (pCodec &#61;&#61; NULL) {LOGE("Could not find Codec");goto end_line;}pCodecCtx &#61; avcodec_alloc_context3(pCodec);avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoIndex]->codecpar);pCodecCtx->thread_count &#61; 1;if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {LOGE("Could not open codec");goto end_line;}pFrame &#61; av_frame_alloc();vPacket &#61; (AVPacket *) av_malloc(sizeof(AVPacket));pFrameYUV &#61; av_frame_alloc();bufferSize &#61; av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,1);out_buffer &#61; (uint8_t *) av_malloc(bufferSize * sizeof(uint8_t));av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer, AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height, 1);sws_ctx &#61; sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_YUV420P, SWS_BICUBIC,NULL, NULL, NULL);outputFile &#61; fopen(outputPath, "wb&#43;");while (av_read_frame(pFormatCtx, vPacket) >&#61; 0) {if (vPacket->stream_index &#61;&#61; videoIndex) {if (avcodec_send_packet(pCodecCtx, vPacket) !&#61; 0) {return -1;}while (avcodec_receive_frame(pCodecCtx, pFrame) &#61;&#61; 0) {sws_scale(sws_ctx, (const uint8_t *const *) pFrame->data, pFrame->linesize,0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);fwrite(pFrameYUV->data[0], (pCodecCtx->width) * (pCodecCtx->height), 1, outputFile);fwrite(pFrameYUV->data[1], (pCodecCtx->width) * (pCodecCtx->height) / 4, 1,outputFile);fwrite(pFrameYUV->data[2], (pCodecCtx->width) * (pCodecCtx->height) / 4, 1,outputFile);}}av_packet_unref(vPacket);}if (outputFile !&#61; NULL) {fclose(outputFile);outputFile &#61; NULL;}sws_freeContext(sws_ctx);end_line:avcodec_close(pCodecCtx);avcodec_free_context(&pCodecCtx);av_free(pFrame);av_free(pFrameYUV);avformat_close_input(&pFormatCtx);avformat_free_context(pFormatCtx);return 0;
}
最终输出一个output.yuv文件
&#xff0c;同步到本地电脑上&#xff0c;使用ffplay可以直接播放&#xff1a;
ffplay -f rawvideo -video_size 480x960 output.yuv
项目地址&#xff1a;ffmpeg-single-swscale
https://github.com/byhook/ffmpeg4android
参考&#xff1a;
https://blog.csdn.net/leixiaohua1020/article/details/14215391