/*
本播放器主要是解决 从优酷上播放视频。 是有多段网络视频组成一个完整视频。
解决方案,开辟两个线程,一个线程从网络中读取数据包放入缓冲池(视频缓冲池和音频缓冲池)
一个线程从音频缓冲池读取数据播放。一个从视频缓冲池中读取播放.
难点1:av_read_frame是读取packet(包) 数据, 几包数据 组成avframe(帧)
音频帧转换成byte[] 存储起来 放入缓冲池 吃音频byte[]可以直接放入音频流中播放
视频帧也是byte[] 存储起来,此视频byte[]数组可以转换为图片 PIX_FMT_RGB24
为了同步音视频,我们把没帧的最后一包的pts记录下来放入缓冲区
*/
#include "stdafx.h"
#include "BonkerPlayer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
#include <libavutil/avstring.h>
//SDL
#include "sdl/SDL.h"
#include "sdl/SDL_thread.h"
};
#define VideoBufferMaxSize 80//2048 //视频缓冲区最大值,大于此值 则不下载数据
#define VideoBufferMinSize 20//1024 //视频缓冲区最小值,小于此值,则唤醒下载
#define AudioBufferMaxSize 80//2048 //音频缓冲区最大值,大于此值 则不下载数据
#define AudioBufferMinSize 20//1024 //音频缓冲区最小值,小于此值,则唤醒下载
#define SDL_AUDIO_BUFFER_SIZE 1024 //音频流的缓冲区
//#define VideoType PIX_FMT_YUV420P //视频转换的格式
#define VideoType PIX_FMT_BGR24 //视频转换的格式
#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
//static char ErrorMsg[100]="";//错误的提示信息
int FileDuration=0; //视频的长度 单位秒
int flag=100; //标识 播放,暂停,退出 0退出,1标识,2暂停
//声明了函数指针
DispalyVideoDele Fn=NULL;
Uint32 audio_len;
Uint8 *audio_pos;
double currentAudioClock=0; //当前音频播放时间
double currentVideoClock=0; //当前视频播放时间
double currentBufferClock=0; //当前以缓冲的时间,用于缓冲进度条
//double currentPlayClock=0;//当前播放的时间,用于播放进度条
double diffClock=0.2; //音视频相差的死区
int CurrentVolume=SDL_MIX_MAXVOLUME/2; //当前声音的大小
SDL_Thread *decodeTid=NULL; //解码线程
SDL_Thread *PlayVideoTid=NULL; //视频播放线程
SDL_Thread *PlayAudioTid=NULL; //音频播放线程
//快进的参数
bool isSeek= false ; //是否在快进
int global_seek_index=0; //文件索引 快进
double globle_seek_pos=0; //快进的地方
//存储音频的队列
typedef struct AudioItem
{
Uint8 *AudioData; //音频数据
int Length; //音频长度
double Pts; //时间戳
AudioItem *Next; //尾部
SDL_AudioSpec *wanted_spec;
}AudioQueueItem;
typedef struct
{
AudioQueueItem *FirstItem; //队列头
AudioQueueItem *LastItem; //队列位
int Length; //队列长度
SDL_mutex *audioMutex; //用于同步两个线程同时操作队列的 互斥量
SDL_cond *audioCond; //唤醒线程
}AudioQueue;
//存储视频的队列
typedef struct VideoItem
{
Uint8 *VideoData; //音频数据
int Width; //视频图片的宽度
int Height; //视频图片的高度
int Length; //视频长度
double Pts; //时间戳
VideoItem *Next; //尾部
}VideoQueueItem;
typedef struct
{
VideoQueueItem *FirstItem; //队列头
VideoQueueItem *LastItem; //队列位
int Length; //队列长度
double BufferPts; //缓冲的pts
SDL_mutex *videoMutex; //用于同步两个线程同时操作队列的 互斥量
SDL_cond *videoCond; //唤醒线程
}VideoQueue;
VideoQueue *videoQueue=NULL; //视频队列
AudioQueue *audioQueue=NULL; //音频队列
//清空视频队列
void VideoQueueClear(VideoQueue *vq)
{
VideoItem *item,*temp;
SDL_LockMutex(vq->videoMutex);
for (item=vq->FirstItem; item!=NULL; item=temp)
{
temp=item->Next; //
av_free(item->VideoData); //释放video里面的数据
av_free(item);
vq->Length--;
}
vq->FirstItem=NULL;
vq->LastItem=NULL;
SDL_UnlockMutex(vq->videoMutex);
}
//清空音频队列
void AudioQueueClear(AudioQueue *aq)
{
AudioItem *item,*temp;
SDL_LockMutex(aq->audioMutex);
for (item=aq->FirstItem; item!=NULL; item=temp)
{
temp=item->Next; //
av_free(item->AudioData); //释放video里面的数据
av_free(item->wanted_spec);
av_free(item);
aq->Length--;
}
aq->FirstItem=NULL;
aq->LastItem=NULL;
SDL_UnlockMutex(aq->audioMutex);
}
//初始化视频队列
void VideoQueueInit(VideoQueue *vq)
{
memset (vq, 0, sizeof (VideoQueue)); //初始化首地址为0
vq->videoMutex=SDL_CreateMutex();
vq->videoCond=SDL_CreateCond();
}
//初始化音频队列
void AudioQueueInit(AudioQueue *aq)
{
memset (aq,0, sizeof (AudioQueue));
aq->audioMutex=SDL_CreateMutex();
aq->audioCond=SDL_CreateCond();
}
//向队列添加数据
int VideoQueuePut(VideoQueue *vq,VideoQueueItem *item)
{
int result=0;
SDL_LockMutex(vq->videoMutex); //加锁
if (vq->Length<VideoBufferMaxSize)
{
if (!vq->FirstItem) //第一个item为null 则队列是空的
{
vq->FirstItem=item;
vq->LastItem=item;
vq->Length=1;
vq->BufferPts=item->Pts;
}
else
{
vq->LastItem->Next=item; //添加到队列后面
vq->Length++;
vq->LastItem=item; //此item变成队列尾部
vq->BufferPts=item->Pts;
}
if (vq->Length>=VideoBufferMinSize)
{
SDL_CondSignal(vq->videoCond); //唤醒其他线程 如果缓冲区里面有几个数据后再唤醒 较好
}
result=1;
}
else
{
SDL_CondWait(vq->videoCond,vq->videoMutex); //解锁 等待被唤醒
}
SDL_UnlockMutex(vq->videoMutex); //解锁
return result;
}
//向队列中取出数据,放入item中
int VideoQueueGet(VideoQueue *vq,VideoQueueItem *item)
{
int result=0;
SDL_LockMutex(vq->videoMutex);
if (vq->Length>0)
{
if (vq->FirstItem) //有数据
{
*item=*(vq->FirstItem);
if (!vq->FirstItem->Next) //只有一个
{
vq->FirstItem=NULL;
vq->LastItem=NULL;
} else
{
vq->FirstItem=vq->FirstItem->Next;
}
vq->Length--;
item->Next=NULL;
result= 1;
}
if (vq->Length<=VideoBufferMinSize)
{
SDL_CondSignal(vq->videoCond); //唤醒下载线程
}
}
else
{
SDL_CondWait(vq->videoCond,vq->videoMutex); //解锁 等待被唤醒
}
SDL_UnlockMutex(vq->videoMutex);
return result;
}
//向队列添加数据
int AudioQueuePut(AudioQueue *aq,AudioQueueItem *item)
{
int result=0;
SDL_LockMutex(aq->audioMutex); //加锁
if (aq->Length<AudioBufferMaxSize)
{
if (!aq->FirstItem) //第一个item为null 则队列是空的
{
aq->FirstItem=item;
aq->LastItem=item;
aq->Length=1;
}
else
{
aq->LastItem->Next=item; //添加到队列后面
aq->Length++;
aq->LastItem=item; //此item变成队列尾部
}
if (aq->Length>=AudioBufferMinSize)
{
SDL_CondSignal(aq->audioCond); //唤醒其他线程 如果缓冲区里面有几个数据后再唤醒 较好
}
result=1;
}
else ///音频缓冲区的大小 大于设定值 则让线程等待
{
SDL_CondWait(aq->audioCond,aq->audioMutex); //解锁 等待被唤醒
}
SDL_UnlockMutex(aq->audioMutex); //解锁
return result;
}
//向队列中取出数据,放入item中
int AudioQueueGet(AudioQueue *aq,AudioQueueItem *item)
{
int result=0;
SDL_LockMutex(aq->audioMutex);
if (aq->Length>0)
{
if (aq->FirstItem) //有数据
{
*item=*(aq->FirstItem);
if (!aq->FirstItem->Next) //只有一个
{
aq->FirstItem=NULL;
aq->LastItem=NULL;
} else
{
aq->FirstItem=aq->FirstItem->Next;
}
aq->Length--;
item->Next=NULL;
result=1;
}
if (aq->Length<=AudioBufferMinSize)
{
SDL_CondSignal(aq->audioCond); //唤醒下载线程
}
} else
{
SDL_CondWait(aq->audioCond,aq->audioMutex); //解锁 等待被唤醒
}
SDL_UnlockMutex(aq->audioMutex);
return result;
}
//输出声音的回调函数
void AudioCallback( void *udata,Uint8 *stream, int len)
{
//SDL 2.0
SDL_memset(stream, 0, len);
if (audio_len==0) /* Only play if we have data left */
return ;
len=(len>audio_len?audio_len:len); /* Mix as much data as possible */
SDL_MixAudio(stream,audio_pos,len,CurrentVolume);
audio_pos += len;
audio_len -= len;
}
//下载视频和音频流并 解码 并放入相应的队列中
int DecodePacket( void *arg)
{
VideoState *vs=(VideoState *)arg;
int length=vs->Length;
double currentAllFilePts=0;
av_register_all(); //注册所有解码器
avformat_network_init(); //初始化流媒体格式
for ( int j = 0; j < length; j++)
{
double currentFilePts=0;
char * url=vs->Urls[j];
AVFormatContext *pFormatCtx;
pFormatCtx = avformat_alloc_context();
//打卡文件
if (avformat_open_input(&pFormatCtx,url,NULL,NULL)!=0)
{
//strcpy(ErrorMsg,"无法打开网络流");
return -1;
}
//avformat_close_input
if (av_find_stream_info(pFormatCtx)<0)
{
//strcpy(ErrorMsg,"无法获取流信息");
return -1;
}
//获取此视频的总时间 微妙转化为妙
//FileDuration+= pFormatCtx->duration/1000000;
//把一个文件拆分为视频流和音频流
int videoIndex=-1,audioIndex=-1;
int i=0;
//获取音频流和视频流的索引
for (i=0; i<pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoIndex=i;
}
else if (pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
{
audioIndex=i;
}
}
AVCodecContext *pCodecCtx,*aCodecCtx; //视频,音频的解码器上下文
AVCodec *pCodec,*aCodec; //视频,音频解码器
if (videoIndex!=-1)
{
//视频解码器上下文,
pCodecCtx=pFormatCtx->streams[videoIndex]->codec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
}
else
{
}
if (audioIndex!=-1)
{
//音频解码器上下文
aCodecCtx=pFormatCtx->streams[audioIndex]->codec;
aCodec=avcodec_find_decoder(aCodecCtx->codec_id);
}
else
{
}
if (videoIndex!=-1)
{
//打开解码器
if (avcodec_open2(pCodecCtx, pCodec,NULL)<0)
{
//strcpy(ErrorMsg,"无法打开视频解码器");
return -1;
}
}
else
{
}
if (audioIndex!=-1)
{
if (avcodec_open2(aCodecCtx, aCodec,NULL)<0)
{
//strcpy(ErrorMsg,"无法打开音频解码器");
return -1;
}
}
else
{
}
AVPacket *packet=(AVPacket *)av_mallocz( sizeof (AVPacket));
AVFrame *pFrame=avcodec_alloc_frame();
AVFrame *pFrameRGB=avcodec_alloc_frame();
int frameFinished=0; //是否凑成一帧数据
int result=0; //标识一个视频是否解码完毕
int audioLength=0; //音频数组的长度
int videoLength=0; //视频数组的长度
//把视频帧转化为数组参数
struct SwsContext *img_convert_ctx;
if (videoIndex!=-1)
{
videoLength=avpicture_get_size(VideoType, pCodecCtx->width, pCodecCtx->height);
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, VideoType, SWS_BICUBIC, NULL, NULL, NULL);
}
//把音频帧转化为数组的参数
//uint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;
AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;
int out_sample_rate=44100;
int64_t in_channel_layout=av_get_channel_layout_nb_channels(aCodecCtx->channels);
int out_channels=av_get_channel_layout_nb_channels(aCodecCtx->channels);
int out_nb_samples=1024;
audioLength=av_samples_get_buffer_size(NULL,out_channels ,out_nb_samples,out_sample_fmt, 1);
struct SwrContext *au_convert_ctx;
au_convert_ctx = swr_alloc();
au_convert_ctx=swr_alloc_set_opts(au_convert_ctx,aCodecCtx->channels, out_sample_fmt, out_sample_rate,
in_channel_layout,aCodecCtx->sample_fmt , aCodecCtx->sample_rate,0, NULL);
swr_init(au_convert_ctx);
int sample=SDL_AUDIO_BUFFER_SIZE;
//解码一包数据,一帧数据有多包
while (flag!=0&&av_read_frame(pFormatCtx, packet)>=0)
{
if (isSeek) //要快进
{
//做快进
if (j==global_seek_index)
{
int seekFlag=avformat_seek_file(pFormatCtx, -1, (globle_seek_pos-10)* AV_TIME_BASE, globle_seek_pos * AV_TIME_BASE, (globle_seek_pos+10)* AV_TIME_BASE, AVSEEK_FLAG_ANY);
if (seekFlag>=0)
{
currentAllFilePts=0;
for ( int k = 0; k < j; k++)
{
currentAllFilePts+=vs->times[k];
}
}
//av_seek_frame(pFormatCtx, -1 , globle_seek_pos * AV_TIME_BASE, AVSEEK_FLAG_ANY);
isSeek= false ;
} else
{
j=global_seek_index-1;
break ;
}
}
if (flag==0) //退出
{
break ;
} else if (flag==1) //播放
{
} else if (flag==2)
{
SDL_Delay(1);
continue ;
}
frameFinished=0;
//视频数据包 添加到视频队列中
if (packet->stream_index==videoIndex)
{
//把数据包转换为数据帧
result=avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,packet);
double pts=0;
if (packet->dts == AV_NOPTS_VALUE
&& pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) {
pts = *(uint64_t *)pFrame->opaque;
} else if (packet->dts != AV_NOPTS_VALUE) {
pts = packet->dts;
} else {
pts = 0;
}
pts *= av_q2d(pFormatCtx->streams[videoIndex]->time_base);
//printf("+readVideo %d\n",videoQueue->Length);
if (result<0) //一个视频解码结束了
{
break ; //跳出循环,继续解码下一个视频
}
if (frameFinished) //解析成了一帧数据,转化为字节数组存放队列中
{
uint8_t *bufferRGB=(uint8_t *)av_mallocz(videoLength);
avpicture_fill((AVPicture *)pFrameRGB, bufferRGB, VideoType, pCodecCtx->width, pCodecCtx->height);
sws_scale(img_convert_ctx, ( const uint8_t* const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
//创建视频item
VideoQueueItem *videoItem;
videoItem=(VideoQueueItem *)av_mallocz( sizeof (VideoQueueItem));
videoItem->Height=pCodecCtx->height;
videoItem->Width=pCodecCtx->width;
videoItem->VideoData=bufferRGB;
//videoItem->Length=videoLength;
//videoItem->VideoData=pFrameRGB->data[0];
videoItem->Length=pFrameRGB->linesize[0];
//获取显示时间戳pts
currentFilePts=pts;
videoItem->Pts = currentAllFilePts+currentFilePts; //音频绝对pts;
videoItem->Next=NULL;
//添加到队列中
while (flag!=0&&!VideoQueuePut(videoQueue,videoItem));
//av_free(bufferRGB);//释放
}
} //音频数据包 ,添加到音频队列中
else if (packet->stream_index==audioIndex)
{
result= avcodec_decode_audio4( aCodecCtx, pFrame,&frameFinished, packet);
double pts=0;
if (packet->dts == AV_NOPTS_VALUE
&& pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) {
pts = *(uint64_t *)pFrame->opaque;
} else if (packet->dts != AV_NOPTS_VALUE) {
pts = packet->dts;
} else {
pts = 0;
}
pts *= av_q2d(pFormatCtx->streams[videoIndex]->time_base);
//printf("+readAudio %d\n",audioQueue->Length);
if (result<0) //一个视频解码结束了
{
break ; //跳出循环,继续解码下一个视频
}
if (frameFinished) //解析成了一帧数据,转化为字节数组存放队列中
{
uint8_t *out_buffer=(uint8_t *)av_mallocz(MAX_AUDIO_FRAME_SIZE*2);
swr_convert(au_convert_ctx,&out_buffer, MAX_AUDIO_FRAME_SIZE,( const uint8_t **)pFrame->data , pFrame->nb_samples);
//创建音频Item
AudioItem *audioItem;
audioItem=(AudioItem *)av_mallocz( sizeof (AudioItem));
audioItem->AudioData=out_buffer;
audioItem->Length=audioLength;
//获取显示时间戳pts
currentFilePts=pts;
audioItem->Pts =currentAllFilePts+currentFilePts; //音频绝对pts
SDL_AudioSpec *wanted_spec=(SDL_AudioSpec *)av_mallocz( sizeof (SDL_AudioSpec));; //音频设置
//初始化音频设置
wanted_spec->silence = 0;
wanted_spec->samples = sample;
wanted_spec->format = AUDIO_S16SYS;
wanted_spec->freq =aCodecCtx->sample_rate;
wanted_spec->channels = out_channels;
wanted_spec->userdata = aCodecCtx;
wanted_spec->callback = AudioCallback;
if (wanted_spec->samples!=pFrame->nb_samples){
//SDL_CloseAudio();
out_nb_samples=pFrame->nb_samples;
audioLength=av_samples_get_buffer_size(NULL,out_channels ,out_nb_samples,out_sample_fmt, 1);
wanted_spec->samples=out_nb_samples;
wanted_spec->freq=aCodecCtx->sample_rate;
//SDL_OpenAudio(&wanted_spec, NULL);
}
audioItem->wanted_spec=wanted_spec;
//添加到队列中
audioItem->Next=NULL;
while (flag!=0&&!AudioQueuePut(audioQueue,audioItem));
}
}
av_free_packet(packet); //释放内存
}
av_free(img_convert_ctx);
av_free(au_convert_ctx);
av_free(pFrame);
av_free(pFrameRGB);
avcodec_close(pCodecCtx);
avcodec_close(aCodecCtx);
avformat_close_input(&pFormatCtx);
currentAllFilePts+=currentFilePts; //把这一段视频地址 累加
if (flag==0)
{
return 1;
}
}
avformat_network_deinit();
flag=3; //解码结束了
return 1;
}
//播放视频线程
int PlayVideo( void *arg)
{
while (flag!=0&& true )
{
if (flag==2) // 暂停
{
SDL_Delay(1);
continue ;
} else if (flag==0) //退出
{
return -1;
} else if (flag==1) //播放
{
} else if (flag==3) //解码结束了
{
//播放结束了
if (audioQueue->Length<=0&&videoQueue->Length<=0)
{
break ;
}
}
//视频快于音频 则等待
if (currentVideoClock>=currentAudioClock+diffClock)
{
//音频队中有数据,这样判断是因为,当只有视频时,视频满了,就可以播放了
if (audioQueue->Length>0&&videoQueue->Length<VideoBufferMaxSize)
{
SDL_Delay(1);
continue ;
}
}
VideoItem *videoItem=(VideoItem *)av_mallocz( sizeof (VideoItem));
//从队列中拿出视频数据
if (VideoQueueGet( videoQueue,videoItem))
{
currentVideoClock=videoItem->Pts; //当前视频时间戳
if (Fn)
{
Fn((unsigned char *)videoItem->VideoData,videoItem->Width,videoItem->Height,videoItem->Pts,videoQueue->BufferPts);
}
av_free(videoItem->VideoData);
}
av_free(videoItem);
}
return 1;
}
//播放音频线程
int PlayAudio( void *arg)
{
if (SDL_Init( SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf ( "Could not initialize SDL - %s\n" , SDL_GetError());
return -1;
}
bool isOpenAudio= false ;
int samples=0;
SDL_AudioSpec spec;
while ( true &&flag!=0)
{
if (flag==2) // 暂停
{
SDL_Delay(1);
continue ;
} else if (flag==0) //退出
{
return -1;
} else if (flag==1) //播放
{
} else if (flag==3) //解码结束了
{
//播放结束了
if (audioQueue->Length<=0&&videoQueue->Length<=0)
{
break ;
}
}
//音频快于视频 则加锁
if (currentAudioClock>=currentVideoClock+diffClock)
{
if (videoQueue->Length>0&&audioQueue->Length<AudioBufferMaxSize)
{
SDL_Delay(1);
continue ;
}
}
AudioItem *audioItem=(AudioItem *)av_mallocz( sizeof (AudioItem));
//从队列中拿出音频数据
if ( AudioQueueGet( audioQueue,audioItem))
{
if (!isOpenAudio)
{
SDL_CloseAudio();
int re=SDL_OpenAudio(audioItem->wanted_spec, &spec);
samples=audioItem->wanted_spec->samples;
isOpenAudio= true ;
}
else
{
if (audioItem==NULL)
{
continue ;
}
if (samples!=audioItem->wanted_spec->samples)
{
SDL_CloseAudio();
int re=SDL_OpenAudio(audioItem->wanted_spec, &spec);
samples=audioItem->wanted_spec->samples;
isOpenAudio= true ;
}
}
currentAudioClock=audioItem->Pts; //当前音频时间戳
audio_pos=audioItem->AudioData;
audio_len=audioItem->Length;
SDL_PauseAudio(0);
while (audio_len>0&&flag!=0)
SDL_Delay(5);
av_free(audioItem->AudioData);
av_free(audioItem->wanted_spec);
}
av_free(audioItem);
}
SDL_CondSignal(audioQueue->audioCond); //唤醒其他线程 如果缓冲区里面有几个数据后再唤醒 较好
SDL_CondSignal(videoQueue->videoCond); //唤醒其他线程 如果缓冲区里面有几个数据后再唤醒 较好
return 1;
}
int _tmain1(VideoState *vs)
{
currentAudioClock=0;
currentVideoClock=0;
currentBufferClock=0;
//currentPlayClock=0;
CurrentVolume=SDL_MIX_MAXVOLUME/2;
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
fprintf (stderr, "Unable to initialize SDL: %s\n" , SDL_GetError());
return 1;
}
atexit (SDL_Quit);
//atexit(SDL_Quit);// 注册SDL_Quit,当退出时调用,使得退出时程序自动清理
//flag=2;
//给音视频队列分配空间
videoQueue=(VideoQueue *)av_mallocz( sizeof (VideoQueue));
audioQueue=(AudioQueue *)av_mallocz( sizeof (AudioQueue));
//初始化音视频队列
VideoQueueInit(videoQueue);
AudioQueueInit(audioQueue);
decodeTid=SDL_CreateThread(DecodePacket, "DecodePacket" ,vs);
PlayVideoTid=SDL_CreateThread(PlayVideo, "PlayVideo" ,NULL);
PlayAudioTid=SDL_CreateThread(PlayAudio, "PlayAudioTid" ,NULL);
return 1;
}
//获取视频的总长度
void InitAllTime(VideoState *vs)
{
FileDuration=0;
int length=vs->Length;
av_register_all(); //注册所有解码器
avformat_network_init(); //初始化流媒体格式
for ( int j = 0; j < length; j++)
{
char * url=vs->Urls[j];
AVFormatContext *pFormatCtx = avformat_alloc_context();
//打卡文件
if (avformat_open_input(&pFormatCtx,url,NULL,NULL)!=0)
{
//strcpy(ErrorMsg,"无法打开网络流");
return ;
}
if (av_find_stream_info(pFormatCtx)<0)
{
//strcpy(ErrorMsg,"无法获取流信息");
return ;
}
//保存每个文件的播放长度
vs->times[j]=pFormatCtx->duration/1000000;
//获取此视频的总时间 微妙转化为妙
FileDuration+= vs->times[j];
avformat_close_input(&pFormatCtx);
}
avformat_network_deinit();
}
void bonker_pause()
{
flag=2;
}
VideoState *_vs;
//获取视频总长度
double bonker_gettime()
{
FileDuration=0;
if (_vs!=NULL)
{
InitAllTime(_vs);
}
return FileDuration;
}
void bonker_open()
{
if (_vs!=NULL)
{
_tmain1(_vs);
} else
{
}
}
void bonker_play()
{
flag=1;
}
void bonker_quit()
{
bonker_close();
}
void bonker_init(DispalyVideoDele _fn)
{
FileDuration=0; //视频的长度 单位秒
flag=2; //标识 播放,暂停,退出 0退出,1标识,2暂停
audio_len=0;
currentAudioClock=0; //当前音频播放时间
currentVideoClock=0; //当前视频播放时间
currentBufferClock=0; //当前以缓冲的时间,用于缓冲进度条
//currentPlayClock=0;//当前播放的时间,用于播放进度条
diffClock=0.2; //音视频相差的死区
CurrentVolume=SDL_MIX_MAXVOLUME/2; //当前声音的大小
//快进的参数
isSeek= false ; //是否在快进
global_seek_index=0; //文件索引 快进
globle_seek_pos=0; //快进的地方
Fn=_fn;
}
void bonker_addurl( char *vs)
{
if (_vs==NULL)
{
_vs=(VideoState *)av_mallocz( sizeof (VideoState));
_vs->Length=0;
}
av_strlcpy(_vs->Urls[_vs->Length],vs,UrlLength);
_vs->Length++;
}
//释放内存资源
void bonker_close()
{
flag=0; //退出
SDL_CloseAudio();
if (videoQueue!=NULL)
{
SDL_CondSignal(videoQueue->videoCond);
}
if (audioQueue!=NULL)
{
SDL_CondSignal(audioQueue->audioCond);
}
SDL_Delay(10);
SDL_WaitThread(PlayVideoTid,NULL);
SDL_WaitThread(PlayAudioTid,NULL);
SDL_WaitThread(decodeTid,NULL);
if (videoQueue!=NULL)
{
VideoQueueClear(videoQueue); //释放视频队列
//videoQueue=NULL;
}
if (audioQueue!=NULL)
{
AudioQueueClear(audioQueue);
//audioQueue=NULL;
}
SDL_DestroyMutex(audioQueue->audioMutex);
SDL_DestroyCond(audioQueue->audioCond);
SDL_DestroyMutex(videoQueue->videoMutex);
SDL_DestroyCond(videoQueue->videoCond);
av_free(audioQueue);
av_free(videoQueue);
flag=2;
/*SDL_DetachThread(decodeTid);
SDL_DetachThread(PlayVideoTid);
SDL_DetachThread(PlayAudioTid);*/
if (_vs!=NULL)
{
av_free(_vs);
}
_vs=NULL;
SDL_Quit();
//关闭解码线程,视频播放线程,音频播放线程
/*if(decodeTid!=NULL)
{
decodeTid=NULL;
}
if(PlayVideoTid!=NULL)
{
PlayVideoTid=NULL;
}
if(PlayAudioTid!=NULL)
{
PlayAudioTid=NULL;
}*/
}
//设置声音
void bonker_set_volumn( int volume)
{
if (volume>=0&&volume<=128)
{
CurrentVolume=volume;
}
}
//获取音量
int bonker_get_volume()
{
return CurrentVolume;
}
//快进 快退
void bonker_seek( double seek_pos)
{
bool flagTemp=flag;
flag=2;
SDL_Delay(50);
//当快进的时间在缓冲区内
if (seek_pos>=currentVideoClock&&seek_pos<=videoQueue->BufferPts)
{
//清空之前的音频
AudioItem *item,*temp;
SDL_LockMutex(audioQueue->audioMutex);
for (item=audioQueue->FirstItem; item!=NULL; item=temp)
{
temp=item->Next; //
av_free(item->AudioData); //释放video里面的数据
av_free(item->wanted_spec);
av_free(item);
audioQueue->Length--;
if (temp!=NULL)
{
if (temp->Pts>=seek_pos) //目前缓冲区,大于此跳转位置
{
break ;
}
} else
{
audioQueue->FirstItem=NULL;
audioQueue->LastItem=NULL;
}
}
SDL_UnlockMutex(audioQueue->audioMutex);
//清空之前的视频
VideoItem *item1,*temp1;
SDL_LockMutex(videoQueue->videoMutex);
for (item1=videoQueue->FirstItem; item1!=NULL; item1=temp1)
{
temp1=item1->Next; //
av_free(item1->VideoData); //释放video里面的数据
av_free(item1);
videoQueue->Length--;
if (temp1!=NULL)
{
if (temp1->Pts>=seek_pos) //目前缓冲区,大于此跳转位置
{
break ;
}
}
else
{
videoQueue->FirstItem=NULL;
videoQueue->LastItem=NULL;
}
}
SDL_UnlockMutex(videoQueue->videoMutex);
}
else if (seek_pos>=0&&seek_pos<=FileDuration) //用av_seek_file,计算那个文件,
{
double pos=0;
for ( int i = 0; i < _vs->Length; i++)
{
pos+=_vs->times[i];
if (pos>seek_pos) //就在这个文件内
{
isSeek= true ;
global_seek_index=i;
globle_seek_pos=seek_pos- pos+_vs->times[i]; //此文件快进到的位置
break ;
}
}
//清空缓冲区
VideoQueueClear(videoQueue);
AudioQueueClear(audioQueue);
}
flag=flagTemp;
SDL_CondSignal(audioQueue->audioCond); //唤醒其他线程 如果缓冲区里面有几个数据后再唤醒 较好
SDL_CondSignal(videoQueue->videoCond); //唤醒其他线程 如果缓冲区里面有几个数据后再唤醒 较好
}
|