您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
19. 完成了IDemux的Seek和界面SeekBar的事件响应~1
发布时间:2021-06-14 16:54:16编辑:雪饮阅读()
Seek的實現比較複雜,這裏首先從cpp/FFDemux.cpp中實現seek方法:
#include "XLog.h"
extern "C"{
#include <libavformat/avformat.h>
}
//分数转为浮点数
static double r2d(AVRational r)
{
return r.num == 0 || r.den == 0 ?0.:(double) r.num/(double)r.den;
}
void FFDemux::Close()
{
mux.lock();
if(ic)
avformat_close_input(&ic);
mux.unlock();
}
//seek 位置 pos 0.0~1.0
bool FFDemux::Seek(double pos)
{
if(pos<0 || pos > 1)
{
XLOGE("Seek value must 0.0~1.0");
return false;
}
bool re = false;
mux.lock();
if(!ic)
{
mux.unlock();
return false;
}
//清理读取的缓冲
avformat_flush(ic);
long long seekPts = 0;
seekPts = ic->streams[videoStream]->duration*pos;
//往后跳转到关键帧
re = av_seek_frame(ic,videoStream,seekPts,AVSEEK_FLAG_FRAME|AVSEEK_FLAG_BACKWARD);
mux.unlock();
return re;
}
//打开文件,或者流媒体 rmtp http rtsp
bool FFDemux::Open(const char *url)
{
XLOGI("Open file %s begin",url);
Close();
mux.lock();
int re = avformat_open_input(&ic,url,0,0);
if(re != 0 )
{
mux.unlock();
char buf[1024] = {0};
av_strerror(re,buf,sizeof(buf));
XLOGE("FFDemux open %s failed!",url);
return false;
}
XLOGI("FFDemux open %s success!",url);
//读取文件信息
re = avformat_find_stream_info(ic,0);
if(re != 0 )
{
mux.unlock();
char buf[1024] = {0};
av_strerror(re,buf,sizeof(buf));
XLOGE("avformat_find_stream_info %s failed!",url);
return false;
}
this->totalMs = ic->duration/(AV_TIME_BASE/1000);
mux.unlock();
XLOGI("total ms = %d!",totalMs);
GetVPara();
GetAPara();
return true;
}
//获取视频参数
XParameter FFDemux::GetVPara()
{
mux.lock();
if (!ic) {
mux.unlock();
XLOGE("GetVPara failed! ic is NULL!");
return XParameter();
}
//获取了视频流索引
int re = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);
if (re < 0) {
mux.unlock();
XLOGE("av_find_best_stream failed!");
return XParameter();
}
videoStream = re;
XParameter para;
para.para = ic->streams[re]->codecpar;
mux.unlock();
return para;
}
//获取音频参数
XParameter FFDemux::GetAPara()
{
mux.lock();
if (!ic) {
mux.unlock();
XLOGE("GetVPara failed! ic is NULL!");
return XParameter();
}
//获取了音频流索引
int re = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0);
if (re < 0) {
mux.unlock();
XLOGE("av_find_best_stream failed!");
return XParameter();
}
audioStream = re;
XParameter para;
para.para = ic->streams[re]->codecpar;
para.channels = ic->streams[re]->codecpar->channels;
para.sample_rate = ic->streams[re]->codecpar->sample_rate;
mux.unlock();
return para;
}
//读取一帧数据,数据由调用者清理
XData FFDemux::Read()
{
mux.lock();
if(!ic)
{
mux.unlock();
return XData();
}
XData d;
AVPacket *pkt = av_packet_alloc();
int re = av_read_frame(ic,pkt);
if(re != 0)
{
av_packet_free(&pkt);
return XData();
}
//XLOGI("pack size is %d ptss %lld",pkt->size,pkt->pts);
d.data = (unsigned char*)pkt;
d.size = pkt->size;
if(pkt->stream_index == audioStream)
{
d.isAudio = true;
}
else if(pkt->stream_index == videoStream)
{
d.isAudio = false;
}
else
{
av_packet_free(&pkt);
mux.unlock();
return XData();
}
//转换pts
pkt->pts = pkt->pts * (1000*r2d(ic->streams[pkt->stream_index]->time_base));
pkt->dts = pkt->dts * (1000*r2d(ic->streams[pkt->stream_index]->time_base));
d.pts = (int)pkt->pts;
//XLOGE("demux pts %d",d.pts);
mux.unlock();
return d;
}
FFDemux::FFDemux()
{
static bool isFirst = true;
if(isFirst)
{
isFirst = false;
//注册所有封装器
av_register_all();
//注册所有的解码器
avcodec_register_all();
//初始化网络
avformat_network_init();
XLOGI("register ffmpeg!");
}
}
在cpp/IDemux.h中也聲明seek函數:
Cpp/IPlayer.cpp中同樣要實現seek:
然後cpp/IPlayer.h也要聲明seek:
在cpp/IPlayerPorxy.cpp同樣要實現seek:
在cpp/IPlayerPorxy.h中也要聲明seek函數:
Cpp/native-lib.cpp中的調用則要大幅度的改動了:
#define XPLAY_IPLAYER_H
#include <mutex>
#include "XThread.h"
#include "XParameter.h"
class IDemux;
class IAudioPlay;
class IVideoView;
class IResample;
class IDecode;
class IPlayer : public XThread
{
public:
static IPlayer *Get(unsigned char index=0);
virtual bool Open(const char *path);
virtual void Close();
virtual bool Start();
virtual void InitView(void *win);
//获取当前的播放进度 0.0 ~ 1.0
virtual double PlayPos();
virtual bool Seek(double pos);
//是否视频硬解码
bool isHardDecode = true;
//音频输出参数配置
XParameter outPara;
IDemux *demux = 0;
IDecode *vdecode = 0;
IDecode *adecode = 0;
IResample *resample = 0;
IVideoView *videoView = 0;
IAudioPlay *audioPlay = 0;
protected:
//用作音视频同步
void Main();
std::mutex mux;
IPlayer(){};
};
#endif //XPLAY_IPLAYER_H
#define XPLAY_IDEMUX_H
#include "XData.h"
#include "XThread.h"
#include "IObserver.h"
#include "XParameter.h"
//解封装接口
class IDemux: public IObserver {
public:
//打开文件,或者流媒体 rmtp http rtsp
virtual bool Open(const char *url) = 0;
//seek 位置 pos 0.0~1.0
virtual bool Seek(double pos) = 0;
virtual void Close() = 0;
//获取视频参数
virtual XParameter GetVPara() = 0;
//获取音频参数
virtual XParameter GetAPara() = 0;
//读取一帧数据,数据由调用者清理
virtual XData Read() = 0;
//总时长(毫秒)
int totalMs = 0;
protected:
virtual void Main();
};
#endif //XPLAY_IDEMUX_H
#include "IDemux.h"
#include "IDecode.h"
#include "IAudioPlay.h"
#include "IVideoView.h"
#include "IResample.h"
#include "XLog.h"
IPlayer *IPlayer::Get(unsigned char index)
{
static IPlayer p[256];
return &p[index];
}
void IPlayer::Main()
{
while (!isExit)
{
mux.lock();
if(!audioPlay|| !vdecode)
{
mux.unlock();
XSleep(2);
continue;
}
//同步
//获取音频的pts 告诉视频
int apts = audioPlay->pts;
//XLOGE("apts = %d",apts);
vdecode->synPts = apts;
mux.unlock();
XSleep(2);
}
}
void IPlayer::Close()
{
mux.lock();
//2 先关闭主体线程,再清理观察者
//同步线程
XThread::Stop();
//解封装
if(demux)
demux->Stop();
//解码
if(vdecode)
vdecode->Stop();
if(adecode)
adecode->Stop();
//2 清理缓冲队列
if(vdecode)
vdecode->Clear();
if(adecode)
adecode->Clear();
if(audioPlay)
audioPlay->Clear();
//3 清理资源
if(audioPlay)
audioPlay->Close();
if(videoView)
videoView->Close();
if(vdecode)
vdecode->Close();
if(adecode)
adecode->Close();
if(demux)
demux->Close();
mux.unlock();
}
double IPlayer::PlayPos()
{
double pos = 0.0;
mux.lock();
int total = 0;
if(demux)
total = demux->totalMs;
if(total>0)
{
if(vdecode)
{
pos = (double)vdecode->pts/(double)total;
}
}
mux.unlock();
return pos;
}
bool IPlayer::Seek(double pos)
{
bool re = false;
mux.lock();
if(demux)
re = demux->Seek(pos);
mux.unlock();
return re;
}
bool IPlayer::Open(const char *path)
{
Close();
mux.lock();
//解封装
if(!demux || !demux->Open(path))
{
mux.unlock();
XLOGE("demux->Open %s failed!",path);
return false;
}
//解码 解码可能不需要,如果是解封之后就是原始数据
if(!vdecode || !vdecode->Open(demux->GetVPara(),isHardDecode))
{
XLOGE("vdecode->Open %s failed!",path);
//return false;
}
if(!adecode || !adecode->Open(demux->GetAPara()))
{
XLOGE("adecode->Open %s failed!",path);
//return false;
}
//重采样 有可能不需要,解码后或者解封后可能是直接能播放的数据
//if(outPara.sample_rate <= 0)
outPara = demux->GetAPara();
if(!resample || !resample->Open(demux->GetAPara(),outPara))
{
XLOGE("resample->Open %s failed!",path);
}
mux.unlock();
return true;
}
bool IPlayer::Start()
{
mux.lock();
if(vdecode)
vdecode->Start();
if(!demux || !demux->Start())
{
mux.unlock();
XLOGE("demux->Start failed!");
return false;
}
if(adecode)
adecode->Start();
if(audioPlay)
audioPlay->StartPlay(outPara);
XThread::Start();
mux.unlock();
return true;
}
void IPlayer::InitView(void *win)
{
if(videoView)
{
videoView->Close();
videoView->SetRender(win);
}
}
#define XPLAY_IPLAYER_H
#include <mutex>
#include "XThread.h"
#include "XParameter.h"
class IDemux;
class IAudioPlay;
class IVideoView;
class IResample;
class IDecode;
class IPlayer : public XThread
{
public:
static IPlayer *Get(unsigned char index=0);
virtual bool Open(const char *path);
virtual void Close();
virtual bool Start();
virtual void InitView(void *win);
//获取当前的播放进度 0.0 ~ 1.0
virtual double PlayPos();
virtual bool Seek(double pos);
//是否视频硬解码
bool isHardDecode = true;
//音频输出参数配置
XParameter outPara;
IDemux *demux = 0;
IDecode *vdecode = 0;
IDecode *adecode = 0;
IResample *resample = 0;
IVideoView *videoView = 0;
IAudioPlay *audioPlay = 0;
protected:
//用作音视频同步
void Main();
std::mutex mux;
IPlayer(){};
};
#endif //XPLAY_IPLAYER_H
#include "FFPlayerBuilder.h"
void IPlayerPorxy::Close()
{
mux.lock();
if(player)
player->Close();
mux.unlock();
}
void IPlayerPorxy::Init(void *vm)
{
mux.lock();
if(vm)
{
FFPlayerBuilder::InitHard(vm);
}
if(!player)
player = FFPlayerBuilder::Get()->BuilderPlayer();
mux.unlock();
}
//获取当前的播放进度 0.0 ~ 1.0
double IPlayerPorxy::PlayPos()
{
double pos = 0.0;
mux.lock();
if(player)
{
pos = player->PlayPos();
}
mux.unlock();
return pos;
}
bool IPlayerPorxy::Seek(double pos)
{
bool re = false;
mux.lock();
if(player)
{
re = player->Seek(pos);
}
mux.unlock();
return re;
}
bool IPlayerPorxy::Open(const char *path)
{
bool re = false;
mux.lock();
if(player)
{
player->isHardDecode = isHardDecode;
re = player->Open(path);
}
mux.unlock();
return re;
}
bool IPlayerPorxy::Start()
{
bool re = false;
mux.lock();
if(player)
re = player->Start();
mux.unlock();
return re;
}
void IPlayerPorxy::InitView(void *win)
{
mux.lock();
if(player)
player->InitView(win);
mux.unlock();
}
#define XPLAY_IPLAYERPORXY_H
#include "IPlayer.h"
#include <mutex>
class IPlayerPorxy: public IPlayer
{
public:
static IPlayerPorxy*Get()
{
static IPlayerPorxy px;
return &px;
}
void Init(void *vm = 0);
virtual bool Open(const char *path);
virtual bool Seek(double pos);
virtual void Close();
virtual bool Start();
virtual void InitView(void *win);
//获取当前的播放进度 0.0 ~ 1.0
virtual double PlayPos();
protected:
IPlayerPorxy(){}
IPlayer *player = 0;
std::mutex mux;
};
#endif //XPLAY_IPLAYERPORXY_H #include <jni.h>
#include <string>
#include <android/native_window_jni.h>
#include "XLog.h"
#include "IPlayerPorxy.h"
extern "C"
JNIEXPORT
jint JNI_OnLoad(JavaVM *vm,void *res)
{
IPlayerPorxy::Get()->Init(vm);
/*IPlayerPorxy::Get()->Open("/sdcard/v1080.mp4");
IPlayerPorxy::Get()->Start();
IPlayerPorxy::Get()->Open("/sdcard/1080.mp4");
IPlayerPorxy::Get()->Start();*/
return JNI_VERSION_1_4;
}
extern "C"
JNIEXPORT jdouble JNICALL
Java_com_example_xplay_MainActivity_PlayPos(JNIEnv *env, jobject thiz) {
return IPlayerPorxy::Get()->PlayPos();
}extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_xplay_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) {
// TODO: implement stringFromJNI()
}extern "C"
JNIEXPORT void JNICALL
Java_com_example_xplay_OpenUrl_Open(JNIEnv *env, jobject thiz, jstring url_) {
const char *url = env->GetStringUTFChars(url_, 0);
IPlayerPorxy::Get()->Open(url);
IPlayerPorxy::Get()->Start();
//IPlayerPorxy::Get()->Seek(0.5);
env->ReleaseStringUTFChars(url_, url);
}extern "C"
JNIEXPORT void JNICALL
Java_com_example_xplay_XPlay_InitView(JNIEnv *env, jobject thiz, jobject surface) {
ANativeWindow *win = ANativeWindow_fromSurface(env,surface);
IPlayerPorxy::Get()->InitView(win);
}extern "C"
JNIEXPORT void JNICALL
Java_com_example_xplay_MainActivity_Seek(JNIEnv *env, jobject thiz, jdouble pos) {
IPlayerPorxy::Get()->Seek(pos);
}
而在MainActivity.java中則也要處理下與seek相關的java編碼:
package com.example.xplay;;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements Runnable,SeekBar.OnSeekBarChangeListener {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary( "native-lib" );
}
private Button bt;
private SeekBar seek;
private Thread th;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
//去掉标题栏
supportRequestWindowFeature( Window.FEATURE_NO_TITLE);
//全屏,隐藏状态
getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN ,
WindowManager.LayoutParams.FLAG_FULLSCREEN
);
//屏幕为横屏
setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE );
setContentView( R.layout.activity_main );
bt = findViewById( R.id.open_button );
seek = findViewById( R.id.aplayseek );
seek.setMax(1000);
seek.setOnSeekBarChangeListener( this );
bt.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.e("XPlay","open button click!");
//打开选择路径窗口
Intent intent = new Intent();
intent.setClass( MainActivity.this ,OpenUrl.class);
startActivity( intent );
}
} );
//启动播放进度线程
th = new Thread(this);
th.start();
}
//播放进度显示
@Override
public void run() {
for(;;)
{
seek.setProgress((int)(PlayPos()*1000));
try {
Thread.sleep( 40 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public native double PlayPos();
public native void Seek(double pos);
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Seek( (double)seekBar.getProgress()/(double)seekBar.getMax() );
}
}
关键字词:IDemux
相关文章
-
无相关信息