FFmpeg 开发(10):FFmpeg 视频录制 – 视频添加滤镜和编码

该文章首发于微信群众号:字节活动

FFmpeg 开发系列连载:

FFmpeg 开发(01):FFmpeg 编译和集成

FFmpeg 开发(02):FFmpeg + ANativeWindow 结束视频解码播映

FFmpeg 开发(03):FFmpeg + OpenSLES 结束音频解码播映

FFmpegelementary是什么意思 开发(04):FFmpeg + OpenGLES 结束音频可视化播映

FFmpeg 开发github中文官网(05):FFmpeg + OpenGLE软件工程S 结束视频解码播映和视频滤镜

FFmpeg 开发(06):FFmpeg 播映器结束音视频同步的三种方法

FFmpeg 开发(07):FFmpeg + OpenGLES 结束 3D 全景播映器

FFmpeg 开发(08):FFmpeg 播映器视频烘托优化

FFmpeg 开发(github中文官网09):FFmpeg、x264 以及 fdk-aac 编译整合

音视频开发中,视频编github中文官网码是另一个重要的部分,依据 FFmgithubpeg 软件解码前面系列文章已经介绍过了,接下来首要介绍软件编码这一块,包含视Git频编码、音频编码、为视频增加滤镜等。后期文章安排elementary是什么意思将介绍 Android Medigithubcom1jie1小可爱aCodec 硬件编解码。

前文我们对 x264、fdk-aac 及 FFmpeg 进行了整合编译,本文将Git运用编译好的 FFmpeg 库对 Android Camera2 搜集的预openglskia览帧先进行opengl是什么意思烘托,然后运用 OpenGL 增加滤镜,终究读取烘托作用进行编码,生成 mp4 文件。

FFmpeg 开发(10):FFmpeg 视频录制 - 视频增加滤镜和编码

FFmpeg 视频编码流程

本文依据 Andgiteeroid Camera 2.0 API 搜集的数据源进行编码,编码流程制作是依据 FFmpeg 4.2.2 版别。

FFmpeg 开发(10):FFmpeg 视频录制 - 视频增加滤镜和编码

相对github官网于视频解码,编opengl版别过低码流程多了一些写文件头尾的操作,需求间断编码时,经过刷入空帧来奉告编码器间断编码。

预览帧增加滤镜、编码

FFmpeg 开发(10):FFmpeg 视频录制 - 视频增加滤镜和编码

element什么意思中文 OpenGL ES 系列文章的时分,许多同学说为啥在 NativElemente 层来写 demo ?

其实就github中文官网是为了协作 Fgithub敞开私库Fmpeg 在视频解码和编码时增加滤elementary镜,那么之前在 native 层写的所有关于滤镜的 demo ,现在可以直接拿过来用了。比方相机基础滤镜,相机抖音滤镜这些,

OpenGLCamera2 github.com/gopengl形式ithubhaoha…
这个项目有 30 多种滤镜供你参看。

我们首先经过 An软件库droid Camera2 预览回调获gitlab取预览帧(YUV):

private ImageReader.OnIm软件商铺ageAvai软件技术lableListengitier mOnPreviewImageAvailableListener = new ImageReader.Onopengl版别过低ImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acqugithub敞开私库ireLatestImage();
if (image !软件工程专业= null) {
if (mCamera2FrameCallback != null) {
mCamer软件工程a2FrameCallback.onPreviewFraGitHubme(CameraUtiopengl怎样晋级l.YUV_420_888_data(image), image.getWidth(), image.getHeight());
}
image.close();
}
}
};

之后运用 GLSurfaceView 来主动创建 OpenGL 环境,创建帧缓冲区方针(FBO),FBO 的首要优点便是坚持图像的分辨率不变,然后在giti FBO 离屏烘托时增加滤镜,读取烘托作用作为 FFmpeg 视频编码的输入,终究绑定到 FBOgithub-zookeeper 的纹理再去做屏幕烘托显示出来。

//离屏烘托,增加滤镜
glBindFramebuffer(GL_FRAMEBUFFER, m_DstFboId)github怎样下载文件;
glViewport(0, 0,github永久回家地址 m_RenderImagitige.height, m_RenderImage.width); //相机的宽和高反了,
glClearGitHub(GL_COLOR_BUFFER_BIT);
glUseProgra软件技术m (m_ProgelementsramObj);
glBindVertexArray(m_VaoId);
UpdateMVPMatrix(0, 0, 1.0, 1.0);
GLUtils::setMat4(m_Progragithub敞开私库mObj, "u_MVPMatrix", m_MVPMatrix);
glActivegitlabTexture(GL_TEXTURE软件库0);
glBindTexture(GL_TEXTURE_2D, m_SrcFboTextureId);
G软件LUtils::setInt(m_ProgramObj, "s_texture0", 0);
GLUtils::setIopengl是什么意思nt(m_ProgramObj, "u_nImgType", IMAGE_FORMAT_RGBA);
glDrawElemgithub怎样下载文件ents(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
GetRenderFrameFromFopengl-legacyBO();
glgithub怎样下载文件BindFragithub官网mebuffer(GL_FRAMEBUFFER, 0);
...
//GetRenderFrameFromFBO 读取烘托作用,然后经过回调传入 FFmpeg 编码部队 
void GLCameraRender::GetRenderFragit指令meFromFBO() {
LOGCATE("openglskiaGLCameraRender::GetRenderFrameFromFBO m_RenderFrameCallback=%p", m_RenderFrameCallback);
if(m_RenderFrameCallback != nullptr) {
uint8_t *pBuffer = new uingitlabt8_t[m_RenderImage.width * m_Regithub中文官网nderImage.height * 4]软件;
NativeImage nativelement是什么牌子eImage = m_RenderImage;
nativeImage.format = IMAGE_FORMAT_RGBA;
nativeImage.wgithub敞开私库idth = m_RenderImaggithub中文官网e.height;
nativeImage.height = m_RenderImage.width;
nativeImage.pLineopengles3.1扩展包Size[0] = nativeImage.width * 4element什么意思中文;
nativeImage.ppPlane[0] = pBuffer;
glReadPixels(openglskia0, 0, nativeImage.width, nativeImage.height, GL_RGBA, GgiticomfortL_UNSIGNGitED_BYTE, pBuffer);
m_RenderFrameCallback(m_CallbackContext, &nativeIgithub是干什么的mage);
delete []pBuffer;
}
}

FFmpeg 视频编码结束软件开发

jni StartRecord 传入视elementary是什么意思频的宽、高、码率、帧率等参数,OnPreviewFrame 接口传入预览帧。

extern "C"
JNIEXElementPORT jint JNICALL
Java_com_byteflow_learnffmpeg_media_MediaRecorderContext_native_1StartRecord(JNIEnv *env,
jogithubbject thiz,
jint recordegitir_type,
jstring out_url,
jint frame_width,
jint frame_height,
jlong video_bit_opengl-legacyrate,
jint fps) {
//MediaRecogit指令rderContext 实际上仅仅对 SingleVideoRecorder 简略封装了一下 
const char* url = env-&软件技术专业gt;GetopenglskiaStringUTFChars(out_url, nullptr);
MediaRecorderContext *pContext = MediaRecorderContext::GetContext(env, thiz);
env->ReleaseStrigithub永久回家地址ngUTgithub下载FChars(out_url, url);
if(pContext) retgitlaburn pContext->StartRecord(regithub是干什么的corder_type, url, frame_width, framegitlab_height, vi软件技术专业deo_bit_rate, fps);
return 0;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_byteflow_learnffmpeg_media_MediaRecorderContext_native_1StopRecord(JNIEnv *env,
jobject thiz) {
MediaRecorderContext *pContextGitHub = MediaRecorderContelementui菜鸟教程ext::GetContext(env, thiz);
if(pContext) return pContext->StopRecord(软件技术专业);
return 0;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_bytefl软件工程ow_learnffmpeg_media_MopenglskiaediaRecorderContext_native_1OnPreviewFrame(JNIEnv *env,
jobject topengl烘托gpuhiz,
jint format,
jbyteArray data,
jint width,
jint height) {
int len = env->GetArrayLength (data);GitHub
unsigned char* bopengl怎样晋级uf = new unsigned char[len];
env->GetByteAropengles3.1扩展包rayRegion(data, 0, len, reintergitipret_cast<jgithub官网byte*>(buf));
Mediagit指令RecorderContext *pContext = MediaRecorderContext::GetContext(env, thiz);
if软件技术(pContext) pContext->OnPreviewFrame(format, buf, width, height);
delete[] buf;
}

视频编码器首要便是翻开一个线程,然后不断地从预览帧部队中读取预opengl烘托gpu览帧进行编码,视频编码器结束:

class SingleVideoRecorder {
public:
Selementary schoolinglelementseVideoRecorder软件商铺(const char* outUrl, int frameWidth, int fraopengl形式meHeiGitHubght, long bitRate, int fps);
~SingleVideoRecorder();软件工程专业
int StartRecord();
int OnFram软件开发e2Encode(NativeImage *github官网inputFrame);
int StopRecord();
private:
static void StartH264EncoderThread(软件技术SingleVideoRecorder *context);
int EncodeFrame(AVFrame *pFrame);
private:
ThreadSafeQueue<NativeImage *giti> m_fragithub敞开私库meQueue;
char m_outUrl[1024] = {0};
int m_frameWidth;
int m_frameHeight;
int m_frameIndex = 0;
long m_bitRate;
int m_frameRate;
AVPacket m_avPacket;
AVFrame  *m_pFrame = nullptr;
uint8_t *m_pFrameBuffer =elementary school nullptr;
AVCodecopengles3.1扩展包  *m_pCodec = nullptr;
AVStream *m_pStream软件库 = nullptr;
AVCodecContext *m_pCodecCtx = nullptr;
AVFogithub怎样下载文件rmatContext *m_pFormatCtx = nullptr;
thread *m_encodeelement什么意思中文Thread = nullptr;
SwsContext *github敞开私库m_SwsContext = nullptr;
volatile int m_exit = 0;
};

视频编码循环:

vgithuboid SingleVidegithub官网oRecorder:github中文官网:StartH264EncoderThread(SingleVideoRecogitlabrder *recorder) {
LOGCATE软件技术专业("SingleVideoRe软件工程corder::StartH264EncoderThread start软件工程");
//间断编码且部队为空时退出编码循环
while (!re软件技术corder->m_exit || !recorder->m_frameopengl是什么意思Queue.Empty())opengl-legacy
{
if(record软件技术专业er->m_frameQueue.Empty()) {
//部队为空,休眠等候
usleep(10 * 1000);
continue;
}
//从部队中取一帧预览帧
NativeImagegithubcom1jie1小可爱 *pImage = recorder->m_frameQueue.Pop()opengl怎样晋级;
AVFrame *pFrame = recorder->m_pFrame;
AVPixelFormat srcPopengl三重缓冲ixFmt = AV_PIX_FMT_YUV420P;
switch (pImage->format) {
case IMAGE_FORMAT_RGBA:
srcPixFmt = AV_PIX_FMT_RGBelementary怎样读音A;
break;
case IMAGE_FORMAT_NV21:
srcPixFmgiteet = AV_PIX_FMT_NV2opengl形式1;
break;
case IMAGE_FORMAopengl-legacyT_NV12:
srcPixFmt = AV_P软件开发IX_FMT_NV12;
break;
case IMAGE_FORMAT_I420:
srcPixFmt = AV_PIX_FMT_YUV420P;
break;
default:
LOGCATE("SingleVideoRecogithub中文官网rder::软件工程专业StartH264EncoderThread unsupgithub敞开私库popengl怎样晋级orgithub官网t format pImage->format=%d", pImage->format);
break;
}
if(软件工程srcPixFmgithub永久回家地址t != AV_PIX_FMT_YUV420P) {
if(recorder->m_SwsContext == nullptr) {
recorder->m_SwsContext = sws_getContext(pImage->width, pImage->heelementary schoolight, srcgitiPixFmt,
recorder->m_frameWidth, recorder->m_frameHeight, AV_PIX_FMT_YUV420P,
SWS_FA软件技术ST_BILINEA软件商铺下载R, nullptr, nullptr, nullptr);
}
//格式不同时,需求转化为编码器的方针格github怎样下载文件式 AV_PIX_FMT_YUV420P,转化之后的图像软件应用在 pFrame 
if(recorder-&gopenglskiat;m_SwsContext != nullptr) {
int slice = sws_scale(recorder->m_SwsContext, pImage->ppPlane, pImage->pLineSize, 0,
recorder->m_framelementary schooleopengl是什么意思Height, pFrame->data, pFrame->linesize);
LOGCATE("SingleVideoRecorder::StartH264EncoderThread swsgithub永久回家地址_scale slice=%d", slice);
}
}软件工程专业
//设置 pts
pFgithub中文官网rame-&gtgithub是干什么的;pts = recor软件工程专业der->m_frameIndex++;
//编码一帧
recordeopengl是什么意思r->EncodeFrame(pFrame);
//开释预览帧内存
NativeImageUtil::FreeNativeImage(p软件开发Imagithubcom1jie1小可爱ge);
delete pImage;
}
LOGCATE("SingleVideoRecorder::StartH264EncoderThread end");
}

编码一帧的函数:

int SingleVide软件测验oRecorder::EncodeFrame(软件技术专业AVFrame *pFrame) {
int result = 0;
result = a软件商铺下载vcodec_send_frame(m_pCodecCtx, pFrame);
if(rgithub中文官网esult < 0)
{
LOGCATE("SingleVideoRecorder::EncodeFrame avcodec_s软件end_frgithub敞开私库ame fail. ret=%d", result);
return result;
}
while(!result) {
result = avcodec_receive_packet(m_pCodecCtx, &m_avPacket);
if (resulGitHubt == AVERROR(EAGAIN) || result == AVERROR_EOF) {
return 0;
} el软件技术se if (resugithub怎样下载文件lt < 0) {
LOGCATE("SingleVideoOpenGLRecorder::EncodeFrame avcodec_receive_packet fail. ret=%d", result);
return result;
}
LOGCATE("SingleVideoRecorder::EncodeFrame frame pts=%ld, size=%d"githubcom1jie1小可爱, m_avPacket.pts, m_avPacket.size);
m_avPacket.stream_index = m_pStream->index;
av_elementary是什么意思packeopengl形式t_rescaelement翻译le_ts(&m_avPacket, m_pCodecCtx->time_base, m_pStream->time_base);
m_avPacket.pos = -1github永久回家地址;
av_in软件库terleaved_write_frame(m_pFormatCtx, &m_avPackeelementui菜鸟教程t);
av_packet_unref(&m_avPacket);
}
return 0;element什么意思中文
}

结束代码途径

LearnFFmpeg

技术交流

技术交流/获取视频教程可以增加我的微信:Byte-Flow