FFMPEG填坑之旅(叁----视频播放(四))

722 阅读2分钟

前面讲了画面输出音频输出,现在来说说多线程输出视频。

这一部分,说起来会比较简单,但是做起来会比较长。

首先,要说多线程,C里面推荐使用pthread,直接使用pthread_create就能创建一个线程。关于pthread的使用,其他时间有空再说吧。这里主要是要传入一个指针函数,用于定义线程中的逻辑。

然后,要创建几个数据队列,用于存放画面和音频的数据。队列这个数据结构应该网上一搜一大把,就不重复说明了

Player *player = (Player *) arg;
AVPacket *packet = av_packet_alloc();
for (;;) {
    pthread_mutex_lock(&seek_mutex);
    while (is_seek) {
        LOGE("Player Log : produce waiting seek");
        pthread_cond_wait(&seek_condition, &seek_mutex);
        LOGE("Player Log : produce wake up seek");
    }
    pthread_mutex_unlock(&seek_mutex);
    if (av_read_frame(player->format_context, packet) < 0) {
        break;
    }
    if (packet->stream_index == player->video_stream_index) {
        queue_in(player->video_queue, packet);
    } else if (packet->stream_index == player->audio_stream_index) {
        queue_in(player->audio_queue, packet);
    }
    packet = av_packet_alloc();
}
break_block(player->video_queue);
break_block(player->audio_queue);
for (;;) {
    if (queue_is_empty(player->video_queue) && queue_is_empty(player->audio_queue)) {
        break;
    }
    sleep(1);
}
player_release(player);
return NULL;

这里控制了队列大小,如果超限输入就会阻塞,等待消费掉再继续读入。

最后,就是要用一个线程来读数据,分别存到上面创建的队列里面,再用对应数量的线程来解码和输出画面和音频等。

pthread_create(&produce_id, NULL, produce, player);
Consumer *video_consumer = (Consumer *) malloc(sizeof(Consumer));
video_consumer->player = player;
video_consumer->stream_index = player->video_stream_index;
pthread_create(&video_consume_id, NULL, consume, video_consumer);
Consumer *audio_consumer = (Consumer *) malloc(sizeof(Consumer));
audio_consumer->player = player;
audio_consumer->stream_index = player->audio_stream_index;
pthread_create(&audio_consume_id, NULL, consume, audio_consumer);

produce是个指针函数,指向读取方法 Consumer里面集成前两节说的输出画面和音频,这样画面和音频就同时输出完成了。当然,差异是会有的,比如读入:

pthread_mutex_lock(&seek_mutex);
while (is_seek) {
    pthread_cond_wait(&seek_condition, &seek_mutex);
}
pthread_mutex_unlock(&seek_mutex);
AVPacket *packet = queue_out(queue);
if (packet == NULL) {
    LOGE("consume packet is null");
    break;
}

不再是从流里面直接读入,而是通过缓存队列取数据,但是其他的操作是一样的。

这样视频播放就说完了。

再编码和filter这些,后续研究了再继续。

待续...不定期更新了