stream.cpp 4.61 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2004-present Facebook. All Rights Reserved.

#include "stream.h"
#include <c10/util/Logging.h>
#include "util.h"

namespace ffmpeg {

namespace {
const size_t kDecoderHeaderSize = sizeof(DecoderHeader);
}

Stream::Stream(
    AVFormatContext* inputCtx,
    MediaFormat format,
    bool convertPtsToWallTime)
    : inputCtx_(inputCtx),
      format_(format),
      convertPtsToWallTime_(convertPtsToWallTime) {}

Stream::~Stream() {
  if (frame_) {
    av_free(frame_); // Copyright 2004-present Facebook. All Rights Reserved.
  }
  if (codecCtx_) {
    avcodec_free_context(&codecCtx_);
  }
}

AVCodec* Stream::findCodec(AVCodecContext* ctx) {
  return avcodec_find_decoder(ctx->codec_id);
}

int Stream::openCodec() {
  AVStream* steam = inputCtx_->streams[format_.stream];
  auto codec_id = steam->codecpar->codec_id;
  AVCodec* codec = avcodec_find_decoder(codec_id);
  if (!codec) {
    LOG(ERROR) << "avcodec_find_decoder failed for codec_id: " << int(codec_id);
    return AVERROR(EINVAL);
  }

  if (!(codecCtx_ = avcodec_alloc_context3(codec))) {
    LOG(ERROR) << "avcodec_alloc_context3 fails";
    return AVERROR(ENOMEM);
  }

  int ret;
  // Copy codec parameters from input stream to output codec context
  if ((ret = avcodec_parameters_to_context(codecCtx_, steam->codecpar)) < 0) {
    LOG(ERROR) << "Failed to copy codec parameters to decoder context";
    return ret;
  }

  // after avcodec_open2, value of codecCtx_->time_base is NOT meaningful
  if ((ret = avcodec_open2(codecCtx_, codec, nullptr)) < 0) {
    LOG(ERROR) << "avcodec_open2 failed. " << Util::generateErrorDesc(ret);
    avcodec_free_context(&codecCtx_);
    codecCtx_ = nullptr;
    return ret;
  }

  frame_ = av_frame_alloc();

  format_.num = inputCtx_->streams[format_.stream]->time_base.num;
  format_.den = inputCtx_->streams[format_.stream]->time_base.den;
  format_.duration = inputCtx_->streams[format_.stream]->duration;

  return initFormat();
}

// rescale package
void Stream::rescalePackage(AVPacket* packet) {
  if (codecCtx_->time_base.num != 0) {
    av_packet_rescale_ts(
        packet,
        inputCtx_->streams[format_.stream]->time_base,
        codecCtx_->time_base);
  }
}

int Stream::analyzePacket(const AVPacket* packet, int* gotFramePtr) {
  int consumed = 0;
  int result = avcodec_send_packet(codecCtx_, packet);
  if (result == AVERROR(EAGAIN)) {
    *gotFramePtr = 0; // no bytes get consumed, fetch frame
  } else if (result == AVERROR_EOF) {
    *gotFramePtr = 0; // more than one flush packet
    if (packet) {
      // got packet after flush, this is an error
      return result;
    }
  } else if (result < 0) {
    LOG(ERROR) << "avcodec_send_packet failed, err: "
               << Util::generateErrorDesc(result);
    return result; // error
  } else {
    consumed = packet ? packet->size : 0; // all bytes get consumed
  }

  result = avcodec_receive_frame(codecCtx_, frame_);

  if (result >= 0) {
    *gotFramePtr = 1; // frame is available
  } else if (result == AVERROR(EAGAIN)) {
    *gotFramePtr = 0; // no frames at this time, needs more packets
    if (!consumed) {
      // precaution, if no packages got consumed and no frames are available
      return result;
    }
  } else if (result == AVERROR_EOF) {
    *gotFramePtr = 0; // the last frame has been flushed
    // precaution, if no more frames are available assume we consume all bytes
    consumed = packet ? packet->size : 0;
  } else { // error
    LOG(ERROR) << "avcodec_receive_frame failed, err: "
               << Util::generateErrorDesc(result);
    return result;
  }
  return consumed;
}

int Stream::decodeFrame(const AVPacket* packet, int* gotFramePtr) {
  return analyzePacket(packet, gotFramePtr);
}

int Stream::getFrameBytes(DecoderOutputMessage* out, bool headerOnly) {
  return fillBuffer(out, false, headerOnly);
}

int Stream::flush(DecoderOutputMessage* out, bool headerOnly) {
  int gotFramePtr = 0;
  int result;
  if (analyzePacket(nullptr, &gotFramePtr) >= 0 && gotFramePtr &&
      (result = fillBuffer(out, false, headerOnly)) > 0) {
    return result;
  } else if ((result = fillBuffer(out, true, headerOnly)) > 0) {
    return result;
  }
  return result;
}

int Stream::fillBuffer(DecoderOutputMessage* out, bool flush, bool headerOnly) {
  int result = -1;
  if (!codecCtx_) {
    LOG(INFO) << "Codec is not initialized";
    return result;
  }

  // assign message
  setHeader(&out->header);

  if (headerOnly) {
    return sizeof(out->header);
  }

  // init sampler, if any and return required bytes
  if ((result = estimateBytes(flush)) < 0) {
    return result;
  }
  out->payload->ensure(result);
  return copyFrameBytes(out->payload.get(), flush);
}

} // namespace ffmpeg