ffmpeg如何无法改变的事实的句子封装的分辨率而不进行重编码

安卓用ffmeg解码
本文章是用ffmeg解码封装格式(如mp4)转换为yuv420p保存到本地,本文是结合雷霄骅博客ppt和某地方学习的一个笔记(说出来等下被认为做广告就尴尬了)
视频编码数据
将封装格式解压后可以得到压缩过的音视频等.
将压缩过的视频解压后可以得到 视频像素数据(RGB,YUV等).常见的视频压缩格式有H.264, MPEG4等…
YUV420P格式介绍
YUV是视频像素格式,在压缩视频格式解压可以得到,YUV也有很多种格式.下面举例三种.
1. Y代表亮度
2. uv代表色度
因为人对亮度的敏感远大于色度
用三个图来直观地表示采集的方式吧,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量
先记住下面这段话,以后提取每个像素的YUV分量会用到。
YUV 4:4:4采样,每一个Y对应一组UV分量。
YUV 4:2:2采样,每两个Y共用一组UV分量。
YUV 4:2:0采样,每四个Y共用一组UV分量。
上面大家看最后一幅图即可.
编译ffmpeg的so库
在ffmpeg历史版本中选择一个版本下载到本地
因为我们安卓是linux系统,所以我们下载到本地放入到linux系统编译后给安卓使用.
1下载压缩包放入linux并解压
下面是解压后目录
我们打开文件夹看看.
2编写脚本控制configure生成so
创建一个build_android.sh文件作为脚本
make clean
export NDK=/usr/ndk/android-ndk-r10e
export SYSROOT=$NDK/platforms/android-9/arch-arm/
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64
export CPU=arm
export PREFIX=$(pwd)/android/$CPU
export ADDI_CFLAGS="-marm"
./configure --target-os=linux \
--prefix=$PREFIX --arch=arm \
--disable-doc \
--enable-shared \
--disable-static \
--disable-yasm \
--disable-symver \
--enable-gpl \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-doc \
--disable-symver \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make install
注意上面的几个地方
export NDK=/usr/ndk/android-ndk-r10e
export PREFIX=$(pwd)/android/$CPU
export CPU=arm
上传到服务器ffmpeg解压目录下.
修改解压后ffmpeg目录文件权限
上面的代码会执行修改ffmpeg目录和子目录的权限为可读可写可执行(子目录也要不然有坑)
最后执行我们的脚本(大约会进行10分钟)
编译完成后会在 ffmpeg目录下的android的arm下生成两个文件
一个是include文件夹 ,另一个是lib
1. include 包含编译生成的so对应头文件
lib 生成的so文件
我们打开lib目录查看:
libXXX.so.YYYY,DDD
上面XXX是so 功能类名
YYYY,DDDD是版本号.这样库在安卓是无法使用的
有人又会说了,目录下面不是有libXXX.so吗?
他只是个linux的软连接,就像快捷方式一样
修改目录下configure文件部分内容如下
注释部分为原来内容
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
修改之后重新 执行./build_android即可
Eclipse编译之旅
将上面的lib中的so文件(当然也可以将lib文件夹复制过去)拷贝到eclipse目录的jni下,将include文件夹也放入jin下
修改Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := libavcodec-56.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avdevice
LOCAL_SRC_FILES := libavdevice-56.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avfilter
LOCAL_SRC_FILES := libavfilter-5.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := libavformat-56.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := libavutil-54.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := postproc
LOCAL_SRC_FILES := libpostproc-53.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := libswresample-1.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := libswscale-3.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE
:= DemoFFmepeg
LOCAL_SRC_FILES := DemoFFmepeg.c
LOCAL_LDLIBS+= -llog
LOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil postproc swresample swscale
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_SHARED_LIBRARY)
我们先看看MainActivity.java 文件
package com.fmple.
import java.io.F
import android.app.A
import android.os.B
import android.os.E
import android.util.L
import android.view.M
import android.view.MenuI
public class MainActivity extends Activity {
System.loadLibrary("DemoFFmepeg");
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final File inputFile = new File(Environment.getExternalStorageDirectory(),"a.mov");
final File outFile = new File(Environment.getExternalStorageDirectory(),"b.yuv");
new Thread(){
public void run() {
ffmpeg(inputFile.getAbsolutePath(), outFile.getAbsolutePath());
}.start();
* input 视频文件的输入路径
* out 把视频文件解码成yuv格式输出路径
public native void ffmpeg(String input,String out);
上面也没什么好说的,就是创建activity的时候调用我们的一个jni方法ffmpeg
最后看看方法的实现文件DemoFFmepeg.c
下面我完整翻译了所用到的API耗时挺久的,英语不好…
#include &jni.h&
#include&stdio.h&
#include&android/log.h&
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO," FMY",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"FMY",FORMAT,##__VA_ARGS__);
char * output_
* 这里提供了另一种方式转化为saveYUV420 本案例不用 大家可以自己体验下此方法(这个方法在网上看到过)
static void saveYUV420P(unsigned char *buf,int wrap,int xsize, int ysize){
FILE *fileOut = NULL;
if(buf ==NULL){
LOGE("缓存为空");
fileOut = fopen(output_cstr,"ab+");
for (i = 0;i& ++i) {
fwrite(buf+i*wrap,1,xsize,fileOut);
fflush(fileOut);
fclose(fileOut);
JNIEXPORT void JNICALL Java_com_fmple_demoffmepeg_MainActivity_ffmpeg
(JNIEnv * env, jobject jobj, jstring input, jstring out){
char * input_char = (*env)-&GetStringUTFChars(env,input,NULL);
output_cstr = (*env)-&GetStringUTFChars(env,out,NULL);
* 初始化libavformat和注册所有的 muxers, demuxers 和协议,如果你不想使用次函数,
* 则可以手动选择你想要的支持格式
* 详情你可以选择下面的函数查询
* av_register_input_format()
* av_register_output_format()
* muxer是合并将视频文件、音频文件和字幕文件合并为某一个视频格式。如,可将a.avi, a.mp3, a.srt用muxer合并为mkv格式的视频文件。
* demuxer是拆分这些文件的。
av_register_all();
AVFormatContext * pFormatCtx = avformat_alloc_context();
* 打开输入流并且读取头信息。但解码器没有打开
* 这个输入流必须使用avformat_close_input()关闭
* ps(第一个参数的形参名称) 指向 你由你提供AVFormatContext(AVFormatContext是由avformat_alloc_context函数分配的)。
* 有可能ps指向空,在这种情况下,AVFormatContext由此函数分配并写入ps。
* 注意: 你提供的AVFormatContext在函数执行失败的时候将会被释放
* url 你要打开视频文件路径.
如果不为空,那么这个参数将强制作为输入格式,否则自动检索
* options 一个关于AVFormatContext and demuxer-private 选项的字典.
* 返回时,此参数将被销毁,并替换为包含未找到的选项的dict。有可能是空的
* 返回0表示成功, 一个负数常量AVERROR是失败的.
* 如果你想自定义IO,你需要预分配格式内容并且设置pd属性
if(avformat_open_input(&pFormatCtx,input_char,NULL,NULL)!=0){
LOGE("NDK&&&%s","avformat_open_input打开失败");
* 读取媒体文件中的数据包以获取流信息,这个对于对于文件格式没有头信息的很有帮助,比如说mpeg
* 这个函数还可以计算在MPEG-2重复帧模式的真实帧速率。
* 逻辑文件位置不会被这个函数改变
* 检索过的数据包或许会缓存以供后续处理
第一个参数 封装格式上下文
如果不为空, 一个长度为 ic.nb_streams (封装格式所有流,字幕 视频 音频等) 的字典。
字典中第i个成员
包含一个对应ic第i个流中对的编码器。
在返回时,每个字典将会填充没有找到的选项
* 如果返回&=0 代表成功, AVERROR_xxx 表示失败
* 这个函数 不保证能打开所有编码器,所以返回一个非空的选项是一个完全正常的行为
下个版本目标无视即可
* Let the user decide somehow what information is needed so that
we do not waste time getting stuff the user does not need.
if( avformat_find_stream_info(pFormatCtx,NULL)&0){
LOGE("NDK&&&%s","avformat_find_stream_info失败");
LOGE("NDK&&&%s","成功");
int v_stream_idx = -1;
int i = 0;
for (; i & pFormatCtx-&nb_ ++i) {
if (pFormatCtx-&streams[i]-&codec-&codec_type ==AVMEDIA_TYPE_VIDEO) {
v_stream_idx =
if (v_stream_idx==-1) {
LOGE("没有找视频流")
LOGE("找到视频流")
AVCodecContext *pCodecCtx = pFormatCtx-&streams[v_stream_idx]-&
AVCodec *pCodec =avcodec_find_decoder(pCodecCtx-&codec_id);
if (pCodec == NULL)
LOGE("%s","找不到解码器\n");
LOGE("%s","找到解码器\n");
* 初始化 指定AVCodecContext去使用 给定的AVCodec
* 在使用之前函数必须使用avcodec_alloc_context3()分配上下文。
* 以下函数 avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),
* avcodec_find_decoder() and avcodec_find_encoder() 提供了一个简便的得到一个解码器的方法
* 这个函数线程不是安全的
* 在使用解码程序之前,始终调用此函数 (如 avcodec_decode_video2()).
* 下面是示例代码
* avcodec_register_all();
* av_dict_set(&opts, "b", "2.5M", 0);
* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
* if (!codec)
* context = avcodec_alloc_context3(codec);
* if (avcodec_open2(context, codec, opts) & 0)
* avctx 要初始化的编码器
* codec 用这个codec去打开给定的上下文编码器.如果 codec 不为空 那么必须
* 事先用avcodec_alloc_context3和avcodec_get_context_defaults3传递给这个context,那么这个codec
* 要么为NULL要么就是上面调用函数所使用的codec
* 选项填充AVCodecContext和编解码器私有选项的字典。返回时,此对象将填充未找到的选项。
* 返回0表示成功, 负数失败
* avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),
av_dict_set(), av_opt_find().
if(avcodec_open2(pCodecCtx,pCodec,NULL)==0){
LOGE("%s","打开编码器成功\n");
LOGE("%s","打开编码器失败\n");
LOGE("视频的文件格式:%s",pFormatCtx-&iformat-&name);
if(pFormatCtx-&duration != AV_NOPTS_VALUE){
int hours, mins, secs,
int64_t duration = pFormatCtx-&duration + 5000;
secs = duration / AV_TIME_BASE;
us = duration % AV_TIME_BASE;
mins = secs / 60;
secs %= 60;
hours = mins/ 60;
mins %= 60;
LOGE("%02d:%02d:%02d.%02d\n", hours, mins, secs, (100 * us) / AV_TIME_BASE);
LOGE("视频的宽高:%d,%d",pCodecCtx-&width,pCodecCtx-&height);
LOGE("解码器的名称:%s",pCodec-&name);
AVPacket *packet =av_malloc(sizeof(AVPacket));
FILE *fp_yuv = fopen(output_cstr,"wb+");
AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameYUV = av_frame_alloc();
* 如果给定存储图片的格式,那么计算给定的宽高所占用的大小
图片像素格式
* 返回计算的图片缓存大小或者错误情况下的负数错误代码
* 这里计算缓存区的大小,但是没有分配,这里是用来后面转码使用
uint8_t *out_buffer = av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P,pCodecCtx-&width,pCodecCtx-&height));
* 基于指定的图片参数和提供的图片缓存区去设置图片字段
* 使用ptr所指向的图片数据缓存
填充图片属性
* 如果 ptr是空,这个函数仅填充图片行大小(linesize)的数组并且返回图片缓存请求的大小
* 要分配图片缓存并且再一次填充图片数据请使用 avpicture_alloc().
要填充的图片
存储图片的数据的缓存区, or NULL
图片像素格式
* 返回请求的字节大小,在错误的情况下返回负数
* av_image_fill_arrays()
avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx-&width, pCodecCtx-&height);
*分配和返回 SwsContext. You need it to perform
* scaling/conversion operations using sws_scale().
* srcW 原始图宽
* srcH 原始图高
* srcFormat 原始图格式
* dstW 目标图宽
* dstH 不解释
* dstFormat 不解释
* flags 指定一个标志用于重新调整算法和选项
具体参考:http://blog.csdn.net/leixiaohua1020/article/details/
* 一个指定分配内容的指针, 错误情况返回空
* this function is to be removed after a saner alternative is
struct SwsContext *sws_ctx =sws_getContext(pCodecCtx-&width, pCodecCtx-&height,pCodecCtx-&pix_fmt,
pCodecCtx-&width, pCodecCtx-&height,AV_PIX_FMT_YUV420P,
SWS_BICUBIC, NULL, NULL, NULL);
int got_picture,
*返回下一帧的流
* 此函数返回存储在文件中的内容,并且不会验证解码器有什么有效帧。
* 函数将存储在文件中的帧进行分割 并且返回给每一个调用者。
* 函数不会删除在有效帧之间的无效数据 以便在可能解码过程中提供解码器最大的信息帮助
* 如果 pkt-&buf 是空的,那么这个对应数据包是有效的直到下一次调用av_read_frame()
* 或者直到使用avformat_close_input().否则包无期限有效
* 在这两种情况下 这个数据包当你不在需要的时候,你必须使用使用av_free_packet释放它
* 对于视屏,数据包刚好只包含一帧.对于音频,如果它每一帧是一个已知固定大小的,那么他包含整数帧(如. PCM or ADPCM data)
* 如果音频帧具有可变大小(如. MPEG audio),那么他只包含一帧
* pkt-&pts, pkt-&dts and pkt-&duration 始终在AVStream.time_base 单位设置正确的数值
*(如果这个格式无法提供.那么进行猜测)
* 如果视频格式有B帧那么pkt-&pts可以是 AV_NOPTS_VALUE.如果你没有解压他的有效部分那么最好依靠pkt-&dts
* 0表示成功, & 0 错误或者文结束
while(av_read_frame(pFormatCtx,packet)&=0){
if (packet-&stream_index==v_stream_idx) {
LOGE("测试");
* 解码视频帧 从avpkt-&data读取数据并且解码avpkt-&size的大小后转化为图片.
* 一些解码器可以支持在一个ACpacket中存在多帧的情况,像这样的解码器将只解码第一帧
输入缓存区必须 实际读取的字节流小于 FF_INPUT_BUFFER_PADDING_SIZE,
* 一些优化过的比特流 读取32位或者64字节 的时候可以一次性读取完
* 在缓冲器的buf结尾设置0以确保被破坏的MPEG流不会发生超线程
* 有 CODEC_CAP_DELAY 才能设置一个在输入和输出之间的延迟,这些需要使用avpkt-&data=NULL,
在结束返回剩余帧数的时候avpkt-&size=0
这个AVCodecContext 在数据包传入解码器之前必须调用avcodec_open2
* avctx 解码器上下文
*[out] 解码的视频帧图片将会被存储在AVFrame.
使用av_frame_alloc 得到一个AVFrame,
编码器将会分配 使用
AVCodecContext.get_buffer2() 回调
的实际图片的内存.
当AVCodecContext.refcounted_frames 设置为1,这帧(frame)是引用计数,并且返回
的引用计数是属于调用者的.
frame在长实际不使用的时候调用者必须调用av_frame_unref()就行释放
如果av_frame_is_writable()返回1那么调用者可以安全的写入到这个frame中。
当AVCodecContext.refcounted_frames设置为0,返回的引用属于解码器,
只有下次使用这个函数或者关闭或者刷新这个编码器之前有效。调用者不会写入它
*@param[in,out] got_picture_ptr 如果为0表示不能解压, 否者它不是0.
*[in] avpkt 这个输入的avpkt包含输入缓存区
你能使用av_init_packet()创建像这样的packet然后设置数据和大小,
一些解码器还可以添加一些其他字段 比如
flags&AV_PKT_FLAG_KEY
flags&AV_PKT_FLAG_KEY
所有解码器都设计为尽可能少地使用
* 再错误时返回一个负数 , 否则返回使用字节数或者或者0(没有帧被解压返回0)otherwise the number of bytes
ret=avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);
if(ret&=0){
LOGE("解压成功");
sws_scale(sws_ctx,pFrame-&data,pFrame-&linesize,0,pCodecCtx-&height,pFrameYUV-&data,pFrameYUV-&linesize);
int y_size = pCodecCtx-&width * pCodecCtx-&
fwrite(pFrameYUV-&data[0], 1, y_size, fp_yuv);
fwrite(pFrameYUV-&data[1], 1, y_size / 4, fp_yuv);
fwrite(pFrameYUV-&data[2], 1, y_size / 4, fp_yuv);
av_free_packet(packet);
(*env)-&ReleaseStringUTFChars(env,input,input_char);
(*env)-&ReleaseStringUTFChars(env,out,output_cstr);
fclose(fp_yuv);
av_frame_free(&pFrame);
av_frame_free(&pFrameYUV);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
avformat_free_context(pFormatCtx);
Android Studio编译
注意:创建项目的时候勾选Include C++ support
导入所有so库到libs中
将include头文件导入cpp目录下
修改CMakeLists.txt文件
cmake_minimum_required(VERSION 3.4.1)
set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs/)
add_library(libavcodec-56-lib SHARED IMPORTED)
set_target_properties(libavcodec-56-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavcodec-56.so)
add_library(libavdevice-56-lib SHARED IMPORTED)
set_target_properties(libavdevice-56-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavdevice-56.so)
add_library(libavfilter-5-lib SHARED IMPORTED)
set_target_properties(libavfilter-5-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavfilter-5.so)
add_library(libavformat-56-lib SHARED IMPORTED)
set_target_properties(libavformat-56-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavformat-56.so)
add_library(libavutil-54-lib SHARED IMPORTED)
set_target_properties(libavutil-54-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavutil-54.so)
add_library(libpostproc-53-lib SHARED IMPORTED)
set_target_properties(libpostproc-53-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libpostproc-53.so)
add_library(libswresample-1-lib SHARED IMPORTED)
set_target_properties(libswresample-1-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libswresample-1.so)
add_library(libswscale-3-lib SHARED IMPORTED)
set_target_properties(libswscale-3-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libswscale-3.so)
add_library(
native-lib
src/main/cpp/native-lib.c )
find_library(
include_directories( src/main/cpp/include/ )
target_link_libraries(
native-lib
${log-lib}
libavcodec-56-lib
libavdevice-56-lib
libavfilter-5-lib
libavformat-56-lib
libavutil-54-lib
libpostproc-53-lib
libswresample-1-lib
libswscale-3-lib
最后代码实现
#include &jni.h&
#include&android/log.h&
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO," FMY",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"FMY",FORMAT,##__VA_ARGS__);
JNIEXPORT void JNICALL
Java_com_example_demoffmpeg_MainActivity_ffmpeg(JNIEnv * env, jobject jobj, jstring input, jstring out) {
char * input_char = (*env)-&GetStringUTFChars(env,input,NULL);
output_cstr = (*env)-&GetStringUTFChars(env,out,NULL);
* 初始化libavformat和注册所有的 muxers, demuxers 和协议,如果你不想使用次函数,
* 则可以手动选择你想要的支持格式
* 详情你可以选择下面的函数查询
* av_register_input_format()
* av_register_output_format()
* muxer是合并将视频文件、音频文件和字幕文件合并为某一个视频格式。如,可将a.avi, a.mp3, a.srt用muxer合并为mkv格式的视频文件。
* demuxer是拆分这些文件的。
av_register_all();
AVFormatContext * pFormatCtx = avformat_alloc_context();
* 打开输入流并且读取头信息。但解码器没有打开
* 这个输入流必须使用avformat_close_input()关闭
* ps(第一个参数的形参名称) 指向 你由你提供AVFormatContext(AVFormatContext是由avformat_alloc_context函数分配的)。
* 有可能ps指向空,在这种情况下,AVFormatContext由此函数分配并写入ps。
* 注意: 你提供的AVFormatContext在函数执行失败的时候将会被释放
* url 你要打开视频文件路径.
如果不为空,那么这个参数将强制作为输入格式,否则自动检索
* options 一个关于AVFormatContext and demuxer-private 选项的字典.
* 返回时,此参数将被销毁,并替换为包含未找到的选项的dict。有可能是空的
* 返回0表示成功, 一个负数常量AVERROR是失败的.
* 如果你想自定义IO,你需要预分配格式内容并且设置pd属性
if(avformat_open_input(&pFormatCtx,input_char,NULL,NULL)!=0){
LOGE("NDK&&&%s","avformat_open_input打开失败");
* 读取媒体文件中的数据包以获取流信息,这个对于对于文件格式没有头信息的很有帮助,比如说mpeg
* 这个函数还可以计算在MPEG-2重复帧模式的真实帧速率。
* 逻辑文件位置不会被这个函数改变
* 检索过的数据包或许会缓存以供后续处理
第一个参数 封装格式上下文
如果不为空, 一个长度为 ic.nb_streams (封装格式所有流,字幕 视频 音频等) 的字典。
字典中第i个成员
包含一个对应ic第i个流中对的编码器。
在返回时,每个字典将会填充没有找到的选项
* 如果返回&=0 代表成功, AVERROR_xxx 表示失败
* 这个函数 不保证能打开所有编码器,所以返回一个非空的选项是一个完全正常的行为
下个版本目标无视即可
* Let the user decide somehow what information is needed so that
we do not waste time getting stuff the user does not need.
if( avformat_find_stream_info(pFormatCtx,NULL)&0){
LOGE("NDK&&&%s","avformat_find_stream_info失败");
LOGE("NDK&&&%s","成功");
int v_stream_idx = -1;
int i = 0;
for (; i & pFormatCtx-&nb_ ++i) {
if (pFormatCtx-&streams[i]-&codec-&codec_type ==AVMEDIA_TYPE_VIDEO) {
v_stream_idx =
if (v_stream_idx==-1) {
LOGE("没有找视频流")
LOGE("找到视频流")
AVCodecContext *pCodecCtx = pFormatCtx-&streams[v_stream_idx]-&
AVCodec *pCodec =avcodec_find_decoder(pCodecCtx-&codec_id);
if (pCodec == NULL)
LOGE("%s","找不到解码器\n");
LOGE("%s","找到解码器\n");
* 初始化 指定AVCodecContext去使用 给定的AVCodec
* 在使用之前函数必须使用avcodec_alloc_context3()分配上下文。
* 以下函数 avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),
* avcodec_find_decoder() and avcodec_find_encoder() 提供了一个简便的得到一个解码器的方法
* 这个函数线程不是安全的
* 在使用解码程序之前,始终调用此函数 (如 avcodec_decode_video2()).
* 下面是示例代码
* avcodec_register_all();
* av_dict_set(&opts, "b", "2.5M", 0);
* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
* if (!codec)
* context = avcodec_alloc_context3(codec);
* if (avcodec_open2(context, codec, opts) & 0)
* avctx 要初始化的编码器
* codec 用这个codec去打开给定的上下文编码器.如果 codec 不为空 那么必须
* 事先用avcodec_alloc_context3和avcodec_get_context_defaults3传递给这个context,那么这个codec
* 要么为NULL要么就是上面调用函数所使用的codec
* 选项填充AVCodecContext和编解码器私有选项的字典。返回时,此对象将填充未找到的选项。
* 返回0表示成功, 负数失败
* avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),
av_dict_set(), av_opt_find().
if(avcodec_open2(pCodecCtx,pCodec,NULL)==0){
LOGE("%s","打开编码器成功\n");
LOGE("%s","打开编码器失败\n");
LOGE("视频的文件格式:%s",pFormatCtx-&iformat-&name);
if(pFormatCtx-&duration != AV_NOPTS_VALUE){
int hours, mins, secs,
int64_t duration = pFormatCtx-&duration + 5000;
secs = duration / AV_TIME_BASE;
us = duration % AV_TIME_BASE;
mins = secs / 60;
secs %= 60;
hours = mins/ 60;
mins %= 60;
LOGE("%02d:%02d:%02d.%02d\n", hours, mins, secs, (100 * us) / AV_TIME_BASE);
LOGE("视频的宽高:%d,%d",pCodecCtx-&width,pCodecCtx-&height);
LOGE("解码器的名称:%s",pCodec-&name);
AVPacket *packet =av_malloc(sizeof(AVPacket));
FILE *fp_yuv = fopen(output_cstr,"wb+");
AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameYUV = av_frame_alloc();
* 如果给定存储图片的格式,那么计算给定的宽高所占用的大小
图片像素格式
* 返回计算的图片缓存大小或者错误情况下的负数错误代码
* 这里计算缓存区的大小,但是没有分配,这里是用来后面转码使用
uint8_t *out_buffer = av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P,pCodecCtx-&width,pCodecCtx-&height));
* 基于指定的图片参数和提供的图片缓存区去设置图片字段
* 使用ptr所指向的图片数据缓存
填充图片属性
* 如果 ptr是空,这个函数仅填充图片行大小(linesize)的数组并且返回图片缓存请求的大小
* 要分配图片缓存并且再一次填充图片数据请使用 avpicture_alloc().
要填充的图片
存储图片的数据的缓存区, or NULL
图片像素格式
* 返回请求的字节大小,在错误的情况下返回负数
* av_image_fill_arrays()
avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx-&width, pCodecCtx-&height);
*分配和返回 SwsContext. You need it to perform
* scaling/conversion operations using sws_scale().
* srcW 原始图宽
* srcH 原始图高
* srcFormat 原始图格式
* dstW 目标图宽
* dstH 不解释
* dstFormat 不解释
* flags 指定一个标志用于重新调整算法和选项
具体参考:http://blog.csdn.net/leixiaohua1020/article/details/
* 一个指定分配内容的指针, 错误情况返回空
* this function is to be removed after a saner alternative is
struct SwsContext *sws_ctx =sws_getContext(pCodecCtx-&width, pCodecCtx-&height,pCodecCtx-&pix_fmt,
pCodecCtx-&width, pCodecCtx-&height,AV_PIX_FMT_YUV420P,
SWS_BICUBIC, NULL, NULL, NULL);
int got_picture,
*返回下一帧的流
* 此函数返回存储在文件中的内容,并且不会验证解码器有什么有效帧。
* 函数将存储在文件中的帧进行分割 并且返回给每一个调用者。
* 函数不会删除在有效帧之间的无效数据 以便在可能解码过程中提供解码器最大的信息帮助
* 如果 pkt-&buf 是空的,那么这个对应数据包是有效的直到下一次调用av_read_frame()
* 或者直到使用avformat_close_input().否则包无期限有效
* 在这两种情况下 这个数据包当你不在需要的时候,你必须使用使用av_free_packet释放它
* 对于视屏,数据包刚好只包含一帧.对于音频,如果它每一帧是一个已知固定大小的,那么他包含整数帧(如. PCM or ADPCM data)
* 如果音频帧具有可变大小(如. MPEG audio),那么他只包含一帧
* pkt-&pts, pkt-&dts and pkt-&duration 始终在AVStream.time_base 单位设置正确的数值
*(如果这个格式无法提供.那么进行猜测)
* 如果视频格式有B帧那么pkt-&pts可以是 AV_NOPTS_VALUE.如果你没有解压他的有效部分那么最好依靠pkt-&dts
* 0表示成功, & 0 错误或者文结束
while(av_read_frame(pFormatCtx,packet)&=0){
if (packet-&stream_index==v_stream_idx) {
LOGE("测试");
* 解码视频帧 从avpkt-&data读取数据并且解码avpkt-&size的大小后转化为图片.
* 一些解码器可以支持在一个ACpacket中存在多帧的情况,像这样的解码器将只解码第一帧
输入缓存区必须 实际读取的字节流小于 FF_INPUT_BUFFER_PADDING_SIZE,
* 一些优化过的比特流 读取32位或者64字节 的时候可以一次性读取完
* 在缓冲器的buf结尾设置0以确保被破坏的MPEG流不会发生超线程
* 有 CODEC_CAP_DELAY 才能设置一个在输入和输出之间的延迟,这些需要使用avpkt-&data=NULL,
在结束返回剩余帧数的时候avpkt-&size=0
这个AVCodecContext 在数据包传入解码器之前必须调用avcodec_open2
* avctx 解码器上下文
*[out] 解码的视频帧图片将会被存储在AVFrame.
使用av_frame_alloc 得到一个AVFrame,
编码器将会分配 使用
AVCodecContext.get_buffer2() 回调
的实际图片的内存.
当AVCodecContext.refcounted_frames 设置为1,这帧(frame)是引用计数,并且返回
的引用计数是属于调用者的.
frame在长实际不使用的时候调用者必须调用av_frame_unref()就行释放
如果av_frame_is_writable()返回1那么调用者可以安全的写入到这个frame中。
当AVCodecContext.refcounted_frames设置为0,返回的引用属于解码器,
只有下次使用这个函数或者关闭或者刷新这个编码器之前有效。调用者不会写入它
*@param[in,out] got_picture_ptr 如果为0表示不能解压, 否者它不是0.
*[in] avpkt 这个输入的avpkt包含输入缓存区
你能使用av_init_packet()创建像这样的packet然后设置数据和大小,
一些解码器还可以添加一些其他字段 比如
flags&AV_PKT_FLAG_KEY
flags&AV_PKT_FLAG_KEY
所有解码器都设计为尽可能少地使用
* 再错误时返回一个负数 , 否则返回使用字节数或者或者0(没有帧被解压返回0)otherwise the number of bytes
ret=avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);
if(ret&=0){
LOGE("解压成功");
sws_scale(sws_ctx,pFrame-&data,pFrame-&linesize,0,pCodecCtx-&height,pFrameYUV-&data,pFrameYUV-&linesize);
int y_size = pCodecCtx-&width * pCodecCtx-&
fwrite(pFrameYUV-&data[0], 1, y_size, fp_yuv);
fwrite(pFrameYUV-&data[1], 1, y_size / 4, fp_yuv);
fwrite(pFrameYUV-&data[2], 1, y_size / 4, fp_yuv);
av_free_packet(packet);
(*env)-&ReleaseStringUTFChars(env,input,input_char);
(*env)-&ReleaseStringUTFChars(env,out,output_cstr);
fclose(fp_yuv);
av_frame_free(&pFrame);
av_frame_free(&pFrameYUV);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
avformat_free_context(pFormatCtx);
没有更多推荐了,

我要回帖

更多关于 改变照片分辨率 的文章

 

随机推荐