[FFmpeg-devel] AVCHD/H.264 decoder: further development/corrections

Ivan Schreter schreter
Sun Jan 25 20:08:06 CET 2009


Hello * (especially h264 maintainers),

in the last few days I tried to find out what has to be done in H.264 
decoder in order to correctly support AVCHD files from full-HD 
camcorders (which is IMHO quite an important use case). I admit, I'm a 
bit selfish there, since I want to get my Panasonic HDC-SD9 
fully-supported :-). But I'm also willing to invest more time and to fix 
the code. However, I need some advice, preferably somewhat more detailed.

I identified the following problems and potential solutions:

   1. Inconsistency between packets returned via av_read_frame() and
      actually delivered full frames from avcodec_decode_video()
   2. Key frame calculation and seeking
   3. Reporting frame type to libavformat

Now the details:

*1. Inconsistency between packets and decoded frames*

H.264 decoder returns AVPackets via av_read_frame(), which contain 
either a full frame or just a field (half frame). The former case is not 
problematic, since decoded frames are 1:1 to returned packets. It is 
problematic, though, when the decoder returns packets, which DO NOT 
correspond to a full frame. This is the case of interlaced AVCHD video 
as produced by various full-HD camcorders (at least Panasonic, Sony and 
Canon). H.264 standard allows namely coding by field, so one picture in 
H.264 terms (as currently returned as AVPacket from av_read_frame()) can 
contain either a single field, two fields (frame) or even repeated 
fields (so in total 1-3 fields per AVPacket).

I'd concentrate first on H.264 pictures having 1 to 2 fields only, since 
the other case (3 fields per picture) is probably not that interesting 
now (it is used to quasi stretch FPS from original cinema material to 
television frame rates).

Although the decoder itself takes this into account, the interface in 
libavformat doesn't. Thus, currently only video having full frames per 
packet decodes really correctly (and this also only with not-yet-applied 
patch concerning frame types). Reason: av_read_frame() doesn't return 
whole frames, although it is documented so.

*Potential solution:* For field pictures, delay returning a packet from 
h264_parse(), until the second field picture is also read. The decoder 
should take then care of decoding both fields correctly and returning a 
full frame for each packet.

*Alternative solution:* Return field packet from h264_parse() 
immediately, but somehow tell libavformat that the packet does not 
represent a full frame and second field has to be read as well. Read it 
in libavformat, extending the existing packet. Thus, av_read_frame() 
returns then full frame.

*No solution:* Leave libavformat and h264_parse as-is and take care of 
second half-frame in ffmpeg.c and other libavformat users. This won't 
work, as we would need to adjust API and thus every single program using 
ffmpeg to correctly handle field frames. Further, libavformat computes 
wrong DTS/PTS for the second field (equal to DTS/PTS of the first field 
of _next_ frame instead of in-between, since second field doesn't 
specify DTS/PTS at all), which causes do_video_out() to drop and 
duplicate frames, producing very jerky video.

*No solution 2:* Communicate to libavformat the fact which field of full 
frame the returned packet contains and adjust DTS/PTS calculation in 
compute_pkt_fields() appropriately, returning last_DTS+duration/2 and 
last_PTS+duration/2 for DTS/PTS of the second field. Again, this is API 
change, since av_read_frame() would not return full frames. Though it 
works in ffmpeg.c, it is unclear if it works in other programs using 
libavformat (probably not).

Now the question: Which solution is the "right" one? I'd go for the 
first one or possibly for the alternative. The first proposed solution 
seems to be most "compatible", since we don't need to extend AVPacket to 
address the issue.

Your opinions? Or eventually a different idea?

*2. Key frame calculation and seeking*

H.264 is different to other video codecs, since it doesn't have fixed 
key frames. Instead, several reference pictures from the history can be 
used to decode a particular picture. There are IDR pictures, which are 
effectively key frames, but these seem not to be really used. AVCHD 
files from camcorders have exactly one IDR frame at the beginning of the 
file.

Other than that, the stream provides information (SEI recovery point) on 
how many frames need to be decoded before the video synchronizes 
starting at the given point. There is already field 
AVPacket.convergence_duration, which is supposed to address exactly this 
(until now unused in h264, though).

My suggestion is to report key frames for IDR pictures and for 
appropriate frames after SEI recovery point (after counting down number 
of frames given in recovery point SEI message).

Alternatively, key frames could be reported for IDR pictures and for 
pictures having recovery point. In this case, the application would have 
to handle it via AVPacket.convergence_duration. Unfortunately, noone 
seems to handle convergence_duration in an application, and I don't 
believe anyone would like to. So IMHO this is a no-go.

My suggestion for current av_seek_frame() would be to do the following 
for streams needing convergence_duration (is there a flag for it 
already?): When seeking to a certain PTS, seek to a frame with given PTS 
and then roll *backward* until the frame with last recovery point with 
convergence_duration <= distance is found (how to find it most optimal?) 
and then re-decode all reference frames (i.e., leaving out unneeded 
B-frames) into dummy buffers from this point on until just before given 
PTS. So the next av_read_frame() will read a key frame, which can be 
decoded correctly. In this way, the application doesn't have to handle 
convergence_duration by itself.

Michael suggested new seeking API. Maybe this should be addressed there 
via a flag (seek to frame with recovery point and use 
convergence_duration in application or let libavformat do the decode up 
to key frame as described above), but for now, an alternative needs to 
be implemented for current seeking API.

Further, I'd propose keeping a small cache of (PTS, position, 
convergence_duration) triples for frames containing SEI recovery point 
message, so the seeking around "current" location would be faster. 
Reason: video editing software, where we often need to seek one frame 
forward/backward.

Your opinions/suggestions?

*3. Reporting frame type to libavformat*

This is a minor thing, but still important for correct computation of 
PTS/DTS and key frame flags. compute_pkt_fields() relies on having the 
information about picture type (I/P/B-frame). However, H.264 doesn't 
have strict I/P/B frames, there is even a possibility to have mixed-type 
slices inside of one frame. Indeed, my camcorder produces in interlaced 
mode top field as I-slice and bottom field as P-slice referring to the 
top field.

So my suggestion is, report picture type I-frame for key frames (which 
are key frames is discussed above) and report P-frame for all frames 
containing only P- and I- slices. Other frames containing also B-slices 
will be reported as B-frames.

Your opinions/suggestions?

Thanks in advance.

Regards,

Ivan





More information about the ffmpeg-devel mailing list