Knuth谈现代编程

![Alt none]http://wangsijie.me/assets/images/Knuth谈现代编程/IMG_0179.jpg)

FFMPEG 中的时间

视频的显示和存放原理

对于一个电影,帧是这样来显示的:I B B P。现在我们需要在显示B帧之前知道P帧中的信息。因此,帧可能会按照这样的方式来存储:IPBB。这就是为什么我们会有一个解码时间戳和一个显示时间戳的原因。解码时间戳告诉我们什么时候需要解码,显示时间戳告诉我们什么时候需要显示。所以,在这种情况下,我们的流可以是这样的:

PTS: 1 4 2 3
DTS: 1 2 3 4
Stream: I P B B

通常PTS和DTS只有在流中有B帧的时候会不同。

DTS和PTS

音频和视频流都有一些关于以多快速度和什么时间来播放它们的信息在里面。音频流有采样,视频流有每秒的帧率。然而,如果我们只是简单的通过数帧和乘以帧率的方式来同步视频,那么就很有可能会失去同步。于是作为一种补充,在流中的包有种叫做DTS(解码时间戳)和PTS(显示时间戳)的机制。为了这两个参数,你需要了解电影存放的方式。像MPEG等格式,使用被叫做B帧(B表示双向bidrectional)的方式。另外两种帧被叫做I帧和P帧(I表示关键帧,P表示预测帧)。I帧包含了某个特定的完整图像。P帧依赖于前面的I帧和P帧并且使用比较或者差分的方式来编码。B帧与P帧有点类似,但是它是依赖于前面和后面的帧的信息的。这也就解释了为什么我们可能在调用avcodec_decode_video以后会得不到一帧图像。

ffmpeg中的时间单位

AV_TIME_BASE

ffmpeg中的内部计时单位(时间基),ffmepg中的所有时间都是于它为一个单位,比如AVStream中的duration即以为着这个流的长度为duration个AV_TIME_BASE。AV_TIME_BASE定义为:

#define 	AV_TIME_BASE   1000000

AV_TIME_BASE_Q

ffmpeg内部时间基的分数表示,实际上它是AV_TIME_BASE的倒数。从它的定义能很清楚的看到这点:

#define 	AV_TIME_BASE_Q   (AVRational){1, AV_TIME_BASE}

AVRatioal的定义如下:

typedef struct AVRational{
int num; //numerator
int den; //denominator
} AVRational;

ffmpeg提供了一个把AVRatioal结构转换成double的函数:

static inline double av_q2d(AVRational a){
/**
* Convert rational to double.
* @param a rational to convert
**/

return a.num / (double) a.den;
}

现在可以根据pts来计算一桢在整个视频中的时间位置:

timestamp(秒) = pts * av_q2d(st->time_base)

计算视频长度的方法:

time(秒) = st->duration * av_q2d(st->time_base)

这里的st是一个AVStream对象指针。

时间基转换公式

timestamp(ffmpeg内部时间戳) = AV_TIME_BASE * time(秒)
time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg内部时间戳)

所以当需要把视频跳转到N秒的时候可以使用下面的方法:

int64_t timestamp = N * AV_TIME_BASE; 
av_seek_frame(fmtctx, index_of_video, timestamp, AVSEEK_FLAG_BACKWARD);

ffmpeg同样为我们提供了不同时间基之间的转换函数:

int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)

这个函数的作用是计算a * bq / cq,来把时间戳从一个时基调整到另外一个时基。在进行时基转换的时候,我们应该首选这个函数,因为它可以避免溢出的情况发生。

使用QGraphicsProxyWidget实现字幕滚动和翻转动画

OS : ubuntu 12.04 QT : 4.8

multiTxtWidget.h

#ifndef MULTITEX_WIDGET_H
#define MULTITEX_WIDGET_H

#include <QGraphicsItem>
#include <QPainter>
#include <QPropertyAnimation>
#include <QGraphicsWidget>
#include <QGraphicsProxyWidget>
#include <QWidget>
#include <qcoreevent.h>

class MulitTexWidget : public QWidget {
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE setText)

public:
    explicit MulitTexWidget(QWidget *parent = 0);
    virtual ~MulitTexWidget();

    void setText(const QString &newText);
    QString text();
    QSize sizeHint() const;

protected:
    void paintEvent(QPaintEvent *event);
    void timerEvent(QTimerEvent *event);
    void showEvent(QShowEvent *event);
    void hideEvent(QHideEvent *event);

private:
    QString myText;
    int offset;
    int myTimerId;
};

class GraphicsWidget : public QGraphicsProxyWidget
{
public:
    GraphicsWidget() : QGraphicsProxyWidget(0) {}
protected:
    virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);
    virtual void resizeEvent(QGraphicsSceneResizeEvent *event);
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};

class TwoSidedGraphicsWidget : public QObject
{
    Q_OBJECT
public:
    TwoSidedGraphicsWidget(QGraphicsScene *scene);
    void setWidget(int index, QWidget *widget);
    QWidget *widget(int index);
public slots:
    void flip();
protected slots:
    void animateFlip();
private:
    GraphicsWidget *m_proxyWidgets[2];
    int m_current;
    int m_angle;
    int m_delta;
};

#endif // MULTITEX_WIDGET_H

multiTxtWidget.cpp

#include "multiTex_widget.h"
#include <QGraphicsOpacityEffect>
#include <QGraphicsScene>
#include <QTimer>

MulitTexWidget::MulitTexWidget(QWidget *parent) :
    QWidget(parent)
{
    offset = 0;
    myTimerId = 0;
    setWindowOpacity(0.5);
}

MulitTexWidget::~MulitTexWidget() {

}

QString MulitTexWidget::text() {
    return myText;
}

void MulitTexWidget::setText(const QString &newText) {
    myText = newText;
    update();
    updateGeometry();
}

QSize MulitTexWidget::sizeHint() const {
    return fontMetrics().size(0, text());
}

void MulitTexWidget::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
 // painter.setFont(QFont("Helvetica", 20));
    painter.setPen(QPen(Qt::blue, 1));

    int textWidth = fontMetrics().width(text());
    if(textWidth < 1) {
        return;
    }
    int x = -offset;
    while(x < width()) {
        painter.drawText(x, 0, textWidth, height() ,Qt::AlignLeft | Qt::AlignVCenter, text());
        x += textWidth;
    }
}

void MulitTexWidget::showEvent(QShowEvent *event) {
    myTimerId = startTimer(50);
}

void MulitTexWidget::timerEvent(QTimerEvent *event) {
    if(event->timerId() == myTimerId) {
        offset++;
        if(offset >= fontMetrics().width(text())) {
            offset = 0;
        }
        scroll(-1, 0);
    }  else {
        QWidget::timerEvent(event);
    }
}

void MulitTexWidget::hideEvent(QHideEvent *event) {
    killTimer(myTimerId);
    myTimerId = 0;
}

QVariant GraphicsWidget::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if (change == ItemPositionChange && scene()) {
        QRectF rect = boundingRect();
        QPointF pos = value.toPointF();
        QRectF sceneRect = scene()->sceneRect();
        if (pos.x() + rect.left() < sceneRect.left())
            pos.setX(sceneRect.left() - rect.left());
        else if (pos.x() + rect.right() >= sceneRect.right())
            pos.setX(sceneRect.right() - rect.right());
        if (pos.y() + rect.top() < sceneRect.top())
            pos.setY(sceneRect.top() - rect.top());
        else if (pos.y() + rect.bottom() >= sceneRect.bottom())
            pos.setY(sceneRect.bottom() - rect.bottom());
        return pos;
    }
    return QGraphicsProxyWidget::itemChange(change, value);
}

void GraphicsWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    setCacheMode(QGraphicsItem::NoCache);
    setCacheMode(QGraphicsItem::ItemCoordinateCache);
    QGraphicsProxyWidget::resizeEvent(event);
}

void GraphicsWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setRenderHint(QPainter::Antialiasing, false);
    QGraphicsProxyWidget::paint(painter, option, widget);
}

TwoSidedGraphicsWidget::TwoSidedGraphicsWidget(QGraphicsScene *scene)
    : QObject(scene)
    , m_current(0)
    , m_angle(0)
    , m_delta(0)
{
    for (int i = 0; i < 2; ++i)
        m_proxyWidgets[i] = 0;
}

void TwoSidedGraphicsWidget::setWidget(int index, QWidget *widget)
{
    if (index < 0 || index >= 2)
    {
        qWarning("TwoSidedGraphicsWidget::setWidget: Index out of bounds, index == %d", index);
        return;
    }

    GraphicsWidget *proxy = new GraphicsWidget;
    proxy->setWidget(widget);

    if (m_proxyWidgets[index])
        delete m_proxyWidgets[index];
    m_proxyWidgets[index] = proxy;

    proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache);
    proxy->setZValue(1e30);

    if (index != m_current)
        proxy->setVisible(false);

    qobject_cast<QGraphicsScene *>(parent())->addItem(proxy);
}

QWidget *TwoSidedGraphicsWidget::widget(int index)
{
    if (index < 0 || index >= 2)
    {
        qWarning("TwoSidedGraphicsWidget::widget: Index out of bounds, index == %d", index);
        return 0;
    }
    return m_proxyWidgets[index]->widget();
}

void TwoSidedGraphicsWidget::flip()
{
    m_delta = (m_current == 0 ? 9 : -9);
    animateFlip();
}

void TwoSidedGraphicsWidget::animateFlip()
{
    m_angle += m_delta;
    if (m_angle == 90) {
        int old = m_current;
        m_current ^= 1;
        m_proxyWidgets[old]->setVisible(false);
        m_proxyWidgets[m_current]->setVisible(true);
        m_proxyWidgets[m_current]->setGeometry(m_proxyWidgets[old]->geometry());
    }

    QRectF r = m_proxyWidgets[m_current]->boundingRect();
    m_proxyWidgets[m_current]->setTransform(QTransform()
        .translate(r.width() / 2, r.height() / 2)
        .rotate(m_angle - 180 * m_current, Qt::XAxis)
        .translate(-r.width() / 2, -r.height() / 2));

    if ((m_current == 0 && m_angle > 0) || (m_current == 1 && m_angle < 180))
        QTimer::singleShot(25, this, SLOT(animateFlip()));
}

main.cpp

#include "multiTex_widget.h"
#include <QApplication>
#include <QTextCodec>
#include <QGLWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);
    QTextCodec::setCodecForTr(codec);

    QGraphicsScene scene;

    MulitTexWidget *mtw1 = new MulitTexWidget;
    mtw1->setText("       I am your friend      ");
    mtw1->setWindowFlags(Qt::FramelessWindowHint);
    mtw1->setGeometry(0, 0, 800, 40);

    MulitTexWidget *mtw2 = new MulitTexWidget;
    mtw2->setText("    yes, you are my friend    ");
    mtw2->setWindowFlags(Qt::FramelessWindowHint);
    mtw2->setGeometry(0, 0, 800, 40);

    TwoSidedGraphicsWidget *twoSided = new TwoSidedGraphicsWidget(&scene);
    twoSided->setWidget(0, mtw1);
    twoSided->setWidget(1, mtw2);

    QTimer *tm = new QTimer;
    tm->setInterval(2000);

    QObject::connect(tm, SIGNAL(timeout()), twoSided, SLOT(flip()));

    QtGraphicsView view(&scene);   
    view.setFrameShape(QFrame::NoFrame);

    view.show();
    tm->start();

    return a.exec();
}

PS:此方法效果不错,但是效率较低,cpu占用高,主要瓶颈在滚动字幕的重绘上。

基于QGraphicsItem的字幕翻转动画效果

OS: mac os x 10.7.5 QT: 5.0.1

main.cpp

#include "mainwindow.h"
#include <QApplication>
#include <QGraphicsScene>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    TxtItem txtItem;
    txtItem.setText("How are you, my friend!!");

    QGraphicsScene scene;
    MyQView mqv(&scene);

    scene.addItem(&txtItem);

    mqv.show();

    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QTimer>

class MyQView : public QGraphicsView {
    Q_OBJECT
public:
    MyQView(QGraphicsScene *scene);
    ~MyQView();
};

class TxtItem : public QObject, public QGraphicsItem {
    Q_OBJECT
public:
    explicit TxtItem(QGraphicsItem *parent = 0);
    ~TxtItem();

   void paint(QPainter * painter,
              const QStyleOptionGraphicsItem * option,
              QWidget * widget = 0);
   QRectF boundingRect () const;
   void setText(const QString &newText);

public slots:
   void flip();

private slots:
   void animateFilp();

private:
    QTimer *tm;
    int m_angle;   //rotation angle
    int m_delta;
    int m_current; //store rotation direction
    QString m_text;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QTimer>

MyQView::MyQView(QGraphicsScene *scene) : QGraphicsView(scene) {
    setGeometry(100, 100, 800, 600);
    setWindowFlags(Qt::FramelessWindowHint);
}

MyQView::~MyQView() {

}

TxtItem::TxtItem(QGraphicsItem *parent)
    : QGraphicsItem(parent) {

    m_angle = 0;
    m_delta = 0;
    m_current = 0;
    setOpacity(0.5);

    tm = new QTimer;
    tm->setInterval(2000);
    connect(tm, SIGNAL(timeout()), this, SLOT(flip()));
    tm->start();
}

TxtItem::~TxtItem() {
    tm->stop();
    delete tm;
}

void TxtItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
    QBrush brush(Qt::blue);
    painter->setBrush(brush);

    painter->drawRect(boundingRect());
    painter->setPen(QPen(Qt::red, 10));
    painter->drawText(boundingRect().x(), boundingRect().y(),
                      boundingRect().width(), boundingRect().height(),
                      Qt::AlignLeft | Qt::AlignVCenter, m_text);
}

QRectF TxtItem::boundingRect() const {
    return QRectF(0, 0, 600, 300);
}

void TxtItem::setText(const QString &newText) {
    m_text = newText;
}

void TxtItem::animateFilp() {
    m_angle += m_delta;

    if(180 == m_angle) {
        m_current ^= 1;
    }

    QRectF r = boundingRect();
    setTransform(QTransform()
                 .translate(r.width() / 2, r.height() /2)
                 .rotate(m_angle, Qt::XAxis)
                 .translate(-r.width() / 2, -r.height() / 2));
    if((0 == m_current && m_angle > 0) || (1 == m_current && m_angle < 360)) {
        QTimer::singleShot(25, this, SLOT(animateFilp()));
    }
}

void TxtItem::flip() {
    m_delta = (m_current == 0 ? 9 : -9);
    animateFilp();
}

linux socket常规操作

/*server*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/wait.h>

#define PORT 3333
#define BACKLOG 10
#define DATASIZE 10

int main(int argc, char **argv) {
	int sockfd, client_fd;
	struct sockaddr_in serv_addr;
	struct sockaddr_in remote_addr;
	char buf[DATASIZE];
	int recv_len;

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket error");
		exit(1);
	}

	bzero(&(serv_addr), sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(PORT);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	int opt = 1;
	if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(int)) == -1) {
		perror("setsocket error");
		exit(1);
	}

	if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr)) == -1) {
		perror("bind error");
		exit(1);
	}

	if(listen(sockfd, BACKLOG) == -1) {
		perror("listen error");
		exit(1);
	}

	size_t sin_size = sizeof(struct sockaddr_in);
	if((client_fd = accept(sockfd, (struct sockaddr*)&remote_addr, &sin_size)) == -1) {
		perror("accept error");
		exit(1);
	}
	printf("receive a connection from %s\n", inet_ntoa(remote_addr.sin_addr));

	while(1) {
		bzero(buf, sizeof(buf));
		recv_len = recv(client_fd, buf, DATASIZE, 0);
		//buf[DATASIZE] = '\n';
		fprintf(stdout, "%s", buf);
	}
	close(sockfd);
}
telnet localhost 3333
/*client*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 3333
#define MAXDATASIZE 100

int main(int argc, char **argv) {
	int sockfd, recvbytes;
	char buf[MAXDATASIZE];
	struct sockaddr_in serv_addr;
	struct hostent *host;

	if(argc < 2) {
		fprintf(stderr, "Please enter the server's hostname\n");
		exit(1);
	}

	if((host = gethostbyname(argv[1])) == NULL) {
		perror("gethostbyname error");
		exit(1);
	}

	printf("host name: %s\n", host->h_name);
	printf("host address: %s\n", inet_ntoa(*((struct in_addr*)host->h_addr)));

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket error");
		exit(1);
	}

	bzero(&serv_addr, sizeof(struct sockaddr_in));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(PORT);
	serv_addr.sin_addr = *((struct in_addr*)host->h_addr);

	if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr)) == -1) {
		perror("bind error");
		exit(1);
	}

	if((recvbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
		perror("recv error");
		exit(1);
	}

	buf[recvbytes] = '\0';
	printf("Recvived: %s", buf);

	close(sockfd);
}