Browse Source

V1.5.0

1.新增播放控制
叶海辉 6 years ago
parent
commit
52edf7a255
6 changed files with 568 additions and 60 deletions
  1. 100 2
      src/mainwindow.cpp
  2. 10 0
      src/mainwindow.h
  3. 109 4
      src/mainwindow.ui
  4. 311 47
      src/videoplayer/videoplayer.cpp
  5. 36 5
      src/videoplayer/videoplayer.h
  6. 2 2
      说明.txt

+ 100 - 2
src/mainwindow.cpp

@@ -10,6 +10,8 @@
 
 #include <QPainter>
 #include <QPaintEvent>
+#include <QFileDialog>
+#include <QDebug>
 
 MainWindow::MainWindow(QWidget *parent) :
     QMainWindow(parent),
@@ -17,11 +19,31 @@ MainWindow::MainWindow(QWidget *parent) :
 {
     ui->setupUi(this);
 
+
+    av_register_all(); //初始化FFMPEG  调用了这个才能正常使用编码器和解码器
+    avformat_network_init(); //支持打开网络文件
+
+    if (SDL_Init(SDL_INIT_AUDIO)) {
+        fprintf(stderr,"Could not initialize SDL - %s. \n", SDL_GetError());
+        exit(1);
+    }
+
     mPlayer = new VideoPlayer;
     connect(mPlayer,SIGNAL(sig_GetOneFrame(QImage)),this,SLOT(slotGetOneFrame(QImage)));
+    connect(mPlayer,SIGNAL(sig_TotalTimeChanged(qint64)),this,SLOT(slotTotalTimeChanged(qint64)));
+    connect(mPlayer,SIGNAL(sig_StateChanged(VideoPlayer::PlayerState)),this,SLOT(slotStateChanged(VideoPlayer::PlayerState)));
+
 
-    mPlayer->setFileName("E:\\in.rmvb");
-    mPlayer->startPlay();
+    mTimer = new QTimer; //定时器-获取当前视频时间
+    connect(mTimer,SIGNAL(timeout()),this,SLOT(slotTimerTimeOut()));
+    mTimer->setInterval(500);
+
+    connect(ui->pushButton_open,SIGNAL(clicked()),this,SLOT(slotBtnClick()));
+    connect(ui->pushButton_play,SIGNAL(clicked()),this,SLOT(slotBtnClick()));
+    connect(ui->pushButton_pause,SIGNAL(clicked()),this,SLOT(slotBtnClick()));
+    connect(ui->pushButton_stop,SIGNAL(clicked()),this,SLOT(slotBtnClick()));
+
+    connect(ui->horizontalSlider,SIGNAL(sliderMoved(int)),this,SLOT(slotSliderMoved(int)));
 
 }
 
@@ -56,3 +78,79 @@ void MainWindow::slotGetOneFrame(QImage img)
     mImage = img;
     update(); //调用update将执行 paintEvent函数
 }
+
+void MainWindow::slotTotalTimeChanged(qint64 uSec)
+{
+    qint64 Sec = uSec/1000000;
+
+    ui->horizontalSlider->setRange(0,Sec);
+
+//    QString hStr = QString("00%1").arg(Sec/3600);
+    QString mStr = QString("00%1").arg(Sec/60);
+    QString sStr = QString("00%1").arg(Sec%60);
+
+    QString str = QString("%1:%2").arg(mStr.right(2)).arg(sStr.right(2));
+    ui->label_totaltime->setText(str);
+
+}
+
+void MainWindow::slotSliderMoved(int value)
+{
+    if (QObject::sender() == ui->horizontalSlider)
+    {
+        mPlayer->seek((qint64)value * 1000000);
+    }
+}
+
+void MainWindow::slotTimerTimeOut()
+{
+    if (QObject::sender() == mTimer)
+    {
+
+        qint64 Sec = mPlayer->getCurrentTime();
+
+        ui->horizontalSlider->setValue(Sec);
+
+    //    QString hStr = QString("00%1").arg(Sec/3600);
+        QString mStr = QString("00%1").arg(Sec/60);
+        QString sStr = QString("00%1").arg(Sec%60);
+
+        QString str = QString("%1:%2").arg(mStr.right(2)).arg(sStr.right(2));
+        ui->label_currenttime->setText(str);
+    }
+}
+
+void MainWindow::slotBtnClick()
+{
+    if (QObject::sender() == ui->pushButton_play)
+    {
+        mPlayer->play();
+    }
+    else if (QObject::sender() == ui->pushButton_pause)
+    {
+        mPlayer->pause();
+    }
+    else if (QObject::sender() == ui->pushButton_stop)
+    {
+        mPlayer->stop(true);
+    }
+    else if (QObject::sender() == ui->pushButton_open)
+    {
+        QString s = QFileDialog::getOpenFileName(
+                   this, "选择要播放的文件",
+                    "/",//初始目录
+                    "视频文件 (*.flv *.rmvb *.avi *.MP4);; 所有文件 (*.*);; ");
+        if (!s.isEmpty())
+        {
+            s.replace("/","\\");
+
+            mPlayer->stop(true); //如果在播放则先停止
+
+            mPlayer->setFileName(s);
+
+            mTimer->start();
+
+        }
+    }
+
+}

+ 10 - 0
src/mainwindow.h

@@ -10,6 +10,7 @@
 #include <QMainWindow>
 #include <QImage>
 #include <QPaintEvent>
+#include <QTimer>
 
 #include "videoplayer/videoplayer.h"
 
@@ -35,9 +36,18 @@ private:
 
     QImage mImage; //记录当前的图像
 
+    QTimer *mTimer; //定时器-获取当前视频时间
+
 private slots:
     void slotGetOneFrame(QImage img);
 
+    void slotTotalTimeChanged(qint64 uSec);
+
+    void slotSliderMoved(int value);
+
+    void slotTimerTimeOut();
+
+    void slotBtnClick();
 };
 
 #endif // MAINWINDOW_H

+ 109 - 4
src/mainwindow.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>785</width>
-    <height>544</height>
+    <width>832</width>
+    <height>540</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -16,7 +16,112 @@
   <widget class="QWidget" name="centralwidget">
    <layout class="QVBoxLayout" name="verticalLayout">
     <item>
-     <widget class="QWidget" name="widget" native="true"/>
+     <spacer name="verticalSpacer">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>378</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item>
+     <widget class="QWidget" name="widget_controller" native="true">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>32</height>
+       </size>
+      </property>
+      <property name="maximumSize">
+       <size>
+        <width>16777215</width>
+        <height>32</height>
+       </size>
+      </property>
+      <property name="styleSheet">
+       <string notr="true">QWidget#widget_controller
+{
+	background-color: rgba(0, 255, 0, 100);
+}</string>
+      </property>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QPushButton" name="pushButton_open">
+         <property name="text">
+          <string>打开视频</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButton_play">
+         <property name="text">
+          <string>播放</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButton_pause">
+         <property name="text">
+          <string>暂停</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButton_stop">
+         <property name="text">
+          <string>停止</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QSlider" name="horizontalSlider">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout">
+         <item>
+          <widget class="QLabel" name="label_currenttime">
+           <property name="text">
+            <string>00:00:00</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="label_2">
+           <property name="text">
+            <string>/</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="label_totaltime">
+           <property name="text">
+            <string>00:00:00</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+      <zorder>pushButton_play</zorder>
+      <zorder>pushButton_pause</zorder>
+      <zorder>pushButton_stop</zorder>
+      <zorder>horizontalSlider</zorder>
+      <zorder>pushButton_open</zorder>
+     </widget>
     </item>
    </layout>
   </widget>
@@ -25,7 +130,7 @@
     <rect>
      <x>0</x>
      <y>0</y>
-     <width>785</width>
+     <width>832</width>
      <height>23</height>
     </rect>
    </property>

+ 311 - 47
src/videoplayer/videoplayer.cpp

@@ -13,6 +13,8 @@
 #define SDL_AUDIO_BUFFER_SIZE 1024
 #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
 
+#define FLUSH_DATA "FLUSH"
+
 void packet_queue_init(PacketQueue *q) {
     memset(q, 0, sizeof(PacketQueue));
     q->mutex = SDL_CreateMutex();
@@ -82,6 +84,30 @@ static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
     return ret;
 }
 
+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")
+        {
+
+        }
+        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);
+}
+
 static int audio_decode_frame(VideoState *is, double *pts_ptr)
 {
     int len1, len2, decoded_data_size;
@@ -96,11 +122,11 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
 
         while (is->audio_pkt_size > 0) {
 
-//            if (is->isPause == true)
-//            {
-//                SDL_Delay(10);
-//                continue;
-//            }
+            if (is->isPause == true) //判断暂停
+            {
+                SDL_Delay(10);
+                continue;
+            }
 
             if (!is->audio_frame) {
                 if (!(is->audio_frame = avcodec_alloc_frame())) {
@@ -206,32 +232,50 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
             is->audio_clock += (double) resampled_data_size
                     / (double) (n * is->audio_st->codec->sample_rate);
 
+
+            if (is->seek_flag_audio)
+            {
+                //发生了跳转 则跳过关键帧到目的时间的这几帧
+               if (is->audio_clock < is->seek_time)
+               {
+                   break;
+               }
+               else
+               {
+                   is->seek_flag_audio = 0;
+               }
+            }
+
+
             // We have data, return it and come back for more later
             return resampled_data_size;
         }
 
-//        if (is->isPause == true)
-//        {
-//            SDL_Delay(10);
-//            continue;
-//        }
+        if (is->isPause == true) //判断暂停
+        {
+            SDL_Delay(10);
+            continue;
+        }
 
         if (pkt->data)
             av_free_packet(pkt);
         memset(pkt, 0, sizeof(*pkt));
-//        if (is->quit)
-//            return -1;
-        if (packet_queue_get(&is->audioq, pkt, 0) <= 0)
-            return -1;
 
-//        if(pkt->data == is->flush_pkt.data) {
-////fprintf(stderr,"avcodec_flush_buffers(is->audio...\n");
-//        avcodec_flush_buffers(is->audio_st->codec);
-////        fprintf(stderr,"avcodec_flush_buffers(is->audio 222\n");
+        if (is->quit)
+            return -1;
 
-//        continue;
+        if (packet_queue_get(&is->audioq, pkt, 0) <= 0)
+        {
+            return -1;
+        }
 
-//        }
+        //收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下
+        if(strcmp((char*)pkt->data,FLUSH_DATA) == 0)
+        {
+            avcodec_flush_buffers(is->audio_st->codec);
+            av_free_packet(pkt);
+            continue;
+        }
 
         is->audio_pkt_data = pkt->data;
         is->audio_pkt_size = pkt->size;
@@ -249,7 +293,6 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
 static void audio_callback(void *userdata, Uint8 *stream, int len) {
     VideoState *is = (VideoState *) userdata;
 
-//    qDebug()<<"audio_callback...";
     int len1, audio_data_size;
 
     double pts;
@@ -260,42 +303,40 @@ static void audio_callback(void *userdata, Uint8 *stream, int len) {
         /*   这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/
         /*   们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更
          /*   多的桢数据 */
-//        qDebug()<<"audio_decode_frame....";
         if (is->audio_buf_index >= is->audio_buf_size) {
+
             audio_data_size = audio_decode_frame(is, &pts);
+
             /* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */
             if (audio_data_size < 0) {
                 /* silence */
                 is->audio_buf_size = 1024;
                 /* 清零,静音 */
+                if (is->audio_buf == NULL) return;
                 memset(is->audio_buf, 0, is->audio_buf_size);
             } else {
                 is->audio_buf_size = audio_data_size;
             }
             is->audio_buf_index = 0;
         }
-
-//        qDebug()<<"audio_decode_frame finished!";
         /*  查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */
         len1 = is->audio_buf_size - is->audio_buf_index;
         if (len1 > len) {
             len1 = len;
         }
 
+        if (is->audio_buf == NULL) return;
+
         memcpy(stream, (uint8_t *) is->audio_buf + is->audio_buf_index, len1);
 //        SDL_MixAudio(stream, (uint8_t * )is->audio_buf + is->audio_buf_index, len1, 50);
 
 //        SDL_MixAudioFormat(stream, (uint8_t * )is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, 50);
 
-
         len -= len1;
         stream += len1;
         is->audio_buf_index += len1;
     }
 
-//    qDebug()<<"audio_callback finished";
-
-
 }
 
 static double get_audio_clock(VideoState *is)
@@ -508,15 +549,45 @@ int video_thread(void *arg)
 
     while(1)
     {
+        if (is->quit)
+        {
+            break;
+        }
 
-        if (packet_queue_get(&is->videoq, packet, 1) <= 0) break;//队列里面没有数据了  读取完毕了
+        if (is->isPause == true) //判断暂停
+        {
+            SDL_Delay(10);
+            continue;
+        }
+
+        if (packet_queue_get(&is->videoq, packet, 0) <= 0)
+        {
+            if (is->readFinished)
+            {//队列里面没有数据了且读取完毕了
+                break;
+            }
+            else
+            {
+                SDL_Delay(1); //队列只是暂时没有数据而已
+                continue;
+            }
+        }
+
+        //收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下
+        if(strcmp((char*)packet->data,FLUSH_DATA) == 0)
+        {
+            avcodec_flush_buffers(is->video_st->codec);
+            av_free_packet(packet);
+            continue;
+        }
 
         ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
 
-//        if (ret < 0) {
-//            printf("decode error.\n");
-//            return;
-//        }
+        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)
         {
@@ -534,9 +605,35 @@ int video_thread(void *arg)
         video_pts *= av_q2d(is->video_st->time_base);
         video_pts = synchronize_video(is, pFrame, video_pts);
 
+        if (is->seek_flag_video)
+        {
+            //发生了跳转 则跳过关键帧到目的时间的这几帧
+           if (video_pts < is->seek_time)
+           {
+               av_free_packet(packet);
+               continue;
+           }
+           else
+           {
+               is->seek_flag_video = 0;
+           }
+        }
+
         while(1)
         {
+            if (is->quit)
+            {
+                break;
+            }
+
             audio_pts = is->audio_clock;
+
+            //主要是 跳转的时候 我们把video_clock设置成0了
+            //因此这里需要更新video_pts
+            //否则当从后面跳转到前面的时候 会卡在这里
+            video_pts = is->video_clock;
+
+
             if (video_pts <= audio_pts) break;
 
             int delayTime = (video_pts - audio_pts) * 1000;
@@ -566,13 +663,20 @@ int video_thread(void *arg)
     av_free(pFrameRGB);
     av_free(out_buffer_rgb);
 
+    if (!is->quit)
+    {
+        is->quit = true;
+    }
+
+    is->videoThreadFinished = true;
+
     return 0;
 }
 
 
 VideoPlayer::VideoPlayer()
 {
-
+    mPlayerState = Stop;
 }
 
 VideoPlayer::~VideoPlayer()
@@ -580,31 +684,118 @@ VideoPlayer::~VideoPlayer()
 
 }
 
-void VideoPlayer::disPlayVideo(QImage img)
+bool VideoPlayer::setFileName(QString path)
 {
-    emit sig_GetOneFrame(img);  //发送信号
+    if (mPlayerState != Stop)
+    {
+        return false;
+    }
+
+    mFileName = path;
+
+    this->start(); //启动线程
+
+    mPlayerState = Playing;
+
+    return true;
+
 }
 
-void VideoPlayer::startPlay()
+bool VideoPlayer::play()
 {
-    ///调用 QThread 的start函数 将会自动执行下面的run函数 run函数是一个新的线程
-    this->start();
+    mVideoState.isPause = false;
 
+    if (mPlayerState != Pause)
+    {
+        return false;
+    }
+
+    mPlayerState = Playing;
+    emit sig_StateChanged(Playing);
+
+    return true;
 }
 
-void VideoPlayer::run()
+bool VideoPlayer::pause()
+{
+    mVideoState.isPause = true;
+
+    if (mPlayerState != Playing)
+    {
+        return false;
+    }
+
+    mPlayerState = Pause;
+
+    emit sig_StateChanged(Pause);
+
+    return true;
+}
+
+bool VideoPlayer::stop(bool isWait)
 {
-    char *file_path = mFileName.toUtf8().data();
+    if (mPlayerState == Stop)
+    {
+        return false;
+    }
 
+    mVideoState.quit = 1;
 
-    av_register_all(); //初始化FFMPEG  调用了这个才能正常使用编码器和解码器
+    if (isWait)
+    {
+        while(!mVideoState.readThreadFinished || !mVideoState.videoThreadFinished)
+        {
+            SDL_Delay(10);
+        }
+    }
+
+    ///关闭SDL音频播放设备
+    if (mVideoState.audioID != 0)
+    {
+        SDL_LockAudio();
+        SDL_PauseAudioDevice(mVideoState.audioID,1);
+        SDL_UnlockAudio();
 
-    if (SDL_Init(SDL_INIT_AUDIO)) {
-        fprintf(stderr,"Could not initialize SDL - %s. \n", SDL_GetError());
-        exit(1);
+        mVideoState.audioID = 0;
     }
 
+    mPlayerState = Stop;
+    emit sig_StateChanged(Stop);
 
+    return true;
+}
+
+void VideoPlayer::seek(int64_t pos)
+{
+    if(!mVideoState.seek_req)
+    {
+        mVideoState.seek_pos = pos;
+        mVideoState.seek_req = 1;
+    }
+}
+
+double VideoPlayer::getCurrentTime()
+{
+    return mVideoState.audio_clock;
+}
+
+int64_t VideoPlayer::getTotalTime()
+{
+    return mVideoState.ic->duration;
+}
+
+void VideoPlayer::disPlayVideo(QImage img)
+{
+    emit sig_GetOneFrame(img);  //发送信号
+}
+
+void VideoPlayer::run()
+{
+    char file_path[1280] = {0};;
+
+    strcpy(file_path,mFileName.toUtf8().data());
+
+    memset(&mVideoState,0,sizeof(VideoState)); //为了安全起见  先将结构体的数据初始化成0了
 
     VideoState *is = &mVideoState;
 
@@ -612,7 +803,6 @@ void VideoPlayer::run()
     AVCodecContext *pCodecCtx;
     AVCodec *pCodec;
 
-
     AVCodecContext *aCodecCtx;
     AVCodec *aCodec;
 
@@ -659,6 +849,10 @@ void VideoPlayer::run()
     }
 
     is->ic = pFormatCtx;
+    is->videoStream = videoStream;
+    is->audioStream = audioStream;
+
+    emit sig_TotalTimeChanged(getTotalTime());
 
     if (audioStream >= 0) {
         /* 所有设置SDL音频流信息的步骤都在这个函数里完成 */
@@ -715,6 +909,51 @@ void VideoPlayer::run()
 
     while (1)
     {
+        if (is->quit) { //停止播放了
+            break;
+        }
+
+        if (is->seek_req)
+        {
+            int stream_index = -1;
+            int64_t seek_target = is->seek_pos;
+
+            if (is->videoStream >= 0)
+                stream_index = is->videoStream;
+            else if (is->audioStream >= 0)
+                stream_index = is->audioStream;
+
+            AVRational aVRational = {1, AV_TIME_BASE};
+            if (stream_index >= 0) {
+                seek_target = av_rescale_q(seek_target, aVRational,
+                        pFormatCtx->streams[stream_index]->time_base);
+            }
+
+            if (av_seek_frame(is->ic, stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0) {
+                fprintf(stderr, "%s: error while seeking\n",is->ic->filename);
+            } else {
+                if (is->audioStream >= 0) {
+                    AVPacket *packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
+                    av_new_packet(packet, 10);
+                    strcpy((char*)packet->data,FLUSH_DATA);
+                    packet_queue_flush(&is->audioq); //清除队列
+                    packet_queue_put(&is->audioq, packet); //往队列中存入用来清除的包
+                }
+                if (is->videoStream >= 0) {
+                    AVPacket *packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
+                    av_new_packet(packet, 10);
+                    strcpy((char*)packet->data,FLUSH_DATA);
+                    packet_queue_flush(&is->videoq); //清除队列
+                    packet_queue_put(&is->videoq, packet); //往队列中存入用来清除的包
+                    is->video_clock = 0;
+                }
+            }
+            is->seek_req = 0;
+            is->seek_time = is->seek_pos / 1000000.0;
+            is->seek_flag_audio = 1;
+            is->seek_flag_video = 1;
+        }
+
         //这里做了个限制  当队列里面的数据超过某个大小的时候 就暂停读取  防止一下子就把视频读完了,导致的空间分配不足
         /* 这里audioq.size是指队列中的所有数据包带的音频数据的总量或者视频数据总量,并不是包的数量 */
         //这个值可以稍微写大一些
@@ -723,9 +962,23 @@ void VideoPlayer::run()
             continue;
         }
 
+        if (is->isPause == true)
+        {
+            SDL_Delay(10);
+            continue;
+        }
+
         if (av_read_frame(pFormatCtx, packet) < 0)
         {
-            break; //这里认为视频读取完了
+            is->readFinished = true;
+
+            if (is->quit)
+            {
+                break; //解码线程也执行完了 可以退出了
+            }
+
+            SDL_Delay(10);
+            continue;
         }
 
         if (packet->stream_index == videoStream)
@@ -745,6 +998,17 @@ void VideoPlayer::run()
         }
     }
 
+    ///文件读取结束 跳出循环的情况
+    ///等待播放完毕
+    while (!is->quit) {
+        SDL_Delay(100);
+    }
+
+    stop();
+
     avcodec_close(pCodecCtx);
     avformat_close_input(&pFormatCtx);
+
+    is->readThreadFinished = true;
+
 }

+ 36 - 5
src/videoplayer/videoplayer.h

@@ -19,7 +19,6 @@ extern "C"
     #include "libswscale/swscale.h"
     #include "libswresample/swresample.h"
 
-
     #include <SDL.h>
     #include <SDL_audio.h>
     #include <SDL_types.h>
@@ -46,6 +45,7 @@ class VideoPlayer; //前置声明
 
 typedef struct VideoState {
     AVFormatContext *ic;
+    int videoStream, audioStream;
     AVFrame *audio_frame;// 解码音频过程中的使用缓存
     PacketQueue audioq;
     AVStream *audio_st; //音频流
@@ -73,6 +73,19 @@ typedef struct VideoState {
     AVStream *video_st;
     PacketQueue videoq;
 
+    /// 跳转相关的变量
+    int             seek_req; //跳转标志
+    int64_t         seek_pos; //跳转的位置 -- 微秒
+    int             seek_flag_audio;//跳转标志 -- 用于音频线程中
+    int             seek_flag_video;//跳转标志 -- 用于视频线程中
+    double          seek_time; //跳转的时间(秒)  值和seek_pos是一样的
+
+    ///播放控制相关
+    bool isPause;  //暂停标志
+    bool quit;  //停止
+    bool readFinished; //文件读取完毕
+    bool readThreadFinished;
+    bool videoThreadFinished;
 
     SDL_Thread *video_tid;  //视频线程id
     SDL_AudioDeviceID audioID;
@@ -86,28 +99,46 @@ class VideoPlayer : public QThread
     Q_OBJECT
 
 public:
+
+    enum PlayerState
+    {
+        Playing,
+        Pause,
+        Stop
+    };
+
     explicit VideoPlayer();
     ~VideoPlayer();
 
-    void setFileName(QString path){mFileName = path;}
+    bool setFileName(QString path);
+
+    bool play();
+    bool pause();
+    bool stop(bool isWait = false); //参数表示是否等待所有的线程执行完毕再返回
 
-    void startPlay();
+    void seek(int64_t pos); //单位是微秒
+
+    int64_t getTotalTime(); //单位微秒
+    double getCurrentTime(); //单位秒
 
     void disPlayVideo(QImage img);
 
 signals:
     void sig_GetOneFrame(QImage); //没获取到一帧图像 就发送此信号
 
+    void sig_StateChanged(VideoPlayer::PlayerState state);
+    void sig_TotalTimeChanged(qint64 uSec); //获取到视频时长的时候激发此信号
+
 protected:
     void run();
 
 private:
     QString mFileName;
 
-
-
     VideoState mVideoState;
 
+    PlayerState mPlayerState; //播放状态
+
 };
 
 #endif // VIDEOPLAYER_H

+ 2 - 2
说明.txt

@@ -1,7 +1,7 @@
 这是Qt的工程,建议使用Qt Creator 打开
 
 
-从零开始学习音视频编程技术(九) FFMPEG Qt视频播放器之同步进阶篇
+从零开始学习音视频编程技术(十) FFMPEG Qt视频播放器之播放控制
 
 FFMPEG的版本是2.5.2
 SDL的版本是2.04
@@ -10,7 +10,7 @@ SDL
 
 
 关于代码的解释 请参考:
-http://blog.yundiantech.com/?log=blog&id=12
+http://blog.yundiantech.com/?log=blog&id=13
 
 
 Qt开发环境的搭建 请参考: