New submission from Andrew Stevens <[email protected]>:
avcodec_decode_video2 returns a NULL got_picture_ptr when decoding h264 movies.
In order to have random access to the video stream I record all of the
AVPacket.pts locations for each frame and then seek to the desired frame by
frame number. After seeking a frame it is decoded and converted to RGB24.
(conversion is turned off in the attached file)
The source attached was compiled with the latest daily build using:
gcc -g -W -Wall main.c -std=c99 `pkg-config --libs --cflags libavcodec
libavformat libswscale`
----------
assignedto: rbultje
files: main.c
messages: 12056
nosy: rbultje
priority: normal
status: new
substatus: new
title: error decoding h264
topic: avcodec
type: bug
________________________________________________
FFmpeg issue tracker <[email protected]>
<https://roundup.ffmpeg.org/issue2249>
________________________________________________
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
AVFormatContext* formatContext=NULL;
AVCodecContext* codecContext=NULL;
AVCodec* videoCodec=NULL;
AVFrame* currentFrame=NULL;
int videoStreamIndex=-1;
int currentFrameNumber=0;
int64_t frameLocations[10000];
int width=0;
int height=0;
int length=0;
void findFrameLocations();
void seekFrame(int frameNumber);
void Rewind(int frameNumber);
AVPacket* fastForward(int64_t framePTS);
AVFrame* decodePacket(AVPacket *packet);
AVFrame* allocateFrame();
//SwsContext* getSwsContext();
void extractCurrentFrameFromPacket(AVPacket *packet);
uint8_t* getPictureBuffer();
int main(int argc, const char* argv[]) {
if(!argc) return -1;
av_log_set_level(2);
av_register_all();
formatContext = avformat_alloc_context();
formatContext->video_codec_id = CODEC_ID_NONE;
if (av_open_input_file(&formatContext, argv[1], NULL, 0, NULL) != 0) return 10;
if (av_find_stream_info(formatContext) < 0) return 20;
for (unsigned int i = 0; i < formatContext->nb_streams; i++)
if (formatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
if (videoStreamIndex == -1) return 30;
codecContext = formatContext->streams[videoStreamIndex]->codec;
codecContext->hurry_up = 1;
videoCodec = avcodec_find_decoder(codecContext->codec_id);
if (videoCodec == NULL) return 40;
if (avcodec_open(codecContext, videoCodec) < 0) return 50;
width = codecContext->width;
height = codecContext->height;
findFrameLocations();
seekFrame(0); /* copy frame zero into currentFrame as RGB24 */
avcodec_close(codecContext);
av_close_input_file(formatContext);
av_free(currentFrame);
}
void findFrameLocations() {
avcodec_flush_buffers(codecContext);
AVPacket packet;
while (av_read_frame(formatContext, &packet) >= 0) {
if (packet.stream_index == videoStreamIndex) {/* check that packet is from the video stream */
frameLocations[currentFrameNumber] = packet.pts;
currentFrameNumber++;
}
av_free_packet(&packet);
}
length = currentFrameNumber;
}
void seekFrame(int frameNumber) {
if (frameNumber == currentFrameNumber) {
return;
} else if (frameNumber >= length) {
printf("\noops seekframe\n"); return;
} else if (frameNumber < currentFrameNumber) {
Rewind(frameNumber);
}
printf("\n%ld\n", frameLocations[frameNumber]);
AVPacket * packet = fastForward(frameLocations[frameNumber]);
extractCurrentFrameFromPacket(packet);
av_free_packet(packet);
currentFrameNumber = frameNumber;
}
void Rewind(int frameNumber) {
if (av_seek_frame(formatContext, videoStreamIndex, frameLocations[frameNumber], AVSEEK_FLAG_BACKWARD) < 0) {
printf("\noops rewind\n"); return;
}
avcodec_flush_buffers(codecContext);
}
AVPacket* fastForward(int64_t framePTS) {
AVPacket *packet = malloc(sizeof(AVPacket));
while (av_read_frame(formatContext, packet) >= 0) {
if (packet->stream_index == videoStreamIndex) {
if (packet->pts >= framePTS) {
break;
}
free(decodePacket(packet));
}
av_free_packet(packet);
}
return packet;
}
AVFrame* decodePacket(AVPacket *packet) {
AVFrame * frame = allocateFrame();
int frameFinished;
avcodec_decode_video2(codecContext, frame, &frameFinished, packet);
if(frameFinished == 0){
printf("\noops decodePacket\n"); return NULL;
}
return frame;
}
AVFrame* allocateFrame() {
AVFrame *frame = avcodec_alloc_frame();
if (frame == NULL) {
printf("\noops allocateFrame\n"); return NULL;
}
return frame;
}
void extractCurrentFrameFromPacket(AVPacket *packet) {
if (currentFrame != NULL) {
free(currentFrame);
}
currentFrame = allocateFrame();
uint8_t *buffer = getPictureBuffer();
avpicture_fill((AVPicture *) currentFrame, buffer, PIX_FMT_RGB24, width, height);
AVFrame * rawFrame = decodePacket(packet);
/*
SwsContext* img_convert_ctx = sws_getContext(width, height, codecContext->pix_fmt, width, height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(img_convert_ctx, rawFrame->data, rawFrame->linesize, 0, height, currentFrame->data, currentFrame->linesize);
*/
av_free(buffer);
/*
sws_freeContext(img_convert_ctx);
*/
av_free(rawFrame);
}
uint8_t* getPictureBuffer() {
int numBytes = avpicture_get_size(PIX_FMT_RGB24, width, height);
uint8_t *buffer = (uint8_t *) av_malloc(numBytes * sizeof (uint8_t));
if (buffer == NULL) {
printf("\noops getPictureBuffer\n"); return NULL;
}
return buffer;
}