0%

ffplay

see build_ffmpeg on linux for how to build ffmpeg easily

1
2
3
4
5
export LD_LIBRARY_PATH=~/code//ffmpeg/install/lib:.
cd ~/code//ffmpeg/install/bin

# run
./ffplay -i /e/RES/Videos/h2645/sample-An.mp4 -threads 1 -filter_threads 1

threads

  • ffplay: main thread, sleeping
  • SDLTimer: some Timer stuffs
  • PulseHotPlug: pulse audio thread
  • read_thread: demuxer thread
  • video_decoder: decoder thread

video decode process

read_thread:

1
2
ret = av_read_frame(ic, pkt);
packet_queue_put(&is->videoq, pkt);

video_decoder:

1
2
3
4
5
6
7
8
9
10
11
// keep try get_video_frame
// change order of showup of avcodec_send_packet & avcodec_receive_frame for better comprehension
get_video_frame(){
decoder_decode_frame(){
if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0)
return -1;
avcodec_send_packet(d->avctx, d->pkt);
avcodec_receive_frame(d->avctx, frame);
}
queue_picture(is, frame, pts, duration, fd ? fd->pkt_pos : -1, is->viddec.pkt_serial);
}

buffer control

read_thread: control the buffer size of stream data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
// size is counted in bytes
/* if the queue are full, no need to read more */
if (infinite_buffer<1 &&
(is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE
|| (stream_has_enough_packets(is->audio_st, is->audio_stream, &is->audioq) &&
stream_has_enough_packets(is->video_st, is->video_stream, &is->videoq) &&
stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq)))) {
/* wait 10 ms */
SDL_LockMutex(wait_mutex);
SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
SDL_UnlockMutex(wait_mutex);
continue;
}

video_decoder: keep pulling data queued from read_thread, and stop when queued pictures size exceeds

note the value of VIDEO_PICTURE_QUEUE_SIZE

1
2
3
4
5
6
7
8
9
#define VIDEO_PICTURE_QUEUE_SIZE 3
#define SUBPICTURE_QUEUE_SIZE 16
#define SAMPLE_QUEUE_SIZE 9
video_thread::queue_picture(){
frame_queue_peek_writable(){
while (f->size >= f->max_size && !f->pktq->abort_request)
SDL_CondWait(f->cond, f->mutex);
}
}

seek

the key is the concept serial, of both PacketQueue::serial, MyAVPacketList::serial in PacketQueue list, and Decoder::pkt_serial.

ffplay use serial to syn its state.

like when seek happens, the PacketQueue change it serial, then the demuxed packets, finally the Decoder object.

for the seek:

  1. :arrow_left: Left-Arrow Keydown
  2. ffpaly: in SDL event_loop: set {seek_req, seek_flag}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
event_loop(){
case SDLK_LEFT:
incr = seek_interval ? -seek_interval : -10.0;
goto do_seek;
...
do_seek:
stream_seek(cur_stream, pos, incr, 1);
}

/* seek in the stream */
static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int by_bytes)
{
// here this seek_req means a unfinished seek request
if (!is->seek_req) {
is->seek_pos = pos;
is->seek_rel = rel;
is->seek_flags &= ~AVSEEK_FLAG_BYTE;
if (by_bytes)
is->seek_flags |= AVSEEK_FLAG_BYTE;
is->seek_req = 1;
SDL_CondSignal(is->continue_read_thread);
}
}
  1. read_thread: seek demuxer and flush packet_buffer

​ note serial is incre by 1 in packet_queue_flush

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static int read_thread(void *arg){
for(;;){
if (is->seek_req) {
ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
packet_queue_flush(&is->videoq);
}
}
}

static void packet_queue_flush(PacketQueue *q)
{
MyAVPacketList pkt1;

SDL_LockMutex(q->mutex);
while (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0)
av_packet_free(&pkt1.pkt);
q->nb_packets = 0;
q->size = 0;
q->duration = 0;
q->serial++; // propagate to packet serial in packet_queue_put_private
SDL_UnlockMutex(q->mutex);
}

static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
{
...
pkt1.serial = q->serial;
...
}
  1. video_decoder: avcodec_flush_buffers when serials unmatch
1
2
3
4
5
6
7
8
9
10
11
12
13
static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
do{
int old_serial = d->pkt_serial;
if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0)
return -1;
if (old_serial != d->pkt_serial) {
avcodec_flush_buffers(d->avctx);
d->finished = 0;
d->next_pts = d->start_pts;
d->next_pts_tb = d->start_pts_tb;
}
} while (1);
}