1. 注册所有容器格式和CODEC:av_register_all()
2. 打开文件:av_open_input_file()
3. 从文件中提取流信息:av_find_stream_info()
4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO
5. 查找对应的解码器:avcodec_find_decoder()
6. 打开编解码器:avcodec_open()
7. 为解码帧分配内存:avcodec_alloc_frame()
8. 不停地从码流中提取出帧数据:av_read_frame()
9. 判断帧的类型,对于视频帧调用:avcodec_decode_video()
10. 解码完后,释放解码器:avcodec_close()
11. 关闭输入文件:av_close_input_file()

FfmpegEncoder.h

/*
* FfmpegEncoder.h
*
* Current, Can Support YUV422sp encoder and decoder
*
* Created on: Dec 5, 2010
* Author: Henry.Wen
*/
#ifndef _H264ENCODER_H
#define _H264ENCODER_H void save_image(const char* filePath, const void* bufferBase, int width, int height); int encoder_init(const char* filePath, int width, int height); int encoder_frame(const void* frame); int encoder_frame_yuv422(const void* frame); void encoder_close(); #endif

 FfmpegEncoder.cpp

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> extern "C"
{
#include <libavutil/mathematics.h>
#include <libavformat/avformat.h>
//#include <libswscale/swscale.h>
} #include <skia/core/SkBitmap.h>
#include <skia/images/SkImageEncoder.h> #include <android_runtime/AndroidRuntime.h>
#include "FfmpegEncoder.h" AVOutputFormat *g_fmt = 0;
AVFormatContext *g_oc = 0;
AVCodec *g_video_codec = 0;
AVStream *g_video_st = 0;
AVFrame *g_frame = 0;
AVPicture g_picture; int g_frame_count = 0;
double g_video_pts = 0;
int g_flagInit = 0;
int g_width = 0;
int g_height = 0; using namespace android;
static Mutex sg_mutexLock; #ifndef LOGI
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "H264ENCODE", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "H264ENCODE", __VA_ARGS__))
#endif void save_image(const char* filePath, const void* bufferBase, int width, int height)
{
Mutex::Autolock lock(sg_mutexLock);
SkBitmap b;
#if (ANDROID_r4_4_0)
b.setConfig(SkBitmap::kARGB_8888_Config, width, height,(size_t)0);
#else
b.setConfig(SkBitmap::kARGB_8888_Config, width, height);
#endif b.setPixels((void*)bufferBase);
SkImageEncoder::EncodeFile(filePath, b, SkImageEncoder::kJPEG_Type, SkImageEncoder::kDefaultQuality); LOGI("save_image image ok====================");
} AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id, int width, int height)
{
AVCodecContext *c;
AVStream *st; /* find the encoder */
*codec = avcodec_find_encoder(codec_id);
LOGI("encoder_init add_stream find encoder='%s'", avcodec_get_name(codec_id));
if (!(*codec))
{
LOGE("encoder_init add_stream could not find encoder for '%s'\n", avcodec_get_name(codec_id));
return 0;
} st = avformat_new_stream(oc, *codec);
if (!st)
{
LOGE("encoder_init add_stream could not allocate stream");
return 0;
}
st->id = oc->nb_streams-1;
c = st->codec; avcodec_get_context_defaults3(c, *codec);
c->codec_id = codec_id;//AV_CODEC_ID_MPEG4; c->bit_rate = 40000;
/* Resolution must be a multiple of two. */
c->width = width;
c->height = height;
c->time_base.den = 25;
c->time_base.num = 1;
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
c->pix_fmt = AV_PIX_FMT_YUV420P; if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
{
c->max_b_frames = 2;
} if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
{
c->mb_decision = 2;
} /* Some formats want stream headers to be separate. */
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER; if(!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp"))
c->flags |= CODEC_FLAG_GLOBAL_HEADER; return st;
} AVFrame* alloc_picture(AVPixelFormat pix_fmt, int width, int height)
{
AVFrame *picture;
uint8_t *picture_buf;
int size; picture = avcodec_alloc_frame();
if (!picture)
return NULL; size = avpicture_get_size(pix_fmt, width, height);
picture_buf = (uint8_t *)av_malloc(size);
if (!picture_buf)
{
av_free(picture);
return NULL;
}
avpicture_fill((AVPicture *)picture, picture_buf, pix_fmt, width, height);
return picture;
} int open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
int ret = 0;
AVCodecContext *c = st->codec; /* open the codec */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0)
{
LOGE("encoder_init open_video could not open video codec: %s", av_err2str(ret));
return -1;
} g_frame = avcodec_alloc_frame();
if (!g_frame)
{
LOGE("encoder_init open_video could not allocate video frame");
return -1;
} ret = avpicture_alloc(&g_picture, c->pix_fmt, c->width, c->height);
if (ret < 0)
{
LOGE("encoder_init open_video could not allocate picture: %s", av_err2str(ret));
free(g_frame);
g_frame = 0; return -1;
}
*((AVPicture *)g_frame) = g_picture; return 0;
} void close_video(AVStream *st)
{
if(st->codec)
{
avcodec_close(st->codec);
st->codec = 0;
} if(g_frame)
{
av_free(g_picture.data[0]);
av_free(g_frame);
g_frame = 0;
}
} int encoder_init(const char* filePath, int width, int height)
{
if(g_flagInit)
return 0; Mutex::Autolock lock(sg_mutexLock); int ret = 0;
LOGI("encoder_init ============begin"); if(!filePath || width <= 0 || height <= 0)
{
LOGE("encoder_init input parameters error ret = %d", (ret = -1));
return -1;
}
av_register_all(); /* allocate the output media context */
avformat_alloc_output_context2(&g_oc, NULL, NULL, filePath); if (!g_oc)
{
LOGI("Could not deduce output format from file extension: using MPEG.");
return -1;
} g_fmt = g_oc->oformat; g_video_st = add_stream(g_oc, &g_video_codec, g_fmt->video_codec, width, height); av_dump_format(g_oc, 0, filePath, 1); if (g_video_st)
{
if(open_video(g_oc, g_video_codec, g_video_st) < 0)
{
LOGE("encoder_init open_video fail!");
close_video(g_video_st);
av_free(g_oc);
g_oc = 0; return -1;
}
}
else
{
LOGE("encoder_init g_video_st is null, not enough memory!");
av_free(g_oc);
g_oc = 0; return -1;
} /* open the output file, if needed */
if (!(g_fmt->flags & AVFMT_NOFILE))
{
LOGI("encoder_init avio_open ============begin");
ret = avio_open(&g_oc->pb, filePath, AVIO_FLAG_WRITE);
LOGI("encoder_init avio_open ret:%d============end", ret);
if (ret < 0)
{
LOGE("encoder_init could not open '%s': %s", filePath, av_err2str(ret));
close_video(g_video_st);
av_free(g_oc);
g_oc = 0;
return -1;
}
}
LOGI("encoder_init avformat_write_header video file");
ret = avformat_write_header(g_oc, NULL); if (ret < 0)
{
LOGE("encoder_init error occurred when opening output file: %s\n", av_err2str(ret));
close_video(g_video_st);
av_free(g_oc);
g_oc = 0; return -1;
} if (g_frame)
g_frame->pts = 0; g_flagInit = 1;
g_width = width;
g_height = height; LOGI("encoder_init ============end");
return 0;
}
static struct SwsContext *swsContext;
void fill_yuv_image(AVFrame *pict, const void* frame)
{
int x, y, tmpIndex = 0, tmpWdith = g_width >> 1, tmpHeight = g_height >> 1;
unsigned char* tmpBuffer_yuv = (unsigned char*)frame;
/* Y */
for (y = 0; y < g_height; ++y)
{
for (x = 0; x < g_width; ++x)
{
pict->data[0][y * pict->linesize[0] + x] = *(tmpBuffer_yuv + tmpIndex);
++tmpIndex;
}
} tmpIndex = 0;
int tmpLength = g_width * g_height;
unsigned char* tmpBuffer_uv = tmpBuffer_yuv + tmpLength;
/* Cb and Cr */
for (y = 0; y < tmpHeight; ++y)
{
for (x = 0; x < tmpWdith; ++x)
{
pict->data[1][y * pict->linesize[1] + x] = *(tmpBuffer_uv + tmpIndex + 1);
pict->data[2][y * pict->linesize[2] + x] = *(tmpBuffer_uv + tmpIndex);
tmpIndex+= 2;
}
} } //fill AVFrame with YUV422p buffer
void fill_yuv422p_image(AVFrame *pict, const void* frameYUV422p)
{
int width = g_width, height = g_height;
unsigned char * pyuv422 = (unsigned char *)frameYUV422p;
unsigned char * pyuv420y = &pict->data[0][0];
unsigned char * pyuv420u = &pict->data[1][0];
unsigned char * pyuv420v = &pict->data[2][0];
int uv_count = 0;
int i, j;
for (i = 0; i < height; i += 2)
for (j = 0; j < width; j += 2) { memcpy(pyuv420y + i * width + j, pyuv422 + i * width * 2 + j * 2,
1);
memcpy(pyuv420y + (i + 1) * width + j,
pyuv422 + (i + 1) * width * 2 + j * 2, 1);
memcpy(pyuv420y + i * width + (j + 1),
pyuv422 + i * width * 2 + (j + 1) * 2, 1);
memcpy(pyuv420y + (i + 1) * width + (j + 1),
pyuv422 + (i + 1) * width * 2 + (j + 1) * 2, 1);
//±£ÁôU ·ÖÁ¿
memcpy(pyuv420u + uv_count, pyuv422 + i * width * 2 + j * 2 + 1, 1);
//±£ÁôV·ÖÁ¿;
memcpy(pyuv420v + uv_count,
pyuv422 + (i + 1) * width * 2 + (j + 1) * 2 + 1, 1);
uv_count++; } /*int x, y, tmpIndex = 0, tmpWdith = g_width >> 1, tmpHeight = g_height >> 1;
unsigned char* tmpBuffer_yuv = (unsigned char*)frameYUV422p; /* Y */
/*for (y = 0; y < g_height; ++y)
{
for (x = 0; x < g_width; ++x)
{
pict->data[0][y * pict->linesize[0] + x] = *(tmpBuffer_yuv + tmpIndex);
++tmpIndex;
}
} tmpIndex = 0;
int tmpLength = g_width * g_height;
unsigned char* tmpBuffer_uv = tmpBuffer_yuv + tmpLength; // Cb and Cr
for (y = 0; y < tmpHeight; ++y)
{
for (x = 0; x < tmpWdith; ++x)
{
pict->data[1][y * pict->linesize[1] + x] = *(tmpBuffer_uv + tmpIndex);
pict->data[2][y * pict->linesize[2] + x] = *(tmpBuffer_uv + tmpIndex +1);
tmpIndex += 2;
}
tmpIndex += g_width;
}*/
} void write_video_frame(AVFormatContext *oc, AVStream *st)
{
int ret;
static struct SwsContext *sws_ctx;
AVCodecContext *c = st->codec; /* encode the image */
AVPacket pkt;
int got_output; av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0; ret = avcodec_encode_video2(c, &pkt, g_frame, &got_output);
if (ret < 0)
{
LOGE("encoder_init error encoding video frame: %s\n", av_err2str(ret));
return;
} //If size is zero, it means the image was buffered.
if (got_output)
{
if (c->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = st->index;
ret = av_interleaved_write_frame(oc, &pkt);
}
else
{
ret = 0;
}
av_free_packet(&pkt); if (ret != 0)
{
LOGE("encoder_init error while writing video frame: %s\n", av_err2str(ret));
return;
}
++g_frame_count = 0;
} int encoder_frame(const void* frame)
{
if(!g_flagInit)
return 0; Mutex::Autolock lock(sg_mutexLock); fill_yuv_image(g_frame, frame); if (g_video_st)
g_video_pts = (double)g_video_st->pts.val * g_video_st->time_base.num / g_video_st->time_base.den;
else
g_video_pts = 0.0; write_video_frame(g_oc, g_video_st);
g_frame->pts += av_rescale_q(1, g_video_st->codec->time_base, g_video_st->time_base); return 0;
} int encoder_frame_yuv422(const void* frame)
{
if(!g_flagInit)
return 0; Mutex::Autolock lock(sg_mutexLock); fill_yuv422p_image(g_frame, frame); if (g_video_st)
g_video_pts = (double)g_video_st->pts.val * g_video_st->time_base.num / g_video_st->time_base.den;
else
g_video_pts = 0.0; write_video_frame(g_oc, g_video_st);
g_frame->pts += av_rescale_q(1, g_video_st->codec->time_base, g_video_st->time_base); return 0;
} void encoder_close()
{
LOGI("encoder_close ============begin");
Mutex::Autolock lock(sg_mutexLock);
if(g_oc)
{
av_write_trailer(g_oc); if (g_video_st)
close_video(g_video_st); for(int i = 0; i < (int)g_oc->nb_streams; ++i)
{
av_freep(&g_oc->streams[i]->codec);
av_freep(&g_oc->streams[i]);
} if (!(g_fmt->flags & AVFMT_NOFILE))
avio_close(g_oc->pb); av_free(g_oc);
} g_oc = 0;
g_video_st = 0;
g_flagInit = 0;
g_frame_count = 0;
g_width = 0;
g_height = 0;
LOGI("encoder_close ============end");
}

  首先第一件事情就是开一个视频文件并从中得到流。我们要做的第一件事情就是使用av_register_all();来初始化libavformat/libavcodec:

这一步注册库中含有的所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。av_register_all()只需
调用一次,所以,要放在初始化代码中。也可以仅仅注册个人的文件格式和编码。
  下一步,打开文件:
AVFormatContext *pFormatCtx;
const char *filename="myvideo.mpg";
av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL); // 打开视频文件
最后三个参数描述了文件格式,缓冲区大小(size)和格式参数;我们通过简单地指明NULL或0告诉 libavformat 去自动探测文件格式并且使用默认的缓
冲区大小。这里的格式参数指的是视频输出参数,比如宽高的坐标。
  下一步,我们需要取出包含在文件中的流信息:
av_find_stream_info(pFormatCtx); // 取出流信息
AVFormatContext 结构体
dump_format(pFormatCtx, 0, filename, false);//我们可以使用这个函数把获取到得参数全部输出。
for(i=0; i<pFormatCtx->nb_streams; i++) //区分视频流和音频流
if(pFormatCtx->streams->codec.codec_type==CODEC_TYPE_VIDEO) //找到视频流,这里也可以换成音频
{
videoStream=i;
break;
}
接下来就需要寻找解码器
AVCodec *pCodec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
avcodec_open(pCodecCtx, pCodec); // 打开解码器
给视频帧分配空间以便存储解码后的图片:
AVFrame *pFrame;
pFrame=avcodec_alloc_frame();
/////////////////////////////////////////开始解码///////////////////////////////////////////
第一步当然是读数据:
我们将要做的是通过读取包来读取整个视频流,然后把它解码成帧,最后转换格式并且保存。
while(av_read_frame(pFormatCtx, &packet)>=0) { //读数据
if(packet.stream_index==videoStream){ //判断是否视频流
avcodec_decode_video(pCodecCtx,pFrame, &frameFinished,
packet.data, packet.size); //解码
if(frameFinished) {
img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,(AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width,pCodecCtx->height);//转换
SaveFrame(pFrameRGB, pCodecCtx->width,pCodecCtx->height, i); //保存数据
av_free_packet(&packet); //释放
av_read_frame()读取一个包并且把它保存到AVPacket结构体中。这些数据可以在后面通过av_free_packet()来释 放。函数avcodec_decode_video()把包
转换为帧。然而当解码一个包的时候,我们可能没有得到我们需要的关于帧的信息。因此,当我们得 到下一帧的时候,avcodec_decode_video()为我们设
置了帧结束标志frameFinished。最后,我们使用 img_convert()函数来把帧从原始格式(pCodecCtx->pix_fmt)转换成为RGB格式。要记住,你可以把一个
AVFrame结构体的指针转换为AVPicture结构体的指针。最后,我们把帧和高度宽度信息传递给我们的SaveFrame函数。
到此解码完毕,显示过程使用SDL完成考虑到我们以后会使用firmware进行显示操作,SDL忽略不讲。
音视频同步
DTS(解码时间戳)和PTS(显示时间戳)
当我们调用av_read_frame()得到一个包的时候,PTS和DTS的信息也会保存在包中。但是我们真正想要的PTS是我们刚刚解码出来的 原始帧 的PTS,这样我
们才能知道什么时候来显示它。然而,我们从avcodec_decode_video()函数中得到的帧只是一个AVFrame,其中并 没有包含有用的PTS值(注意:AVFrame并
没有包含时间戳信息,但当我们等到帧的时候并不是我们想要的样子)。。我们保存一帧的第一个包的PTS: 这将作为整个这一帧的PTS。我们 可以通过函
数avcodec_decode_video()来计算出哪个包是一帧的第一个包。怎样实现呢?任何时候当一个包开始一帧的时 候,avcodec_decode_video()将调用一个函数
来为一帧申请一个缓冲。当然,ffmpeg允许我们重新定义那个分配内存的函数。计算前 一帧和现在这一帧的时间戳来预测出下一个时间戳的时间。同时,我
们需要同步视频到音频。我们将设置一个音频时间audioclock;一个内部值记录了我 们正在播放的音频的位置。就像从任意的mp3播放器中读出来的数字一
样。既然我们把视频同步到音频,视频线程使用这个值来算出是否太快还是太慢。

ffmpeg编码的更多相关文章

  1. ffmpeg编码h264只包含I帧P帧的方法

    ffmpeg使用avcodc_encode_video编码,默认产生的h264包含B帧,在安防行业很多地方是不需要用到B帧的. 1.基础知识充电 这就涉及到h264的各种profile格式了,参考 h ...

  2. 如何强制ffmpeg编码时输出一个关键帧

    http://blog.csdn.net/ashlingr/article/details/7829429 如何强制ffmpeg编码时输出一个关键帧   如何强制ffmpeg编码时输出一个关键帧 AV ...

  3. OpenCV和ffmpeg编码资料分享

    本博也是在进行视频转码的学习道路上,也只是菜鸟一枚,收集了大量的资料,想在这和同路人分享一下 在博园里我发表一个JavaCV的随笔,里面介绍了JavaCV这个框架,它整合了OpenCV和ffmpeg等 ...

  4. 【FFMPEG】【ARM-Linux开发】fmpeg安装第三方编码器(encoder)库,ffmpeg编码h264(完)

    fmpeg安装第三方编码器(encoder)库,ffmpeg编码h264(完) ffmpeg安装第三方编码器(encoder)库 关键词:ffmpeg.编码h264.第三方encoder 安装好了ff ...

  5. ffmpeg编码YUV420视频序列

    依旧是这里的测试序列 http://www.cnblogs.com/zzugyl/p/3678865.html测试了JM和libx264的编解码质量后来用ffmpeg转码 发现忘记了命令行转码的命令网 ...

  6. 流媒体技术学习笔记之(十五)FFmpeg编码遇到的错误、警告、Debug记录

    When encoding H.264 using ffmpeg I get the following type of warnings en masse: Past duration 0.6063 ...

  7. 使用ffmpeg编码时,如何设置恒定码率,并控制好关键帧I帧间隔

    1. 大家在使用ffmpeg进行视频编码时,使用-b命令,想控制比特率,却发现结果并没有如我们设置所愿,通过码流分析器观察视频码流,码率的波动还是很大的,ffmpeg控制的并不好,这时候,我们可以通过 ...

  8. FFmpeg编码详细流程

    FFmpeg在编码一个视频的时候的函数调用流程.为了保证结构清晰,其中仅列出了最关键的函数,剔除了其它不是特别重要的函数. 函数背景色 函数在图中以方框的形式表现出来.不同的背景色标志了该函数不同的作 ...

  9. ffmpeg编码常见问题排查方法

    播放问题排查: 一旦我们遇到视频播放不了,第一件事,就是要找几个别的播放器也播放看看,做一下对比测试,或者对码流做一些基础分析,以便更好的定位问题的源头,而各个平台比较常见的播放/分析工具有如下几个: ...

随机推荐

  1. [Intermediate Algorithm] - Finders Keepers

    题目 写一个 function,它浏览数组(第一个参数)并返回数组中第一个通过某种方法(第二个参数)验证的元素. 提示 Array.filter() 测试用例 find([1, 3, 5, 8, 9, ...

  2. springMVC返回汉字字符串乱码,以及返回的字符串乱码的问题

    1.springMVC在使用@ResponseBody注解返回字符串为什么出现乱码呢?(这里以spring4.3.1为例) 原因分析:原因在返回字符串时StringHttpMessageConvert ...

  3. 如果说需要注册数据中心,这样才能使用demo部署数据中心license证需要申请,使用云之间-工作流程......

    如果说需要注册数据中心,这样才能使用demo部署数据中心license证需要申请,使用云之间-工作流程......

  4. C#第十四节课

    函数的调用 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System ...

  5. codeforces 244B-Undoubtedly Lucky Numbers 搜索

    题意:给你一个n,求不大于n的并且仅由两种或者一种数字组成的数的个数.(有点绕,,简单点就是,看看小于等于n点数中,,有多少数字只有一种数字,或者有两种数字组成) “哎,自己还是太菜了,训练的时候只做 ...

  6. c++ STL - priority_queue优先队列详解

    简述 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除.在优先队列中,元素被赋予优先级.当访问元素时,具有最高优先级的元素最先删除.优先队列具有最高级先出 (first in, l ...

  7. SpringBoot 整合 Mybatis 进行CRUD测试开发

    今天来和大家分享下 Spring Boot 整合 MyBatis 的 CRUD 测试方法开发.因为 MyBaits 有两种开发形式,一种基于注解,一种基于 xml . SpringBoot配置文件也有 ...

  8. 今天写了一个简单的新浪新闻RSS操作类库

    今天,有位群友问我如何获新浪新闻列表相关问题,我想,用正则表达式网页中取显然既复杂又不一定准确,现在许多大型网站都有RSS集合,所以我就跟他说用RSS应该好办一些. 一年前我写过一个RSS阅读器,不过 ...

  9. POJ 1703 Find them, Catch them(并查集高级应用)

    手动博客搬家:本文发表于20170805 21:25:49, 原地址https://blog.csdn.net/suncongbo/article/details/76735893 URL: http ...

  10. Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理

    1.Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理 2. 自定义注解: 使用@interfac ...