Ffmpeg和SDL教程(七)快进快退

本文来自:http://blog.csdn.net/jinhaijian/archive/2010/09/02/5859648.aspx

 

处理快进快退命令

现在我们来为我们的播放器加入一些快进和快退的功能,因为如果你不能全局搜索一部电影是很让人讨厌的。同时,这将告诉你av_seek_frame函数是多么容易使用。

我们将在电影播放中使用左方向键和右方向键来表示向后和向前一小段,使用向上和向下键来表示向前和向后一大段。这里一小段是10秒,一大段是60 秒。所以我们需要设置我们的主循环来捕捉键盘事件。然而当我们捕捉到键盘事件后我们不能直接调用av_seek_frame函数。我们要主要的解码线程 decode_thread的循环中做这些。所以,我们要添加一些变量到大结构体中,用来包含新的跳转位置和一些跳转标志:

int seek_req;

int seek_flags;

int64_t seek_pos;

现在让我们在主循环中捕捉按键:

for(;;) {

double incr, pos;

SDL_WaitEvent(&event);

switch(event.type) {

case SDL_KEYDOWN:

switch(event.key.keysym.sym) {

case SDLK_LEFT:

incr = -10.0;

goto do_seek;

case SDLK_RIGHT:

incr = 10.0;

goto do_seek;

case SDLK_UP:

incr = 60.0;

goto do_seek;

case SDLK_DOWN:

incr = -60.0;

goto do_seek;

do_seek:

if(global_video_state) {

pos = get_master_clock(global_video_state);

pos += incr;

stream_seek(global_video_state,

(int64_t)(pos * AV_TIME_BASE), incr);

}

break;

default:

break;

}

break;

为了检测按键,我们先查了一下是否有SDL_KEYDOWN事件。然后我们使用event.key.keysym.sym来判断哪个按键被按下。一旦我们知道了如何来跳转,我们就来计算新的时间,方法为把增加的时间值加到从函数get_master_clock中得到的时间值上。然后我们调用 stream_seek函数来设置seek_pos等变量。我们把新的时间转换成为avcodec中的内部时间戳单位。在流中调用那个时间戳将使用帧而不是用秒来计算,公式为seconds = frames * time_base(fps)。默认的avcodec值为1,000,000fps(所以2秒的内部时间戳为2,000,000)。在后面我们来看一下为什么要把这个值进行一下转换。

这就是我们的stream_seek函数。请注意我们设置了一个标志为后退服务:

void stream_seek(VideoState *is, int64_t pos, int rel) {

if(!is->seek_req) {

is->seek_pos = pos;

is->seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0;

is->seek_req = 1;

}

}

现在让我们看一下如果在decode_thread中实现跳转。你会注意到我们已经在源文件中标记了一个叫做“seek stuff goes here”的部分。现在我们将把代码写在这里。

跳转是围绕着av_seek_frame函数的。这个函数用到了一个格式上下文,一个流,一个时间戳和一组标记来作为它的参数。这个函数将会跳转到你所给的时间戳的位置。时间戳的单位是你传递给函数的流的时基time_base。然而,你并不是必需要传给它一个流(流可以用-1来代替)。如果你这样做了,时基time_base将会是avcodec中的内部时间戳单位,或者是1000000fps。这就是为什么我们在设置seek_pos的时候会把位置乘以AV_TIME_BASER的原因。

但是,如果给av_seek_frame函数的stream参数传递传-1,你有时会在播放某些文件的时候遇到问题(比较少见),所以我们会取文件中的第一个流并且把它传递到av_seek_frame函数。不要忘记我们也要把时间戳timestamp的单位进行转化。

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;

if(stream_index>=0){

seek_target= av_rescale_q(seek_target, AV_TIME_BASE_Q,

pFormatCtx->streams[stream_index]->time_base);

}

if(av_seek_frame(is->pFormatCtx, stream_index,

seek_target, is->seek_flags) < 0) {

fprintf(stderr, "%s: error while seeking/n",

is->pFormatCtx->filename);

} else {

这里av_rescale_q(a,b,c)是用来把时间戳从一个时基调整到另外一个时基时候用的函数。它基本的动作是计算a*b/c,但是这个函数还是必需的,因为直接计算会有溢出的情况发生。

AV_TIME_BASE_Q是AV_TIME_BASE作为分母后的版本。它们是很不相同的:AV_TIME_BASE * time_in_seconds = avcodec_timestamp而AV_TIME_BASE_Q * avcodec_timestamp = time_in_seconds(注意AV_TIME_BASE_Q实际上是一个AVRational对象,所以你必需使用avcodec中特定的q函数来处理它)。

清空我们的缓冲

我们已经正确设定了跳转位置,但是我们还没有结束。记住我们有一个堆放了很多包的队列。既然我们跳到了不同的位置,我们必需把队列中的内容清空否则电影是不会跳转的。不仅如此,avcodec也有它自己的内部缓冲,也需要每次被清空。

要实现这个,我们需要首先写一个函数来清空我们的包队列。然后我们需要一种命令声音和视频线程来清空avcodec内部缓冲的办法。我们可以在清空队列后把特定的包放入到队列中,然后当它们检测到特定的包的时候,它们就会把自己的内部缓冲清空。

让我们开始写清空函数。其实很简单的,所以我直接把代码写在下面:

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;

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);

}

既然队列已经清空了,我们放入“清空包”。但是开始我们要定义和创建这个包:

AVPacket flush_pkt;

main() {

...

av_init_packet(&flush_pkt);

flush_pkt.data = "FLUSH";

...

}

现在我们把这个包放到队列中:

} else {

if(is->audioStream >= 0) {

packet_queue_flush(&is->audioq);

packet_queue_put(&is->audioq, &flush_pkt);

}

if(is->videoStream >= 0) {

packet_queue_flush(&is->videoq);

packet_queue_put(&is->videoq, &flush_pkt);

}

}

is->seek_req = 0;

}

(这些代码片段是接着前面decode_thread中的代码片段的)我们也需要修改

packet_queue_put函数才不至于直接简单复制了这个包:

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

AVPacketList *pkt1;

if(pkt != &flush_pkt && av_dup_packet(pkt) < 0) {

return -1;

}

然后在声音线程和视频线程中,我们在packet_queue_get后立即调用函数avcodec_flush_buffers:

if(packet_queue_get(&is->audioq, pkt, 1) < 0) {

return -1;

}

if(packet->data == flush_pkt.data) {

avcodec_flush_buffers(is->audio_st->codec);

continue;

}

上面的代码片段与视频线程中的一样,只要把“audio”换成“video”。

就这样,让我们编译我们的播放器:

gcc -o tutorial07 tutorial07.c -lavutil -lavformat -lavcodec -lz -lm`sdl-config --cflags --libs`

试一下!我们几乎已经都做完了;下次我们只要做一点小的改动就好了,那就是检测ffmpeg提供的小的软件缩放采样。

 

### 回答1: FFmpeg是一个开源的跨平台多媒体处理工具,可以用来编码、解码、转码和播放各种音频和视频文件。SDL(Simple DirectMedia Layer)是一个跨平台的多媒体库,可以用来处理音频、视频和输入设备。 FFmpegSDL可以结合使用来实现一个简单的媒体播放器。首先,我们需要使用FFmpeg来解码音频和视频文件。通过FFmpeg的解码功能,我们可以将音频和视频数据解析出来,然后就可以利用SDL将它们播放出来。 在SDL中,我们可以创建一个音频流和一个视频流,并将解码后的音频和视频数据分别写入其中。SDL会负责将这些数据渲染到音频设备和视频窗口,从而实现播放效果。我们可以通过控制音频和视频流的缓冲区大小和时钟同步来实现音视频的同步播放。 另外,我们还可以利用FFmpeg的一些其他功能来提升播放器的性能和功能。例如,可以使用FFmpeg的过滤器功能来实现音频和视频的裁剪、旋转、缩放等操作。也可以利用FFmpeg的网络协议支持来播放网络上的音频和视频流。 总之,FFmpegSDL可以组成一个简单但功能强大的媒体播放器。通过使用FFmpeg的解码功能和SDL的渲染功能,我们可以实现音视频的解码和播放。而且,FFmpeg提供了许多其他的功能,比如过滤器、网络协议支持等,可以让我们的播放器更加灵活和强大。 ### 回答2: ffmpeg sdl播放器是一种基于ffmpegSDL库开发的音视频播放器。ffmpeg是一种开源的多媒体处理库,可以对音视频数据进行解码、编码、转码等操作。SDL是一种跨平台的多媒体开发库,可以实现多媒体的显示、音频播放、事件处理等功能。 使用ffmpeg sdl播放器可以实现对各种音视频格式的播放。首先,它可以将各种格式的音视频文件进行解码,将数据转换成可供显示和播放的格式。然后,通过SDL库可以将解码后的音频数据进行声音的播放,并将视频数据进行显示。 此外,ffmpeg sdl播放器还支持音视频的同步播放。它会根据视频帧的时间戳来计算音频数据的播放时间,从而实现音视频的同步播放。同时,它还能够处理音视频的各种事件,如播放暂停、进、退等。 ffmpeg sdl播放器还具有良好的扩展性和可定制性。开发者可以根据自己的需求,进行特定功能的定制和扩展,如添加字幕显示、视频特效等功能。 总的来说,ffmpeg sdl播放器是一个功能强大、灵活性高的音视频播放器。它可以支持各种音视频格式,实现音视频的同步播放,并且具有良好的扩展性。无论是在桌面应用还是移动应用中,ffmpeg sdl播放器都是一个理想的选择。 ### 回答3: ffmpeg sdl播放器是一款基于FFmpegSDL(Simple DirectMedia Layer)库开发的视频播放器。通过FFmpeg库,它可以解码各种视频格式,并且还支持音频解码功能。SDL库则提供了跨平台的图形、声音、事件处理等功能。 这款播放器具有以下特点和功能: 1. 跨平台性能:FFmpegSDL都是跨平台的库,因此该播放器可以在多个操作系统(如Windows、Mac OS、Linux等)上运行,并且具有良好的性能和稳定性。 2. 支持多种视频格式:FFmpeg库提供了广泛的视频格式支持,如AVI、MP4、MKV等,因此该播放器可以播放多种常见的视频文件。 3. 支持多种音频格式:除了视频,该播放器还支持音频解码,可以播放多种音频格式,如MP3、AAC、FLAC等。 4. 播放控制和界面:播放器提供基本的播放控制,如播放、暂停、进、退等功能,同时还有播放进度条和音量控制条,用户可以根据需求进行设置。 5. 良好的用户体验:播放器界面简洁易用,具有良好的用户体验,适合不同年龄段的用户使用。 总之,ffmpeg sdl播放器是一款功能强大且易于使用的视频播放器,它支持多种视频和音频格式,提供基本的播放控制和界面,可以在不同操作系统上运行。无论是观看电影、视频教程,还是听音乐,这款播放器都能满足用户的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值