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

Reply via email to