1396 lines
43 KiB
C++
1396 lines
43 KiB
C++
|
|
|||
|
#include "videoplayer.h"
|
|||
|
#include <stdio.h>
|
|||
|
#include <QDebug>
|
|||
|
#include <QTimer>
|
|||
|
#include <qeventloop.h>
|
|||
|
#define FLUSH_DATA "FLUSH"
|
|||
|
#define SDL_AUDIO_BUFFER_SIZE 1024
|
|||
|
#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: packet_queue_init
|
|||
|
Description: 队列初始化
|
|||
|
Input: *q
|
|||
|
OutPut: *q
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
void packet_queue_init(PacketQueue *q)
|
|||
|
{
|
|||
|
memset(q, 0, sizeof(PacketQueue));
|
|||
|
/**Create a mutex, initialized unlocked.**/
|
|||
|
q->mutex = SDL_CreateMutex();
|
|||
|
|
|||
|
/**Create a condition variable.**/
|
|||
|
q->cond = SDL_CreateCond();
|
|||
|
q->size = 0;
|
|||
|
q->nb_packets = 0;
|
|||
|
q->first_pkt = NULL;
|
|||
|
q->last_pkt = NULL;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: packet_queue_put
|
|||
|
Description: 数据压入队列
|
|||
|
Input: *pkt
|
|||
|
OutPut: *q
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
int packet_queue_put(PacketQueue *q, AVPacket *pkt) {
|
|||
|
|
|||
|
AVPacketList *pkt1;
|
|||
|
if (av_dup_packet(pkt) < 0) {
|
|||
|
return -1;
|
|||
|
}
|
|||
|
pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList));
|
|||
|
if (!pkt1)
|
|||
|
{
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
pkt1->pkt = *pkt;
|
|||
|
pkt1->next = NULL;
|
|||
|
|
|||
|
SDL_LockMutex(q->mutex);
|
|||
|
if (!q->last_pkt)
|
|||
|
{
|
|||
|
q->first_pkt = pkt1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
q->last_pkt->next = pkt1;
|
|||
|
}
|
|||
|
|
|||
|
q->last_pkt = pkt1;
|
|||
|
q->nb_packets++;
|
|||
|
q->size += pkt1->pkt.size;
|
|||
|
SDL_CondSignal(q->cond);
|
|||
|
SDL_UnlockMutex(q->mutex);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: packet_queue_get
|
|||
|
Description: 数据出队列
|
|||
|
Input: *pkt,block
|
|||
|
OutPut: *q
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
|
|||
|
AVPacketList *pkt1;
|
|||
|
int ret = 0;
|
|||
|
|
|||
|
SDL_LockMutex(q->mutex);
|
|||
|
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
pkt1 = q->first_pkt;
|
|||
|
if (pkt1)
|
|||
|
{
|
|||
|
q->first_pkt = pkt1->next;
|
|||
|
if (!q->first_pkt)
|
|||
|
{
|
|||
|
q->last_pkt = NULL;
|
|||
|
}
|
|||
|
q->nb_packets--;
|
|||
|
q->size -= pkt1->pkt.size;
|
|||
|
*pkt = pkt1->pkt;
|
|||
|
av_free(pkt1);
|
|||
|
ret = 1;
|
|||
|
break;
|
|||
|
}
|
|||
|
else if (!block)
|
|||
|
{
|
|||
|
ret = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
SDL_CondWait(q->cond, q->mutex);
|
|||
|
}
|
|||
|
}
|
|||
|
SDL_UnlockMutex(q->mutex);
|
|||
|
return ret;
|
|||
|
}
|
|||
|
/********************************************************
|
|||
|
Function: packet_queue_flush
|
|||
|
Description: 清空队列
|
|||
|
Input: *q
|
|||
|
OutPut: *q
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
static void packet_queue_flush(PacketQueue *q)
|
|||
|
{
|
|||
|
AVPacketList *pkt, *pkt1;
|
|||
|
|
|||
|
SDL_LockMutex(q->mutex);
|
|||
|
for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1)
|
|||
|
{
|
|||
|
pkt1 = pkt->next;
|
|||
|
|
|||
|
if(pkt1->pkt.data != (uint8_t *)"FLUSH")
|
|||
|
{
|
|||
|
/**do nothing ?**/
|
|||
|
}
|
|||
|
av_free_packet(&pkt->pkt);
|
|||
|
av_freep(&pkt);
|
|||
|
|
|||
|
}
|
|||
|
q->last_pkt = NULL;
|
|||
|
q->first_pkt = NULL;
|
|||
|
q->nb_packets = 0;
|
|||
|
q->size = 0;
|
|||
|
SDL_UnlockMutex(q->mutex);
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: audio_decode_frame
|
|||
|
Description: 音频解码函数
|
|||
|
Input: m_pstVideosState
|
|||
|
OutPut: pts_ptr
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
static int audio_decode_frame(VideoState *m_pstVideosState, double *pts_ptr)
|
|||
|
{
|
|||
|
int n;
|
|||
|
int len1;
|
|||
|
int len2;
|
|||
|
int decoded_data_size;
|
|||
|
int got_frame = 0;
|
|||
|
int wanted_nb_samples;
|
|||
|
int resampled_data_size;
|
|||
|
double pts;
|
|||
|
int64_t dec_channel_layout;
|
|||
|
AVPacket *pkt = &m_pstVideosState->audio_pkt;
|
|||
|
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
while (m_pstVideosState->audio_pkt_size > 0)
|
|||
|
{
|
|||
|
/**判断暂停**/
|
|||
|
if (m_pstVideosState->isPause == true)
|
|||
|
{
|
|||
|
SDL_Delay(10);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/**为audio_frame分配空间**/
|
|||
|
if (!m_pstVideosState->audio_frame)
|
|||
|
{
|
|||
|
if (!(m_pstVideosState->audio_frame = av_frame_alloc()))
|
|||
|
{
|
|||
|
return AVERROR(ENOMEM);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/**Unreference all the buffers referenced by frame and reset the frame fields.**/
|
|||
|
//取消引用帧引用的所有缓冲区并重置帧字段。
|
|||
|
av_frame_unref(m_pstVideosState->audio_frame);
|
|||
|
}
|
|||
|
|
|||
|
/**Decode the audio frame of size pkt->size from pkt->data into audio_frame.**/
|
|||
|
//将大小为pkt->size from pkt->data的音频帧解码为音频帧。
|
|||
|
len1 = avcodec_decode_audio4(m_pstVideosState->audio_st->codec, m_pstVideosState->audio_frame,
|
|||
|
&got_frame, pkt);
|
|||
|
/**解码失败**/
|
|||
|
if (len1 < 0)
|
|||
|
{
|
|||
|
/**error, skip the frame**/
|
|||
|
m_pstVideosState->audio_pkt_size = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
m_pstVideosState->audio_pkt_data += len1; /**??**/
|
|||
|
m_pstVideosState->audio_pkt_size -= len1; /**??**/
|
|||
|
|
|||
|
/**获取帧失败**/
|
|||
|
if (!got_frame)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/**计算解码出来的桢需要的缓冲大小**/
|
|||
|
decoded_data_size = av_samples_get_buffer_size(NULL,
|
|||
|
m_pstVideosState->audio_frame->channels, m_pstVideosState->audio_frame->nb_samples,
|
|||
|
(AVSampleFormat)m_pstVideosState->audio_frame->format, 1);
|
|||
|
|
|||
|
/**Channel layout of the audio data**/
|
|||
|
dec_channel_layout =
|
|||
|
(m_pstVideosState->audio_frame->channel_layout
|
|||
|
&& m_pstVideosState->audio_frame->channels
|
|||
|
== av_get_channel_layout_nb_channels(
|
|||
|
m_pstVideosState->audio_frame->channel_layout)) ?
|
|||
|
m_pstVideosState->audio_frame->channel_layout :
|
|||
|
av_get_default_channel_layout(
|
|||
|
m_pstVideosState->audio_frame->channels);
|
|||
|
|
|||
|
/**number of audio samples (per channel) described by this frame**/
|
|||
|
//此帧描述的音频采样数(每个通道)
|
|||
|
wanted_nb_samples = m_pstVideosState->audio_frame->nb_samples;
|
|||
|
|
|||
|
if (m_pstVideosState->audio_frame->format != m_pstVideosState->audio_src_fmt
|
|||
|
|| dec_channel_layout != m_pstVideosState->audio_src_channel_layout
|
|||
|
|| m_pstVideosState->audio_frame->sample_rate != m_pstVideosState->audio_src_freq
|
|||
|
|| (wanted_nb_samples != m_pstVideosState->audio_frame->nb_samples
|
|||
|
&& !m_pstVideosState->swr_ctx))
|
|||
|
{
|
|||
|
if (m_pstVideosState->swr_ctx)
|
|||
|
{
|
|||
|
swr_free(&m_pstVideosState->swr_ctx);
|
|||
|
}
|
|||
|
|
|||
|
/**Allocate SwrContext if needed and set/reset common parameters**/
|
|||
|
m_pstVideosState->swr_ctx = swr_alloc_set_opts(NULL,
|
|||
|
m_pstVideosState->audio_tgt_channel_layout, (AVSampleFormat)m_pstVideosState->audio_tgt_fmt,
|
|||
|
m_pstVideosState->audio_tgt_freq, dec_channel_layout,
|
|||
|
(AVSampleFormat)m_pstVideosState->audio_frame->format, m_pstVideosState->audio_frame->sample_rate,
|
|||
|
0, NULL);
|
|||
|
if (!m_pstVideosState->swr_ctx || swr_init(m_pstVideosState->swr_ctx) < 0)
|
|||
|
{
|
|||
|
//fprintf(stderr,"swr_init() failed\n");
|
|||
|
break;
|
|||
|
}
|
|||
|
m_pstVideosState->audio_src_channel_layout = dec_channel_layout;
|
|||
|
m_pstVideosState->audio_src_channels = m_pstVideosState->audio_st->codec->channels;
|
|||
|
m_pstVideosState->audio_src_freq = m_pstVideosState->audio_st->codec->sample_rate;
|
|||
|
m_pstVideosState->audio_src_fmt = m_pstVideosState->audio_st->codec->sample_fmt;
|
|||
|
}
|
|||
|
|
|||
|
/* 这里我们可以对采样数进行调整,增加或者减少,一般可以用来做声画同步 */
|
|||
|
if (m_pstVideosState->swr_ctx)
|
|||
|
{
|
|||
|
const uint8_t **in =
|
|||
|
(const uint8_t **) m_pstVideosState->audio_frame->extended_data;
|
|||
|
uint8_t *out[] = { m_pstVideosState->audio_buf2 };
|
|||
|
if (wanted_nb_samples != m_pstVideosState->audio_frame->nb_samples)
|
|||
|
{
|
|||
|
if (swr_set_compensation(m_pstVideosState->swr_ctx,
|
|||
|
(wanted_nb_samples - m_pstVideosState->audio_frame->nb_samples)
|
|||
|
* m_pstVideosState->audio_tgt_freq
|
|||
|
/ m_pstVideosState->audio_frame->sample_rate,
|
|||
|
wanted_nb_samples * m_pstVideosState->audio_tgt_freq
|
|||
|
/ m_pstVideosState->audio_frame->sample_rate) < 0)
|
|||
|
{
|
|||
|
//fprintf(stderr,"swr_set_compensation() failed\n");
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
len2 = swr_convert(m_pstVideosState->swr_ctx, out,
|
|||
|
sizeof(m_pstVideosState->audio_buf2) / m_pstVideosState->audio_tgt_channels
|
|||
|
/ av_get_bytes_per_sample(m_pstVideosState->audio_tgt_fmt),
|
|||
|
in, m_pstVideosState->audio_frame->nb_samples);
|
|||
|
if (len2 < 0)
|
|||
|
{
|
|||
|
//fprintf(stderr,"swr_convert() failed\n");
|
|||
|
break;
|
|||
|
}
|
|||
|
if (len2== sizeof(m_pstVideosState->audio_buf2) / m_pstVideosState->audio_tgt_channels
|
|||
|
/ av_get_bytes_per_sample(m_pstVideosState->audio_tgt_fmt))
|
|||
|
{
|
|||
|
//fprintf(stderr,"warning: audio buffer m_pstVideosState probably too small\n");
|
|||
|
swr_init(m_pstVideosState->swr_ctx);
|
|||
|
}
|
|||
|
m_pstVideosState->audio_buf = m_pstVideosState->audio_buf2;
|
|||
|
resampled_data_size = len2 * m_pstVideosState->audio_tgt_channels
|
|||
|
* av_get_bytes_per_sample(m_pstVideosState->audio_tgt_fmt);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
resampled_data_size = decoded_data_size;
|
|||
|
m_pstVideosState->audio_buf = m_pstVideosState->audio_frame->data[0];
|
|||
|
}
|
|||
|
|
|||
|
pts = m_pstVideosState->audio_clock;
|
|||
|
*pts_ptr = pts;
|
|||
|
n = 2 * m_pstVideosState->audio_st->codec->channels;
|
|||
|
m_pstVideosState->audio_clock += (double) resampled_data_size
|
|||
|
/ (double) (n * m_pstVideosState->audio_st->codec->sample_rate);
|
|||
|
|
|||
|
if (m_pstVideosState->seek_flag_audio)
|
|||
|
{
|
|||
|
/**发生了跳转 则跳过关键帧到目的时间的这几帧**/
|
|||
|
if (m_pstVideosState->audio_clock < m_pstVideosState->seek_time)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_pstVideosState->seek_flag_audio = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**We have data, return it and come back for more later**/
|
|||
|
return resampled_data_size;
|
|||
|
}
|
|||
|
|
|||
|
/**判断暂停**/
|
|||
|
if (m_pstVideosState->isPause == true)
|
|||
|
{
|
|||
|
SDL_Delay(10);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (pkt->data)
|
|||
|
{
|
|||
|
av_free_packet(pkt);
|
|||
|
}
|
|||
|
|
|||
|
memset(pkt, 0, sizeof(*pkt));
|
|||
|
|
|||
|
if (m_pstVideosState->quit)
|
|||
|
{
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/**从音频队列中获取音频数据**/
|
|||
|
if (packet_queue_get(&m_pstVideosState->audioq, pkt, 0) <= 0)
|
|||
|
{
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/**收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下**/
|
|||
|
if(strcmp((char*)pkt->data,FLUSH_DATA) == 0)
|
|||
|
{
|
|||
|
avcodec_flush_buffers(m_pstVideosState->audio_st->codec);
|
|||
|
av_free_packet(pkt);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
m_pstVideosState->audio_pkt_data = pkt->data;
|
|||
|
m_pstVideosState->audio_pkt_size = pkt->size;
|
|||
|
|
|||
|
/**if update, update the audio clock w/pts **/
|
|||
|
if (pkt->pts != AV_NOPTS_VALUE)
|
|||
|
{
|
|||
|
m_pstVideosState->audio_clock = av_q2d(m_pstVideosState->audio_st->time_base) * pkt->pts;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: audio_callback
|
|||
|
Description: 音频解码回调函数
|
|||
|
Input: *userdata
|
|||
|
OutPut:
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
* This function is called when the audio device needs more data.
|
|||
|
*
|
|||
|
* \param userdata An application-specific parameter saved in
|
|||
|
* the SDL_AudioSpec structure
|
|||
|
* \param stream A pointer to the audio data buffer.
|
|||
|
* \param len The length of that buffer in bytes.
|
|||
|
*
|
|||
|
* Once the callback returns, the buffer will no longer be valid.
|
|||
|
* Stereo samples are stored in a LRLRLR ordering.
|
|||
|
*
|
|||
|
* You can choose to avoid callbacks and use SDL_QueueAudio() instead, if
|
|||
|
* you like. Just open your audio device with a NULL callback.
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
|
|||
|
static void audio_callback(void *userdata, Uint8 *stream, int len)
|
|||
|
{
|
|||
|
VideoState *m_pstVideosState = (VideoState *) userdata;
|
|||
|
|
|||
|
int len1;
|
|||
|
double pts;
|
|||
|
int audio_data_size;
|
|||
|
|
|||
|
/**len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 **/
|
|||
|
while (len > 0)
|
|||
|
{
|
|||
|
/**audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,
|
|||
|
这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我
|
|||
|
们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更多的桢数据**/
|
|||
|
if (m_pstVideosState->audio_buf_index >= m_pstVideosState->audio_buf_size)
|
|||
|
{
|
|||
|
/**音频解码**/
|
|||
|
audio_data_size = audio_decode_frame(m_pstVideosState, &pts);
|
|||
|
/**没能解码出数据,默认播放静音 **/
|
|||
|
if (audio_data_size < 0)
|
|||
|
{
|
|||
|
/**silence**/
|
|||
|
m_pstVideosState->audio_buf_size = 1024;
|
|||
|
/**清零,静音**/
|
|||
|
if (m_pstVideosState->audio_buf == NULL)
|
|||
|
{
|
|||
|
printf("audio decode error \n");
|
|||
|
return;
|
|||
|
}
|
|||
|
memset(m_pstVideosState->audio_buf, 0, m_pstVideosState->audio_buf_size);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_pstVideosState->audio_buf_size = audio_data_size;
|
|||
|
}
|
|||
|
m_pstVideosState->audio_buf_index = 0;
|
|||
|
}
|
|||
|
/**查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy **/
|
|||
|
len1 = m_pstVideosState->audio_buf_size - m_pstVideosState->audio_buf_index;
|
|||
|
if (len1 > len) {
|
|||
|
len1 = len;
|
|||
|
}
|
|||
|
|
|||
|
if (m_pstVideosState->audio_buf == NULL)
|
|||
|
{
|
|||
|
printf("audio buf is full \n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
memcpy(stream, (uint8_t *) m_pstVideosState->audio_buf + m_pstVideosState->audio_buf_index, len1);
|
|||
|
|
|||
|
len -= len1;
|
|||
|
stream += len1;
|
|||
|
m_pstVideosState->audio_buf_index += len1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: get_audio_clock
|
|||
|
Description:
|
|||
|
Input:
|
|||
|
OutPut:
|
|||
|
Return:
|
|||
|
Others: 实际该函数没有被使用
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
static double get_audio_clock(VideoState *m_pstVideosState)
|
|||
|
{
|
|||
|
double pts;
|
|||
|
int hw_buf_size, bytes_per_sec, n;
|
|||
|
|
|||
|
pts = m_pstVideosState->audio_clock; /**maintained in the audio thread**/
|
|||
|
hw_buf_size = m_pstVideosState->audio_buf_size - m_pstVideosState->audio_buf_index;
|
|||
|
bytes_per_sec = 0;
|
|||
|
n = m_pstVideosState->audio_st->codec->channels * 2;
|
|||
|
if(m_pstVideosState->audio_st)
|
|||
|
{
|
|||
|
bytes_per_sec = m_pstVideosState->audio_st->codec->sample_rate * n;
|
|||
|
}
|
|||
|
if(bytes_per_sec)
|
|||
|
{
|
|||
|
pts -= (double)hw_buf_size / bytes_per_sec;
|
|||
|
}
|
|||
|
return pts;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: synchronize_video
|
|||
|
Description:
|
|||
|
Input:
|
|||
|
OutPut:
|
|||
|
Return:
|
|||
|
Others: 视频时间同步
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
static double synchronize_video(VideoState *m_pstVideosState, AVFrame *src_frame, double pts)
|
|||
|
{
|
|||
|
|
|||
|
double frame_delay;
|
|||
|
|
|||
|
if (pts != 0) {
|
|||
|
/* if we have pts, set video clock to it */
|
|||
|
m_pstVideosState->video_clock = pts;
|
|||
|
} else {
|
|||
|
/* if we aren't given a pts, set it to the clock */
|
|||
|
pts = m_pstVideosState->video_clock;
|
|||
|
}
|
|||
|
/* update the video clock */
|
|||
|
frame_delay = av_q2d(m_pstVideosState->video_st->codec->time_base);
|
|||
|
/* if we are repeating a frame, adjust clock accordingly */
|
|||
|
frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
|
|||
|
m_pstVideosState->video_clock += frame_delay;
|
|||
|
return pts;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: audio_stream_component_open
|
|||
|
Description: 音频相关设置
|
|||
|
Input: m_pstVideosState,stream_index
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others: 初始化音频解码器,初始化音频存储队列
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
int audio_stream_component_open(VideoState *m_pstVideosState, uint stream_index)
|
|||
|
{
|
|||
|
AVCodecContext *codecCtx;
|
|||
|
AVCodec *codec;
|
|||
|
SDL_AudioSpec spec; /**输出实际音频信息**/
|
|||
|
SDL_AudioSpec wanted_spec; /**输入音频信息**/
|
|||
|
AVFormatContext *pFormatContext = m_pstVideosState->pFormatContext;
|
|||
|
int wanted_nb_channels;
|
|||
|
uint64_t wanted_channel_layout = 0;
|
|||
|
|
|||
|
/**SDL支持的声道数为 1, 2, 4, 6 **/
|
|||
|
/**后面我们会使用这个数组来纠正不支持的声道数目**/
|
|||
|
const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 };
|
|||
|
|
|||
|
if (stream_index < 0 || stream_index >= pFormatContext->nb_streams)
|
|||
|
{
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
codecCtx = pFormatContext->streams[stream_index]->codec;
|
|||
|
wanted_nb_channels = codecCtx->channels;
|
|||
|
if (!wanted_channel_layout|| wanted_nb_channels
|
|||
|
!= av_get_channel_layout_nb_channels(wanted_channel_layout))
|
|||
|
{
|
|||
|
wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
|
|||
|
wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
|
|||
|
}
|
|||
|
|
|||
|
/**音频通道设置**/
|
|||
|
wanted_spec.channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
|
|||
|
wanted_spec.freq = codecCtx->sample_rate;
|
|||
|
|
|||
|
/**没有音频信息**/
|
|||
|
if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0)
|
|||
|
{
|
|||
|
/**fprintf(stderr,"Invalid sample rate or channel count!\n");**/
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
wanted_spec.format = AUDIO_S16SYS; /**Signed 16-bit samples**/
|
|||
|
wanted_spec.silence = 0; /**0指示静音**/
|
|||
|
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; /**自定义SDL缓冲区大小 1024**/
|
|||
|
/**This function is called when the audio device needs more data**/
|
|||
|
wanted_spec.callback = audio_callback; /**音频解码的关键回调函数**/
|
|||
|
wanted_spec.userdata = m_pstVideosState; /**传给上面回调函数的外带数据**/
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
/**Open a specific audio device,正常时返回值不为0**/
|
|||
|
//打开音频设备,SDL_OpenAudioDevice函数只能用来打开AudioDeviceID大于等于2的设备, 而默认的设备,即AudioDeviceID等于1,只能用SDL_OpenAudio函数打开
|
|||
|
int count = SDL_GetNumAudioDevices(0);
|
|||
|
for (int i = 0; i < count; i++)
|
|||
|
{
|
|||
|
qDebug() << "Audio device " << i << " : " << SDL_GetAudioDeviceName(i, 0);
|
|||
|
}
|
|||
|
m_pstVideosState->audioID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(1,0),0,&wanted_spec, &spec,0);
|
|||
|
|
|||
|
/**打印通道信息**/
|
|||
|
fprintf(stderr,"SDL_OpenAudio (%d channels): %s\n",wanted_spec.channels, SDL_GetError());
|
|||
|
qDebug()<<QString("SDL_OpenAudio (%1 channels): %2").arg(wanted_spec.channels).arg(SDL_GetError());
|
|||
|
|
|||
|
/**通道合法性判断**/
|
|||
|
wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
|
|||
|
if (!wanted_spec.channels)
|
|||
|
{
|
|||
|
fprintf(stderr,"No more channel combinations to tyu, audio open failed\n");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/**Return default channel layout for a given number of channels**/
|
|||
|
wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
|
|||
|
}while(m_pstVideosState->audioID == 0);
|
|||
|
|
|||
|
/**检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充)**/
|
|||
|
if (spec.format != AUDIO_S16SYS)
|
|||
|
{
|
|||
|
fprintf(stderr,"SDL advised audio format %d m_pstVideosState not supported!\n",spec.format);
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/**通道判断**/
|
|||
|
if (spec.channels != wanted_spec.channels)
|
|||
|
{
|
|||
|
wanted_channel_layout = av_get_default_channel_layout(spec.channels);
|
|||
|
if (!wanted_channel_layout)
|
|||
|
{
|
|||
|
fprintf(stderr,"SDL advised channel count %d m_pstVideosState not supported!\n",spec.channels);
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
m_pstVideosState->audio_hw_buf_size = spec.size;
|
|||
|
|
|||
|
/**把设置好的参数保存到大结构中**/
|
|||
|
m_pstVideosState->audio_src_fmt = m_pstVideosState->audio_tgt_fmt = AV_SAMPLE_FMT_S16;
|
|||
|
m_pstVideosState->audio_src_freq = m_pstVideosState->audio_tgt_freq = spec.freq;
|
|||
|
m_pstVideosState->audio_src_channel_layout = m_pstVideosState->audio_tgt_channel_layout = wanted_channel_layout;
|
|||
|
m_pstVideosState->audio_src_channels = m_pstVideosState->audio_tgt_channels = spec.channels;
|
|||
|
|
|||
|
/** Find a registered decoder with a matching codec ID.**/
|
|||
|
codec = avcodec_find_decoder(codecCtx->codec_id);
|
|||
|
|
|||
|
/**open codec**/
|
|||
|
if (!codec || (avcodec_open2(codecCtx, codec, NULL) < 0))
|
|||
|
{
|
|||
|
fprintf(stderr,"Unsupported codec!\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/**设置丢弃没有用的音频包**/
|
|||
|
pFormatContext->streams[stream_index]->discard = AVDISCARD_DEFAULT;
|
|||
|
|
|||
|
switch (codecCtx->codec_type)
|
|||
|
{
|
|||
|
case AVMEDIA_TYPE_AUDIO:
|
|||
|
{
|
|||
|
// m_pstVideosState->s32AStreamCount = stream_index;
|
|||
|
m_pstVideosState->audio_st = pFormatContext->streams[stream_index];
|
|||
|
m_pstVideosState->audio_buf_size = 0;
|
|||
|
m_pstVideosState->audio_buf_index = 0;
|
|||
|
memset(&m_pstVideosState->audio_pkt, 0, sizeof(m_pstVideosState->audio_pkt));
|
|||
|
|
|||
|
/**队列初始化**/
|
|||
|
packet_queue_init(&m_pstVideosState->audioq);
|
|||
|
// SDL_PauseAudio(0); // 开始播放静音
|
|||
|
|
|||
|
/**设置开始和停止调用回调函数**/
|
|||
|
/**para2 为0 时,开始回调函数audio_callback**/
|
|||
|
SDL_PauseAudioDevice(m_pstVideosState->audioID,0);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: video_thread
|
|||
|
Description: 视频解码线程
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
int video_thread(void *arg)
|
|||
|
{
|
|||
|
int ret;
|
|||
|
int got_picture;
|
|||
|
int numBytes;
|
|||
|
//int l_s32VideoPack = 0;
|
|||
|
|
|||
|
double video_pts = 0; /**当前视频的pts**/
|
|||
|
double audio_pts = 0; /**音频pts**/
|
|||
|
|
|||
|
AVPacket pkt1;
|
|||
|
AVPacket *packet = &pkt1;
|
|||
|
VideoState *m_pstVideosState = (VideoState *) arg;
|
|||
|
|
|||
|
/**解码视频相关**/
|
|||
|
AVFrame *pFrame; /**解码器解码之后的图像**/
|
|||
|
AVFrame *pFrameRGB;/**转换编码之后的视频流**/
|
|||
|
uint8_t *out_buffer_rgb; /**解码后的图像数据**/
|
|||
|
struct SwsContext *img_convert_ctx; /**用于解码后的视频格式转换**/
|
|||
|
|
|||
|
AVCodecContext *l_pstVCodecCtx = m_pstVideosState->video_st->codec; /**视频解码器上下文**/
|
|||
|
|
|||
|
pFrame = av_frame_alloc();//分配空间
|
|||
|
pFrameRGB = av_frame_alloc();//分配空间
|
|||
|
|
|||
|
/**这里我们改成了 将解码后的YUV数据转换成RGB32**/
|
|||
|
//成功后返回SwsContext 类型的结构体。
|
|||
|
//参数1:被转换源的宽
|
|||
|
//参数2:被转换源的高
|
|||
|
//参数3:被转换源的格式,eg:YUV、RGB……(枚举格式,也可以直接用枚举的代号表示eg:AV_PIX_FMT_YUV420P这些枚举的格式在libavutil/pixfmt.h中列出)
|
|||
|
//参数4:转换后指定的宽
|
|||
|
//参数5:转换后指定的高
|
|||
|
//参数6:转换后指定的格式同参数3的格式
|
|||
|
//参数7:转换所使用的算法,
|
|||
|
|
|||
|
img_convert_ctx = sws_getContext(l_pstVCodecCtx->width, l_pstVCodecCtx->height,
|
|||
|
AV_PIX_FMT_YUV420P, l_pstVCodecCtx->width, l_pstVCodecCtx->height,
|
|||
|
AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
|
|||
|
|
|||
|
numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, l_pstVCodecCtx->width,l_pstVCodecCtx->height);
|
|||
|
|
|||
|
out_buffer_rgb = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
|
|||
|
//该函数会自动填充AVFrame的data和linesize字段
|
|||
|
avpicture_fill((AVPicture *) pFrameRGB, out_buffer_rgb, AV_PIX_FMT_YUV420P,l_pstVCodecCtx->width, l_pstVCodecCtx->height);
|
|||
|
|
|||
|
while(1)
|
|||
|
{
|
|||
|
/**判断退出**/
|
|||
|
if (m_pstVideosState->quit)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/**判断暂停**/
|
|||
|
if (m_pstVideosState->isPause == true)
|
|||
|
{
|
|||
|
/**延时,ms 单位**/
|
|||
|
SDL_Delay(10);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/**队列里取数据**/
|
|||
|
if (packet_queue_get(&m_pstVideosState->videoq, packet, 0) <= 0)
|
|||
|
{
|
|||
|
|
|||
|
if (m_pstVideosState->readFinished)
|
|||
|
{
|
|||
|
/**队列里面没有数据了且读取完毕了**/
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/**队列只是暂时没有数据而已**/
|
|||
|
/**延时,ms 单位**/
|
|||
|
SDL_Delay(1);
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// printf("get video packe %d \n",l_s32VideoPack++);
|
|||
|
|
|||
|
/**收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下**/
|
|||
|
if(strcmp((char*)packet->data,FLUSH_DATA) == 0)
|
|||
|
{
|
|||
|
avcodec_flush_buffers(m_pstVideosState->video_st->codec);
|
|||
|
av_free_packet(packet);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/**视频解码**/
|
|||
|
/**将packet->data中packet->size 字节的数据解码到 pFrame 中**/
|
|||
|
ret = avcodec_decode_video2(l_pstVCodecCtx, pFrame, &got_picture,packet);
|
|||
|
/**解码失败**/
|
|||
|
if (ret < 0)
|
|||
|
{
|
|||
|
qDebug()<<"decode error.\n";
|
|||
|
av_free_packet(packet);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/**解码时间无效**/
|
|||
|
if (packet->dts == AV_NOPTS_VALUE && pFrame->opaque&& *(uint64_t*) pFrame->opaque != AV_NOPTS_VALUE)
|
|||
|
{
|
|||
|
video_pts = *(uint64_t *) pFrame->opaque;
|
|||
|
}/**解码时间有效**/
|
|||
|
else if (packet->dts != AV_NOPTS_VALUE)
|
|||
|
{
|
|||
|
/**解码时间戳赋值给显示时间戳**/
|
|||
|
video_pts = packet->dts;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/**显示时间戳赋值为0**/
|
|||
|
video_pts = 0;
|
|||
|
}
|
|||
|
|
|||
|
/**AVRational 转换为double 数据类型**/
|
|||
|
video_pts *= av_q2d(m_pstVideosState->video_st->time_base);
|
|||
|
|
|||
|
/**同步视频时间**/
|
|||
|
video_pts = synchronize_video(m_pstVideosState, pFrame, video_pts);
|
|||
|
|
|||
|
/**跳转标志 -- 用于视频线程中,run 线程中设置该标志**/
|
|||
|
if (m_pstVideosState->seek_flag_video)
|
|||
|
{
|
|||
|
/**发生了跳转 则跳过关键帧到目的时间的这几帧**/
|
|||
|
if (video_pts < m_pstVideosState->seek_time)
|
|||
|
{
|
|||
|
av_free_packet(packet);
|
|||
|
continue;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_pstVideosState->seek_flag_video = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**循环等待视频和视频的时间同步
|
|||
|
有个问题:如果音频解码失败,音频pts一直为0 ,者会播放卡住**/
|
|||
|
while(1)
|
|||
|
{
|
|||
|
/**判断是否退出**/
|
|||
|
if (m_pstVideosState->quit)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
audio_pts = m_pstVideosState->audio_clock;
|
|||
|
|
|||
|
/**主要是 跳转的时候 我们把video_clock设置成0了
|
|||
|
因此这里需要更新video_pts
|
|||
|
否则当从后面跳转到前面的时候 会卡在这里**/
|
|||
|
|
|||
|
video_pts = m_pstVideosState->video_clock;
|
|||
|
|
|||
|
if(video_pts <= audio_pts)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// printf("video_pts = %f,audio_pts = %f, diff = %f \n",
|
|||
|
// video_pts,audio_pts,video_pts - audio_pts);
|
|||
|
|
|||
|
int delayTime = (video_pts - audio_pts) * 1000;
|
|||
|
delayTime = delayTime > 5 ? 5:delayTime;
|
|||
|
SDL_Delay(delayTime);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (got_picture)
|
|||
|
{
|
|||
|
sws_scale(img_convert_ctx,
|
|||
|
(uint8_t const * const *) pFrame->data,
|
|||
|
pFrame->linesize, 0, l_pstVCodecCtx->height, pFrameRGB->data,
|
|||
|
pFrameRGB->linesize);
|
|||
|
|
|||
|
/**把这个RGB数据 用QImage加载**/
|
|||
|
// QImage tmpImg((uchar *)out_buffer_rgb,l_pstVCodecCtx->width,l_pstVCodecCtx->height,QImage::Format_RGB32);
|
|||
|
|
|||
|
/**把图像复制一份 传递给界面显示**/
|
|||
|
//QImage image = tmpImg.copy();
|
|||
|
|
|||
|
/**调用激发信号的函数,更新图像显示**/
|
|||
|
// char *outputfilename = "d:\\test3.yuv";
|
|||
|
// m_pstVideosState->player->SaveYuv(pFrameRGB->data[0], pFrameRGB->linesize[0], l_pstVCodecCtx->width, l_pstVCodecCtx->height, outputfilename);
|
|||
|
// m_pstVideosState->player->SaveYuv(pFrameRGB->data[1], pFrameRGB->linesize[1], l_pstVCodecCtx->width/2, l_pstVCodecCtx->height/2, outputfilename);
|
|||
|
// m_pstVideosState->player->SaveYuv(pFrameRGB->data[2], pFrameRGB->linesize[2], l_pstVCodecCtx->width/2, l_pstVCodecCtx->height/2, outputfilename);
|
|||
|
m_pstVideosState->player->disPlayVideo(out_buffer_rgb,l_pstVCodecCtx->width,l_pstVCodecCtx->height);
|
|||
|
}
|
|||
|
av_free_packet(packet);
|
|||
|
}
|
|||
|
|
|||
|
av_free(pFrame);
|
|||
|
av_free(pFrameRGB);
|
|||
|
av_free(out_buffer_rgb);
|
|||
|
if (!m_pstVideosState->quit)
|
|||
|
{
|
|||
|
m_pstVideosState->quit = true;
|
|||
|
}
|
|||
|
|
|||
|
m_pstVideosState->videoThreadFinished = true;
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: VideoPlayer
|
|||
|
Description: 构造函数
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others: 默认停止播放
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
VideoPlayer::VideoPlayer()
|
|||
|
{
|
|||
|
mPlayerState = Stop;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: VideoPlayer
|
|||
|
Description: 析构函数
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
VideoPlayer::~VideoPlayer()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: setFileName
|
|||
|
Description: 获取需要播放文件的名字
|
|||
|
Input: path
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:获取到名字之后开始自动视频播放
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
bool VideoPlayer::setFileName(QString path)
|
|||
|
{
|
|||
|
if (mPlayerState != Stop)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
mFileName = path;
|
|||
|
|
|||
|
/**启动run线程**/
|
|||
|
this->start();
|
|||
|
|
|||
|
mPlayerState = Playing;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: play
|
|||
|
Description: 视频播放事件
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
bool VideoPlayer::play()
|
|||
|
{
|
|||
|
mVideoState.isPause = false;
|
|||
|
|
|||
|
if (mPlayerState != Pause)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
mPlayerState = Playing;
|
|||
|
emit sig_StateChanged(Playing);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: pause
|
|||
|
Description: 视频停止播放
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
bool VideoPlayer::pause()
|
|||
|
{
|
|||
|
mVideoState.isPause = true;
|
|||
|
|
|||
|
if (mPlayerState != Playing)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
mPlayerState = Pause;
|
|||
|
|
|||
|
emit sig_StateChanged(Pause);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: stop
|
|||
|
Description: 关闭视频播放
|
|||
|
Input: isWait
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
bool VideoPlayer::stop(bool isWait)
|
|||
|
{
|
|||
|
if (mPlayerState == Stop)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
//给退出标记赋值
|
|||
|
mVideoState.quit = 1;
|
|||
|
|
|||
|
if (isWait)
|
|||
|
{
|
|||
|
while(!mVideoState.readThreadFinished || !mVideoState.videoThreadFinished)
|
|||
|
{
|
|||
|
SDL_Delay(10);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**关闭SDL音频播放设备**/
|
|||
|
if (mVideoState.audioID != 0)
|
|||
|
{
|
|||
|
SDL_LockAudio();
|
|||
|
SDL_PauseAudioDevice(mVideoState.audioID,1);
|
|||
|
SDL_UnlockAudio();
|
|||
|
|
|||
|
mVideoState.audioID = 0;
|
|||
|
}
|
|||
|
mPlayerState = Stop;
|
|||
|
emit sig_StateChanged(Stop);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: seek
|
|||
|
Description: 发生了拖动事件
|
|||
|
Input: pos
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
void VideoPlayer::seek(int64_t pos)
|
|||
|
{
|
|||
|
if(!mVideoState.seek_req)
|
|||
|
{
|
|||
|
mVideoState.seek_pos = pos;
|
|||
|
mVideoState.seek_req = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: getCurrentTime
|
|||
|
Description: 获取当前的pts
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
double VideoPlayer::getCurrentTime()
|
|||
|
{
|
|||
|
return mVideoState.audio_clock;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: getTotalTime
|
|||
|
Description: 获取当前播放文件的总时长
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
int64_t VideoPlayer::getTotalTime()
|
|||
|
{
|
|||
|
return mVideoState.pFormatContext->duration;
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: disPlayVideo
|
|||
|
Description: 发送信号,刷新视频画面显示
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
void VideoPlayer::disPlayVideo(QImage img)
|
|||
|
{
|
|||
|
emit sig_GetOneFrame(img);
|
|||
|
}
|
|||
|
|
|||
|
void VideoPlayer::disPlayVideo(uchar *ptr, uint width, uint height)
|
|||
|
{
|
|||
|
emit sigYuv_liuyi(ptr,width,height);
|
|||
|
}
|
|||
|
|
|||
|
/********************************************************
|
|||
|
Function: run()
|
|||
|
Description: 视频音频数据读取线程,将读取的视频数据存入视频
|
|||
|
队列,将读取的音频数据存入音频队列里
|
|||
|
Input: none
|
|||
|
OutPut: none
|
|||
|
Return:
|
|||
|
Others:
|
|||
|
Author:
|
|||
|
Date:
|
|||
|
*********************************************************/
|
|||
|
void VideoPlayer::run()
|
|||
|
{
|
|||
|
AVCodec *l_pstVCodec;//视频解码器
|
|||
|
AVCodec *l_pstACodec;//音频解码器
|
|||
|
AVFormatContext *l_pstFormatCtx;//存储整个视频信息
|
|||
|
AVCodecContext *l_pstVCodecCtx;//视频解码器
|
|||
|
AVCodecContext *l_pstACodecCtx;//音频解码器
|
|||
|
VideoState *m_pstVideosState = &mVideoState;
|
|||
|
|
|||
|
int l_s32VStreamCount;
|
|||
|
int l_s32AStreamCount;
|
|||
|
char file_path[1280] = {0};
|
|||
|
|
|||
|
//int l_s32VideoPacketNum = 0;
|
|||
|
//int l_s32AudioPacketNum = 0;
|
|||
|
|
|||
|
strcpy(file_path,mFileName.toUtf8().data());
|
|||
|
memset(&mVideoState,0,sizeof(VideoState));
|
|||
|
|
|||
|
/**分配空间**/
|
|||
|
l_pstFormatCtx = avformat_alloc_context();
|
|||
|
|
|||
|
/**打开输入文件**/
|
|||
|
if (avformat_open_input(&l_pstFormatCtx, file_path, NULL, NULL) != 0)
|
|||
|
{
|
|||
|
printf("can't open the file. \n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/**获取媒体信息**/
|
|||
|
if (avformat_find_stream_info(l_pstFormatCtx, NULL) < 0)
|
|||
|
{
|
|||
|
printf("Could't find stream infomation.\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
l_s32VStreamCount = -1;
|
|||
|
l_s32AStreamCount = -1;
|
|||
|
|
|||
|
/**循环查找视频中包含的流信息**/
|
|||
|
for (uint i = 0; i < l_pstFormatCtx->nb_streams; i++)
|
|||
|
{
|
|||
|
if (l_pstFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
|
|||
|
{
|
|||
|
l_s32VStreamCount = i;//记录视频流的数量
|
|||
|
}
|
|||
|
if (l_pstFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && l_s32AStreamCount < 0)
|
|||
|
{
|
|||
|
l_s32AStreamCount = i;//记录音频流的数量
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**如果videoStream为-1 说明没有找到视频流**/
|
|||
|
if (l_s32VStreamCount == -1) {
|
|||
|
printf("Didn't find a video stream.\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/**如果videoStream为-1 说明没有找到音频流**/
|
|||
|
if (l_s32AStreamCount == -1) {
|
|||
|
printf("Didn't find a audio stream.\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/**赋值**/
|
|||
|
m_pstVideosState->pFormatContext = l_pstFormatCtx;
|
|||
|
m_pstVideosState->s32VStreamCount = l_s32VStreamCount;
|
|||
|
m_pstVideosState->s32AStreamCount = l_s32AStreamCount;
|
|||
|
|
|||
|
/**发送媒体长度,用来更新显示进度条**/
|
|||
|
emit sig_TotalTimeChanged(getTotalTime());
|
|||
|
|
|||
|
if (l_s32AStreamCount >= 0)
|
|||
|
{
|
|||
|
/**所有设置SDL音频流信息的步骤都在这个函数里完成**/
|
|||
|
audio_stream_component_open(&mVideoState, l_s32AStreamCount);
|
|||
|
}
|
|||
|
|
|||
|
/**查找音频解码器**/
|
|||
|
l_pstACodecCtx = l_pstFormatCtx->streams[l_s32AStreamCount]->codec;
|
|||
|
l_pstACodec = avcodec_find_decoder(l_pstACodecCtx->codec_id);
|
|||
|
|
|||
|
if (l_pstACodec == NULL) {
|
|||
|
printf("ACodec not found.\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/**打开音频解码器**/
|
|||
|
if (avcodec_open2(l_pstACodecCtx, l_pstACodec, NULL) < 0) {
|
|||
|
printf("Could not open audio codec.\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
//转化为音频流
|
|||
|
m_pstVideosState->audio_st = l_pstFormatCtx->streams[l_s32AStreamCount];
|
|||
|
|
|||
|
/**查找视频解码器**/
|
|||
|
l_pstVCodecCtx = l_pstFormatCtx->streams[l_s32VStreamCount]->codec;
|
|||
|
l_pstVCodec = avcodec_find_decoder(l_pstVCodecCtx->codec_id);
|
|||
|
|
|||
|
if (l_pstVCodec == NULL) {
|
|||
|
printf("PCodec not found.\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/**打开视频解码器**/
|
|||
|
if (avcodec_open2(l_pstVCodecCtx, l_pstVCodec, NULL) < 0) {
|
|||
|
printf("Could not open video codec.\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
//转化为视频流
|
|||
|
m_pstVideosState->video_st = l_pstFormatCtx->streams[l_s32VStreamCount];
|
|||
|
|
|||
|
/**初始化队列**/
|
|||
|
packet_queue_init(&m_pstVideosState->videoq);
|
|||
|
|
|||
|
/**创建一个线程专门用来解码视频**/
|
|||
|
m_pstVideosState->video_tid = SDL_CreateThread(video_thread, "video_thread", &mVideoState);
|
|||
|
m_pstVideosState->player = this;
|
|||
|
|
|||
|
// int y_size = l_pstVCodecCtx->width * l_pstVCodecCtx->height;
|
|||
|
|
|||
|
/**分配一个packet 用来存放读取的视频**/
|
|||
|
AVPacket *packet = (AVPacket *) malloc(sizeof(AVPacket));
|
|||
|
// av_new_packet(packet, y_size); //av_read_frame 会给它分配空间 因此这里不需要了
|
|||
|
|
|||
|
/**打印输出视频信息**/
|
|||
|
av_dump_format(l_pstFormatCtx, 0, file_path, 0);
|
|||
|
// int l_s32VideoPacketNum = 1;
|
|||
|
// int l_s32AudioPacketNum = 1;
|
|||
|
while (1)
|
|||
|
{
|
|||
|
/**停止播放了**/
|
|||
|
if (m_pstVideosState->quit) {
|
|||
|
break;
|
|||
|
}
|
|||
|
/**发生了进度条的拖动事件**/
|
|||
|
if (m_pstVideosState->seek_req)
|
|||
|
{
|
|||
|
int stream_index = -1;
|
|||
|
int64_t seek_target = m_pstVideosState->seek_pos;//获取拖动的位置
|
|||
|
|
|||
|
if (m_pstVideosState->s32VStreamCount >= 0)
|
|||
|
{
|
|||
|
stream_index = m_pstVideosState->s32VStreamCount;
|
|||
|
}
|
|||
|
else if (m_pstVideosState->s32AStreamCount >= 0)
|
|||
|
{
|
|||
|
stream_index = m_pstVideosState->s32AStreamCount;
|
|||
|
}
|
|||
|
|
|||
|
AVRational aVRational = {1, AV_TIME_BASE};
|
|||
|
if (stream_index >= 0)
|
|||
|
{
|
|||
|
/**重新调整时间位置**/
|
|||
|
/**Rescale a 64-bit integer by 2 rational numbers.**/
|
|||
|
seek_target = av_rescale_q(seek_target, aVRational,
|
|||
|
l_pstFormatCtx->streams[stream_index]->time_base);
|
|||
|
}
|
|||
|
|
|||
|
/**跳转到需要偏移时间位置附近的视频关键帧位置**/
|
|||
|
/**Seek to the keyframe at timestamp.**/
|
|||
|
if (av_seek_frame(m_pstVideosState->pFormatContext, stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0)
|
|||
|
{
|
|||
|
fprintf(stderr, "%s: error while seeking\n",m_pstVideosState->pFormatContext->filename);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (m_pstVideosState->s32AStreamCount >= 0)
|
|||
|
{
|
|||
|
/**分配一个packet**/
|
|||
|
AVPacket *packet = (AVPacket *) malloc(sizeof(AVPacket));
|
|||
|
av_new_packet(packet, 10);
|
|||
|
strcpy((char*)packet->data,FLUSH_DATA);
|
|||
|
/**清除队列**/
|
|||
|
packet_queue_flush(&m_pstVideosState->audioq);
|
|||
|
/**往队列中存入用来清除的包**/
|
|||
|
packet_queue_put(&m_pstVideosState->audioq, packet);
|
|||
|
}
|
|||
|
if (m_pstVideosState->s32VStreamCount >= 0)
|
|||
|
{
|
|||
|
/**分配一个packet**/
|
|||
|
AVPacket *packet = (AVPacket *) malloc(sizeof(AVPacket));
|
|||
|
av_new_packet(packet, 10);
|
|||
|
|
|||
|
strcpy((char*)packet->data,FLUSH_DATA);
|
|||
|
|
|||
|
/**清除队列**/
|
|||
|
packet_queue_flush(&m_pstVideosState->videoq);
|
|||
|
/**往队列中存入用来清除的包**/
|
|||
|
packet_queue_put(&m_pstVideosState->videoq, packet);
|
|||
|
/**视频时钟清零**/
|
|||
|
m_pstVideosState->video_clock = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
m_pstVideosState->seek_req = 0;
|
|||
|
m_pstVideosState->seek_time = m_pstVideosState->seek_pos / 1000000.0;
|
|||
|
m_pstVideosState->seek_flag_audio = 1;
|
|||
|
m_pstVideosState->seek_flag_video = 1;
|
|||
|
}
|
|||
|
|
|||
|
/**这里做了个限制 当队列里面的数据超过某个大小的时候 就暂停读取 防止一下子就把视频读完了,导致的空间分配不足**/
|
|||
|
/**这里audioq.size是指队列中的所有数据包带的音频数据的总量或者视频数据总量,并不是包的数量**/
|
|||
|
/**这个值可以稍微写大一些**/
|
|||
|
if (m_pstVideosState->audioq.size > MAX_AUDIO_SIZE || m_pstVideosState->videoq.size > MAX_VIDEO_SIZE)
|
|||
|
{
|
|||
|
SDL_Delay(10);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (m_pstVideosState->isPause == true)
|
|||
|
{
|
|||
|
SDL_Delay(10);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (av_read_frame(l_pstFormatCtx, packet) < 0)
|
|||
|
{
|
|||
|
m_pstVideosState->readFinished = true;
|
|||
|
|
|||
|
if (m_pstVideosState->quit)
|
|||
|
{
|
|||
|
break; /**解码线程也执行完了 可以退出了**/
|
|||
|
}
|
|||
|
|
|||
|
SDL_Delay(10);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (packet->stream_index == l_s32VStreamCount)
|
|||
|
{
|
|||
|
/**将视频数据存入视频队列中**/
|
|||
|
packet_queue_put(&m_pstVideosState->videoq, packet);
|
|||
|
//qDebug()<<("video input num %d \n",l_s32VideoPacketNum++);
|
|||
|
}
|
|||
|
else if( packet->stream_index == l_s32AStreamCount )
|
|||
|
{
|
|||
|
/**将音频数据存入音频队列中**/
|
|||
|
packet_queue_put(&m_pstVideosState->audioq, packet);
|
|||
|
// qDebug()<<("audio input num %d \n",l_s32AudioPacketNum++);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/**Free the packet that was allocated by av_read_frame**/
|
|||
|
av_free_packet(packet);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**文件读取结束 跳出循环的情况**/
|
|||
|
/**等待播放完毕**/
|
|||
|
while (!m_pstVideosState->quit) {
|
|||
|
SDL_Delay(100);
|
|||
|
}
|
|||
|
stop();
|
|||
|
avcodec_close(l_pstVCodecCtx);
|
|||
|
avformat_close_input(&l_pstFormatCtx);
|
|||
|
m_pstVideosState->readThreadFinished = true;
|
|||
|
|
|||
|
}
|
|||
|
int VideoPlayer::SaveYuv(unsigned char *buf, int wrap, int xsize, int ysize, char *filename)
|
|||
|
{
|
|||
|
FILE *f;
|
|||
|
int i;
|
|||
|
|
|||
|
f = fopen(filename, "ab+");
|
|||
|
for (i = 0; i<ysize; i++)
|
|||
|
{
|
|||
|
fwrite(buf + i * wrap, 1, xsize, f);
|
|||
|
}
|
|||
|
fclose(f);
|
|||
|
return 1;
|
|||
|
}
|