api-seek-test.c 9.07 KB
Newer Older
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 30 31 32
/*
 * Copyright (c) 2015 Ludmila Glinskih
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * Seek test.
 */

#include "libavutil/adler32.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"

int64_t *pts_array;
33
uint32_t *crc_array;
34 35 36
int size_of_array;
int number_of_elements;

37
static int add_crc_to_array(uint32_t crc, int64_t pts)
38 39 40 41 42
{
    if (size_of_array <= number_of_elements) {
        if (size_of_array == 0)
            size_of_array = 10;
        size_of_array *= 2;
43 44
        crc_array = av_realloc_f(crc_array, size_of_array, sizeof(uint32_t));
        pts_array = av_realloc_f(pts_array, size_of_array, sizeof(int64_t));
45 46 47 48 49 50 51 52 53 54 55
        if ((crc_array == NULL) || (pts_array == NULL)) {
            av_log(NULL, AV_LOG_ERROR, "Can't allocate array to store crcs\n");
            return AVERROR(ENOMEM);
        }
    }
    crc_array[number_of_elements] = crc;
    pts_array[number_of_elements] = pts;
    number_of_elements++;
    return 0;
}

56
static int compare_crc_in_array(uint32_t crc, int64_t pts)
57 58 59 60 61
{
    int i;
    for (i = 0; i < number_of_elements; i++) {
        if (pts_array[i] == pts) {
            if (crc_array[i] == crc) {
62
                printf("Comparing 0x%08"PRIx32" %"PRId64" %d is OK\n", crc, pts, i);
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
                return 0;
            }
            else {
                av_log(NULL, AV_LOG_ERROR, "Incorrect crc of a frame after seeking\n");
                return -1;
            }
        }
    }
    av_log(NULL, AV_LOG_ERROR, "Incorrect pts of a frame after seeking\n");
    return -1;
}

static int compute_crc_of_packets(AVFormatContext *fmt_ctx, int video_stream,
                                AVCodecContext *ctx, AVFrame *fr, uint64_t ts_start, uint64_t ts_end, int no_seeking)
{
    int number_of_written_bytes;
    int got_frame = 0;
    int result;
    int end_of_stream = 0;
    int byte_buffer_size;
    uint8_t *byte_buffer;
84
    uint32_t crc;
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    AVPacket pkt;

    byte_buffer_size = av_image_get_buffer_size(ctx->pix_fmt, ctx->width, ctx->height, 16);
    byte_buffer = av_malloc(byte_buffer_size);
    if (!byte_buffer) {
        av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n");
        return AVERROR(ENOMEM);
    }

    if (!no_seeking) {
        result = av_seek_frame(fmt_ctx, video_stream, ts_start, AVSEEK_FLAG_ANY);
        printf("Seeking to %"PRId64", computing crc for frames with pts < %"PRId64"\n", ts_start, ts_end);
        if (result < 0) {
            av_log(NULL, AV_LOG_ERROR, "Error in seeking\n");
            return result;
        }
        avcodec_flush_buffers(ctx);
    }

    av_init_packet(&pkt);
    do {
        if (!end_of_stream)
            if (av_read_frame(fmt_ctx, &pkt) < 0)
                end_of_stream = 1;
        if (end_of_stream) {
            pkt.data = NULL;
            pkt.size = 0;
        }
        if (pkt.stream_index == video_stream || end_of_stream) {
            got_frame = 0;
            if ((pkt.pts == AV_NOPTS_VALUE) && (!end_of_stream)) {
                av_log(NULL, AV_LOG_ERROR, "Error: frames doesn't have pts values\n");
                return -1;
            }
            result = avcodec_decode_video2(ctx, fr, &got_frame, &pkt);
            if (result < 0) {
                av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");
                return result;
            }
            if (got_frame) {
                number_of_written_bytes = av_image_copy_to_buffer(byte_buffer, byte_buffer_size,
                                        (const uint8_t* const *)fr->data, (const int*) fr->linesize,
                                        ctx->pix_fmt, ctx->width, ctx->height, 1);
                if (number_of_written_bytes < 0) {
                    av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n");
                    return number_of_written_bytes;
                }
132
                if ((!no_seeking) && (fr->pts > ts_end))
133 134
                    break;
                crc = av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes);
135
                printf("%10"PRId64", 0x%08"PRIx32"\n", fr->pts, crc);
136
                if (no_seeking) {
137
                    if (add_crc_to_array(crc, fr->pts) < 0)
138 139 140
                        return -1;
                }
                else {
141
                    if (compare_crc_in_array(crc, fr->pts) < 0)
142 143 144 145
                        return -1;
                }
            }
        }
146
        av_packet_unref(&pkt);
147
        av_init_packet(&pkt);
148
    } while ((!end_of_stream || got_frame) && (no_seeking || (fr->pts + av_frame_get_pkt_duration(fr) <= ts_end)));
149

150
    av_packet_unref(&pkt);
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    av_freep(&byte_buffer);

    return 0;
}

static long int read_seek_range(const char *string_with_number)
{
    long int number;
    char *end_of_string = NULL;
    number = strtol(string_with_number, &end_of_string, 10);
    if ((strlen(string_with_number) != end_of_string - string_with_number)  || (number < 0)) {
        av_log(NULL, AV_LOG_ERROR, "Incorrect input ranges of seeking\n");
        return -1;
    }
    else if ((number == LONG_MAX) || (number == LONG_MIN)) {
        if (errno == ERANGE) {
            av_log(NULL, AV_LOG_ERROR, "Incorrect input ranges of seeking\n");
            return -1;
        }
    }
    return number;
}

static int seek_test(const char *input_filename, const char *start, const char *end)
{
    AVCodec *codec = NULL;
177 178
    AVCodecContext *ctx= NULL;
    AVCodecParameters *origin_par = NULL;
179 180 181 182 183 184 185 186 187
    AVFrame *fr = NULL;
    AVFormatContext *fmt_ctx = NULL;
    int video_stream;
    int result;
    int i, j;
    long int start_ts, end_ts;

    size_of_array = 0;
    number_of_elements = 0;
188 189
    crc_array = NULL;
    pts_array = NULL;
190 191 192 193 194 195 196 197 198 199

    result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL);
    if (result < 0) {
        av_log(NULL, AV_LOG_ERROR, "Can't open file\n");
        return result;
    }

    result = avformat_find_stream_info(fmt_ctx, NULL);
    if (result < 0) {
        av_log(NULL, AV_LOG_ERROR, "Can't get stream info\n");
200
        goto end;
201 202 203 204
    }

    start_ts = read_seek_range(start);
    end_ts = read_seek_range(end);
205 206 207 208
    if ((start_ts < 0) || (end_ts < 0)) {
        result = -1;
        goto end;
    }
209 210 211 212 213

    //TODO: add ability to work with audio format
    video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (video_stream < 0) {
      av_log(NULL, AV_LOG_ERROR, "Can't find video stream in input file\n");
214 215
      result = video_stream;
      goto end;
216 217
    }

218
    origin_par = fmt_ctx->streams[video_stream]->codecpar;
219

220
    codec = avcodec_find_decoder(origin_par->codec_id);
221 222
    if (!codec) {
        av_log(NULL, AV_LOG_ERROR, "Can't find decoder\n");
223 224
        result = AVERROR_DECODER_NOT_FOUND;
        goto end;
225 226 227 228 229
    }

    ctx = avcodec_alloc_context3(codec);
    if (!ctx) {
        av_log(NULL, AV_LOG_ERROR, "Can't allocate decoder context\n");
230 231
        result = AVERROR(ENOMEM);
        goto end;
232 233
    }

234
    result = avcodec_parameters_to_context(ctx, origin_par);
235 236
    if (result) {
        av_log(NULL, AV_LOG_ERROR, "Can't copy decoder context\n");
237
        goto end;
238 239 240 241 242
    }

    result = avcodec_open2(ctx, codec, NULL);
    if (result < 0) {
        av_log(ctx, AV_LOG_ERROR, "Can't open decoder\n");
243
        goto end;
244 245 246 247 248
    }

    fr = av_frame_alloc();
    if (!fr) {
        av_log(NULL, AV_LOG_ERROR, "Can't allocate frame\n");
249 250
        result = AVERROR(ENOMEM);
        goto end;
251 252
    }

253
    result = compute_crc_of_packets(fmt_ctx, video_stream, ctx, fr, 0, 0, 1);
254
    if (result != 0)
255
        goto end;
256 257

    for (i = start_ts; i < end_ts; i += 100) {
258 259 260
        for (j = i + 100; j < end_ts; j += 100) {
            result = compute_crc_of_packets(fmt_ctx, video_stream, ctx, fr, i, j, 0);
            if (result != 0)
261
                break;
262
        }
263 264
    }

265
end:
266 267 268 269 270 271
    av_freep(&crc_array);
    av_freep(&pts_array);
    av_frame_free(&fr);
    avcodec_close(ctx);
    avformat_close_input(&fmt_ctx);
    avcodec_free_context(&ctx);
272
    return result;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
}

int main(int argc, char **argv)
{
    if (argc < 4) {
        av_log(NULL, AV_LOG_ERROR, "Incorrect input\n");
        return 1;
    }

    av_register_all();

    if (seek_test(argv[1], argv[2], argv[3]) != 0)
        return 1;

    return 0;
}