从开始的解封装,初始化解码器,解码,接受,所有的东西都看不到实际的效果,接下来就是展示视频音频的步骤了。
一:音频的重采样
在数据接受后判断音频流还是视频流,在音频流中做处理
SwrContext *actx=swr_alloc();
//2p:通道格式默认 声道数 3p:输出格式
actx = swr_alloc_set_opts(actx, av_get_default_channel_layout(2), AV_SAMPLE_FMT_S16,ac->sample_rate,
av_get_default_channel_layout(ac->channels), ac->sample_fmt, ac->sample_rate,0,0);
re = swr_init(actx);
char *pcm = new char[48000*4*2];
pcm是缓冲区,actx是频重采样上下文。
swr_alloc_set_opts是上下文参数配置包括原数据参数和转换参数,声道数可以根据实际的channels设置,但通常只需要双声道兼容大部分的声卡。
下面是进行转换的操作,主要借助swr_convert
uint8_t *out[2];
out[0]=(uint8_t *)pcm;
//out 输出数据 p3:一个frame中有多少个音频样板
int len=swr_convert(actx,out,frame->nb_samples,
(const uint8_t **)frame->data,frame->nb_samples);
//通道输出样品数
LOGW("swr_convert = %d", len);
二:视频像素格式和尺寸转换
和音频相同,先对转换上下文初始化,设置转换后的视频宽高为1920和720
//初始化像素格式转换的上下文
SwsContext *vctx=NULL;
//输出宽高
LOGW("the video with:%d",vc->width);
int outWidth=1920;
int outHeight=720;
char *rgb = new char[1920*1080*4];
上下文初始化代码:
vctx = sws_getCachedContext(vctx,frame->width,frame->height,(AVPixelFormat) frame->format,
outWidth,
outHeight,
AV_PIX_FMT_RGBA,
SWS_FAST_BILINEAR,
0, 0, 0);
转换的像素格式为AV_PIX_FMT_RGBA,SWS_FAST_BILINEAR是进行转换的算法。后面参数不需要。
下面进行转换操作:
uint8_t *data[AV_NUM_DATA_POINTERS] = {0};
data[0] = (uint8_t *) rgb;
int lines[AV_NUM_DATA_POINTERS] = {0};
lines[0] = outWidth * 4;
int h = sws_scale(vctx,
(const uint8_t **) frame->data,
frame->linesize, 0,
frame->height,
data, lines);
测试用了两个视频,出现一个转换失败的情况:
转换失败的:原视频vc->width=1920 转换尺寸为1920 转换尺寸换成1080成功
转化成功的:原视频vc->width=1080 转换尺寸为1280 转换尺寸换成1080成功
暂时不清楚具体原因是否是元数据的尺寸导致的。
如果h 转换后的高度是目标高度则转换成功。
三:视频绘制
android中视频的绘制借助GLSurfaceView完成,作为视频播放的容器,也需要使用到android底层的C++库完成视频绘制。
首先定义一个继承GLSurfaceView的PlayView:
public class PlayView extends GLSurfaceView implements Runnable, SurfaceHolder.Callback {
public PlayView(Context context) {
super(context);
}
public PlayView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void run() {
Open("/sdcard/LL迷窟-04.mp4",getHolder().getSurface());
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
new Thread(this).start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
}
public native void Open(String url, Object surface);
}
设置到布局,这里新定义了native方法Open,参数是视频路径和Surface。
需要配置Android相关的库
初始化窗口
//初始化窗口客服端传递的容器 Android 相关的底层库
ANativeWindow *nwin = ANativeWindow_fromSurface(env, surface);
ANativeWindow_setBuffersGeometry(nwin, outWidth, outHeight, WINDOW_FORMAT_RGBA_8888);
ANativeWindow_Buffer wbuffer;
完成数据拷贝:
if (h > 0) {
ANativeWindow_lock(nwin,&wbuffer,0);
uint8_t *dst=(uint8_t*)wbuffer.bits;
//vWidth * vHeight * 4 RGBA8888 四个字节
//拷贝数据
memcpy(dst, rgb, outWidth * outHeight * 4);
ANativeWindow_unlockAndPost(nwin);
}
注意 memcpy(dst, rgb, outWidth * outHeight * 4);方法可能引起视频界面不对齐的问题、
竖屏情况下显示有问题:
横屏的话正常:
终于看见画面了,但是没有声音。