正在显示
18 个修改的文件
包含
1513 行增加
和
696 行删除
pip/AVDecoder.cpp
0 → 100644
| 1 | +#include "AVDecoder.h" | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +CAVDecoder::CAVDecoder() : | ||
| 5 | +_a_start_time_ms(INT64_MAX), | ||
| 6 | +_v_start_time_ms(INT64_MAX), | ||
| 7 | +_cur_a_ts_ms(INT64_MAX), | ||
| 8 | +_cur_v_ts_ms(INT64_MAX), | ||
| 9 | +_end_time_ms(0), | ||
| 10 | +_cur_a_frame(NULL), | ||
| 11 | +_cur_v_frame(NULL) | ||
| 12 | +{ | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +CAVDecoder::~CAVDecoder() | ||
| 17 | +{ | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +int CAVDecoder::add(media_info &info) | ||
| 21 | +{ | ||
| 22 | + if (info.m_type == mt_audio) { | ||
| 23 | + _a_start_time_ms = info.start_time_ms; | ||
| 24 | + _a_end_time_ms = info.end_time_ms; | ||
| 25 | + _audio_info.push_back(info); | ||
| 26 | + _audio_decoder.add(info); | ||
| 27 | + } | ||
| 28 | + else { | ||
| 29 | + _v_start_time_ms = info.start_time_ms; | ||
| 30 | + _v_end_time_ms = info.end_time_ms; | ||
| 31 | + _video_info.push_back(info); | ||
| 32 | + _video_decoder.add(info); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + if (_cur_a_ts_ms == INT64_MAX) { | ||
| 36 | + _cur_a_ts_ms = info.start_time_ms; | ||
| 37 | + _cur_v_ts_ms = info.start_time_ms; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + if (_end_time_ms < info.end_time_ms) { | ||
| 41 | + _end_time_ms = info.end_time_ms; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + return 0; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +unsigned int CAVDecoder::getuid() | ||
| 48 | +{ | ||
| 49 | + return 0; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +bool CAVDecoder::get_one_v_frame() | ||
| 53 | +{ | ||
| 54 | + int64_t ts; | ||
| 55 | + int ret = -1; | ||
| 56 | + if (_video_info.size()) { | ||
| 57 | + ret = _video_decoder.get_one_frame(&_cur_v_frame, ts); | ||
| 58 | + if (ret == 0) { | ||
| 59 | + _cur_v_ts_ms = _v_start_time_ms + ts; | ||
| 60 | + } | ||
| 61 | + else { | ||
| 62 | + _video_info.pop_front(); | ||
| 63 | + if (_cur_v_ts_ms < _end_time_ms) { | ||
| 64 | + _cur_v_ts_ms += 50;//return last v frame | ||
| 65 | + ret = 0; | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + if (ret) {//no video decoded | ||
| 71 | + if (_cur_v_ts_ms < _end_time_ms) {//should have as video frame | ||
| 72 | + _cur_v_ts_ms += 50;//return last v frame | ||
| 73 | + ret = 0; | ||
| 74 | + if (!_cur_v_frame) { | ||
| 75 | + _cur_v_frame = get_blank_frame(); | ||
| 76 | + } | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + return ret == 0; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +AVFrame * CAVDecoder::get_blank_frame() | ||
| 84 | +{ | ||
| 85 | + return NULL; | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +AVFrame * CAVDecoder::get_silence_frame() | ||
| 89 | +{ | ||
| 90 | + return NULL; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | + | ||
| 94 | + | ||
| 95 | +void CAVDecoder::free_cur_a_frame() | ||
| 96 | +{ | ||
| 97 | + if (_cur_a_frame) { | ||
| 98 | + av_frame_free(&_cur_a_frame); | ||
| 99 | + } | ||
| 100 | +} | ||
| 101 | + | ||
| 102 | + | ||
| 103 | +void CAVDecoder::free_cur_v_frame() | ||
| 104 | +{ | ||
| 105 | + if (_cur_v_frame) { | ||
| 106 | + av_frame_free(&_cur_v_frame); | ||
| 107 | + } | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +bool CAVDecoder::get_one_a_frame() | ||
| 111 | +{ | ||
| 112 | + int64_t ts; | ||
| 113 | + int ret = -1; | ||
| 114 | + if (_audio_info.size()) { | ||
| 115 | + ret = _audio_decoder.get_one_frame(&_cur_v_frame, ts); | ||
| 116 | + if (ret == 0) { | ||
| 117 | + _cur_a_ts_ms = _a_start_time_ms + ts; | ||
| 118 | + } | ||
| 119 | + else { | ||
| 120 | + _audio_info.pop_front(); | ||
| 121 | + if (_cur_a_ts_ms < _end_time_ms) { | ||
| 122 | + _cur_a_ts_ms += AFRAME_DURATION_MS;//return silence frame | ||
| 123 | + ret = 0; | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + if (ret) {//no video decoded | ||
| 129 | + if (_cur_a_ts_ms < _end_time_ms) {//should have a audio frame | ||
| 130 | + _cur_a_ts_ms += AFRAME_DURATION_MS;//return last a frame | ||
| 131 | + ret = 0; | ||
| 132 | + if (!_cur_a_frame) { | ||
| 133 | + _cur_a_frame = get_silence_frame(); | ||
| 134 | + } | ||
| 135 | + } | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + return ret == 0; | ||
| 139 | +} |
pip/AVDecoder.h
0 → 100644
| 1 | +#pragma once | ||
| 2 | +#include "AudioDecoder.h" | ||
| 3 | +#include "VideoDecoder.h" | ||
| 4 | + | ||
| 5 | +class CAVDecoder | ||
| 6 | +{ | ||
| 7 | +public: | ||
| 8 | + CAVDecoder(); | ||
| 9 | + virtual ~CAVDecoder(); | ||
| 10 | + | ||
| 11 | + int add(media_info &info); | ||
| 12 | + | ||
| 13 | + unsigned int getuid(); | ||
| 14 | + bool get_one_a_frame(); | ||
| 15 | + bool get_one_v_frame(); | ||
| 16 | + int64_t _cur_a_ts_ms; | ||
| 17 | + int64_t _cur_v_ts_ms; | ||
| 18 | + | ||
| 19 | +protected: | ||
| 20 | + list<media_info> _video_info; | ||
| 21 | + list<media_info> _audio_info; | ||
| 22 | + CAudioDecoder _audio_decoder; | ||
| 23 | + CVideoDecoder _video_decoder; | ||
| 24 | + int64_t _a_start_time_ms; | ||
| 25 | + int64_t _a_end_time_ms; | ||
| 26 | + int64_t _v_start_time_ms; | ||
| 27 | + int64_t _v_end_time_ms; | ||
| 28 | + int64_t _end_time_ms; | ||
| 29 | + AVFrame * _cur_a_frame; | ||
| 30 | + AVFrame * _cur_v_frame; | ||
| 31 | +private: | ||
| 32 | + AVFrame * get_blank_frame(); | ||
| 33 | + AVFrame * get_silence_frame(); | ||
| 34 | +public: | ||
| 35 | + void free_cur_a_frame(); | ||
| 36 | + void free_cur_v_frame(); | ||
| 37 | +}; | ||
| 38 | + |
pip/AVTranscoder.cpp
0 → 100644
| 1 | +#include "AVTranscoder.h" | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +CAVTranscoder::CAVTranscoder(): | ||
| 5 | +_start_time(INT64_MAX), | ||
| 6 | +_all_processed(true) | ||
| 7 | +{ | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +CAVTranscoder::~CAVTranscoder() | ||
| 12 | +{ | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +int CAVTranscoder::add(media_info & info) | ||
| 16 | +{ | ||
| 17 | + _all_processed = false; | ||
| 18 | + if (_start_time == INT64_MAX) { | ||
| 19 | + _start_time = info.start_time_ms; | ||
| 20 | + _cur_v_time = _start_time; | ||
| 21 | + _cur_a_time = _start_time; | ||
| 22 | + } | ||
| 23 | + vector < CAVDecoder *>::iterator it = _decoders.begin(); | ||
| 24 | + for (; it != _decoders.end(); it++) { | ||
| 25 | + if ((*it)->getuid() == info.uid){ | ||
| 26 | + (*it)->add(info); | ||
| 27 | + break; | ||
| 28 | + } | ||
| 29 | + } | ||
| 30 | + if (it == _decoders.end()) { | ||
| 31 | + CAVDecoder * pVideoDecoder = new CAVDecoder(); | ||
| 32 | + pVideoDecoder->add(info); | ||
| 33 | + _decoders.push_back(pVideoDecoder); | ||
| 34 | + } | ||
| 35 | + return 0; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +int64_t CAVTranscoder::transcode() | ||
| 39 | +{ | ||
| 40 | + vector<CAVDecoder *> decoders_got_frame; | ||
| 41 | + vector < CAVDecoder *>::iterator it = _decoders.begin(); | ||
| 42 | + for (; it != _decoders.end();) { | ||
| 43 | + if((*it)->get_one_v_frame()){ | ||
| 44 | + decoders_got_frame.push_back(*it); | ||
| 45 | + } | ||
| 46 | + else { | ||
| 47 | + it = _decoders.erase(it); | ||
| 48 | + continue; | ||
| 49 | + } | ||
| 50 | + it++; | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + _all_processed = decoders_got_frame.size() == 0; | ||
| 54 | + mix_and_output_vframe(decoders_got_frame); | ||
| 55 | + | ||
| 56 | + _cur_v_time += 50; | ||
| 57 | + //sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); | ||
| 58 | + | ||
| 59 | + while (_cur_a_time < _cur_v_time) | ||
| 60 | + { | ||
| 61 | + decoders_got_frame.clear(); | ||
| 62 | + vector < CAVDecoder *>::iterator it = _decoders.begin(); | ||
| 63 | + for (; it != _decoders.end();) { | ||
| 64 | + if ((*it)->get_one_a_frame()){ | ||
| 65 | + decoders_got_frame.push_back(*it); | ||
| 66 | + } | ||
| 67 | + else { | ||
| 68 | + it = _decoders.erase(it); | ||
| 69 | + continue; | ||
| 70 | + } | ||
| 71 | + it++; | ||
| 72 | + } | ||
| 73 | + mix_and_output_aframe(decoders_got_frame); | ||
| 74 | + _cur_a_time += AFRAME_DURATION_MS; | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + return _cur_v_time; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +bool CAVTranscoder::all_processed() | ||
| 81 | +{ | ||
| 82 | + return _all_processed; | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +int CAVTranscoder::close() | ||
| 86 | +{ | ||
| 87 | + return 0; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | + | ||
| 91 | +int CAVTranscoder::open_output_file(const char *filename) | ||
| 92 | +{ | ||
| 93 | + AVStream *out_stream; | ||
| 94 | + AVCodecContext *dec_ctx, *enc_ctx; | ||
| 95 | + AVCodec *encoder; | ||
| 96 | + int ret; | ||
| 97 | + unsigned int i; | ||
| 98 | + | ||
| 99 | + ofmt_ctx = NULL; | ||
| 100 | + avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename); | ||
| 101 | + if (!ofmt_ctx) { | ||
| 102 | + av_log(NULL, AV_LOG_ERROR, "Could not create output context\n"); | ||
| 103 | + return AVERROR_UNKNOWN; | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + | ||
| 107 | + for (i = 0; i < 2; i++) { | ||
| 108 | + out_stream = avformat_new_stream(ofmt_ctx, NULL); | ||
| 109 | + if (!out_stream) { | ||
| 110 | + av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n"); | ||
| 111 | + return AVERROR_UNKNOWN; | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + enc_ctx = out_stream->codec; | ||
| 115 | + | ||
| 116 | + if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) | ||
| 117 | + enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; | ||
| 118 | + | ||
| 119 | + | ||
| 120 | + if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO | ||
| 121 | + || dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 122 | + /* in this example, we choose transcoding to same codec */ | ||
| 123 | + encoder = avcodec_find_encoder(dec_ctx->codec_id); | ||
| 124 | + if (!encoder) { | ||
| 125 | + av_log(NULL, AV_LOG_FATAL, "Necessary encoder not found\n"); | ||
| 126 | + return AVERROR_INVALIDDATA; | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + /* In this example, we transcode to same properties (picture size, | ||
| 130 | + * sample rate etc.). These properties can be changed for output | ||
| 131 | + * streams easily using filters */ | ||
| 132 | + if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { | ||
| 133 | + enc_ctx->height = dec_ctx->height; | ||
| 134 | + enc_ctx->width = dec_ctx->width; | ||
| 135 | + enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio; | ||
| 136 | + /* take first format from list of supported formats */ | ||
| 137 | + enc_ctx->pix_fmt = encoder->pix_fmts[0]; | ||
| 138 | + /* video time_base can be set to whatever is handy and supported by encoder */ | ||
| 139 | + enc_ctx->time_base = dec_ctx->time_base; | ||
| 140 | + | ||
| 141 | + enc_ctx->me_range = 16; | ||
| 142 | + enc_ctx->max_qdiff = 4; | ||
| 143 | + enc_ctx->qmin = 10; | ||
| 144 | + enc_ctx->qmax = 30; | ||
| 145 | + enc_ctx->qcompress = 0.6; | ||
| 146 | + } | ||
| 147 | + else { | ||
| 148 | + enc_ctx->sample_rate = dec_ctx->sample_rate; | ||
| 149 | + enc_ctx->channel_layout = dec_ctx->channel_layout; | ||
| 150 | + enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout); | ||
| 151 | + /* take first format from list of supported formats */ | ||
| 152 | + enc_ctx->sample_fmt = encoder->sample_fmts[0]; | ||
| 153 | + enc_ctx->time_base.num = 1; | ||
| 154 | + enc_ctx->time_base.den = enc_ctx->sample_rate; | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + /* Third parameter can be used to pass settings to encoder */ | ||
| 158 | + ret = avcodec_open2(enc_ctx, encoder, NULL); | ||
| 159 | + if (ret < 0) { | ||
| 160 | + av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i); | ||
| 161 | + return ret; | ||
| 162 | + } | ||
| 163 | + } | ||
| 164 | + else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) { | ||
| 165 | + av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, cannot proceed\n", i); | ||
| 166 | + return AVERROR_INVALIDDATA; | ||
| 167 | + } | ||
| 168 | +#if 0 | ||
| 169 | + else { | ||
| 170 | + /* if this stream must be remuxed */ | ||
| 171 | + ret = avcodec_copy_context(ofmt_ctx->streams[i]->codec, | ||
| 172 | + ifmt_ctx->streams[i]->codec); | ||
| 173 | + if (ret < 0) { | ||
| 174 | + av_log(NULL, AV_LOG_ERROR, "Copying stream context failed\n"); | ||
| 175 | + return ret; | ||
| 176 | + } | ||
| 177 | +#endif | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | +#if 0 | ||
| 181 | + av_dump_format(ofmt_ctx, 0, filename, 1); | ||
| 182 | + | ||
| 183 | + if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) { | ||
| 184 | + ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE); | ||
| 185 | + if (ret < 0) { | ||
| 186 | + av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", filename); | ||
| 187 | + return ret; | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + /* init muxer, write output file header */ | ||
| 192 | + ret = avformat_write_header(ofmt_ctx, NULL); | ||
| 193 | + if (ret < 0) { | ||
| 194 | + av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file\n"); | ||
| 195 | + return ret; | ||
| 196 | + } | ||
| 197 | +#endif | ||
| 198 | + return 0; | ||
| 199 | +} | ||
| 200 | + | ||
| 201 | + | ||
| 202 | + int CAVTranscoder::mix_and_output_vframe(vector<CAVDecoder *> & decoders_got_frame) | ||
| 203 | + { | ||
| 204 | + vector < CAVDecoder *>::iterator it = decoders_got_frame.begin(); | ||
| 205 | + for (; it != decoders_got_frame.end(); it++) { | ||
| 206 | + (*it)->free_cur_a_frame(); | ||
| 207 | + } | ||
| 208 | + return 0; | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + int CAVTranscoder::mix_and_output_aframe(vector<CAVDecoder *> & decoders_got_frame) | ||
| 212 | + { | ||
| 213 | + vector < CAVDecoder *>::iterator it = decoders_got_frame.begin(); | ||
| 214 | + for (; it != decoders_got_frame.end(); it++) { | ||
| 215 | + (*it)->free_cur_v_frame(); | ||
| 216 | + } | ||
| 217 | + return 0; | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + int encode_write_frame(AVFrame *filt_frame, unsigned int stream_index, int *got_frame) { | ||
| 221 | + int ret; | ||
| 222 | + int got_frame_local; | ||
| 223 | + AVPacket enc_pkt; | ||
| 224 | +#if 0 | ||
| 225 | + int(*enc_func)(AVCodecContext *, AVPacket *, const AVFrame *, int *) = | ||
| 226 | + (ifmt_ctx->streams[stream_index]->codec->codec_type == | ||
| 227 | + AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2; | ||
| 228 | + | ||
| 229 | + if (!got_frame) | ||
| 230 | + got_frame = &got_frame_local; | ||
| 231 | + | ||
| 232 | + av_log(NULL, AV_LOG_INFO, "Encoding frame\n"); | ||
| 233 | + /* encode filtered frame */ | ||
| 234 | + enc_pkt.data = NULL; | ||
| 235 | + enc_pkt.size = 0; | ||
| 236 | + av_init_packet(&enc_pkt); | ||
| 237 | + ret = enc_func(ofmt_ctx->streams[stream_index]->codec, &enc_pkt, | ||
| 238 | + filt_frame, got_frame); | ||
| 239 | + av_frame_free(&filt_frame); | ||
| 240 | + if (ret < 0) | ||
| 241 | + return ret; | ||
| 242 | + if (!(*got_frame)) | ||
| 243 | + return 0; | ||
| 244 | + | ||
| 245 | + /* prepare packet for muxing */ | ||
| 246 | + enc_pkt.stream_index = stream_index; | ||
| 247 | + av_packet_rescale_ts(&enc_pkt, | ||
| 248 | + ofmt_ctx->streams[stream_index]->codec->time_base, | ||
| 249 | + ofmt_ctx->streams[stream_index]->time_base); | ||
| 250 | + | ||
| 251 | + av_log(NULL, AV_LOG_DEBUG, "Muxing frame\n"); | ||
| 252 | + /* mux encoded frame */ | ||
| 253 | + ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt); | ||
| 254 | +#endif | ||
| 255 | + return ret; | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +#if 0 | ||
| 259 | +static int flush_encoder(unsigned int stream_index) | ||
| 260 | +{ | ||
| 261 | + int ret; | ||
| 262 | + int got_frame; | ||
| 263 | + | ||
| 264 | + if (!(ofmt_ctx->streams[stream_index]->codec->codec->capabilities & | ||
| 265 | + CODEC_CAP_DELAY)) | ||
| 266 | + return 0; | ||
| 267 | + | ||
| 268 | + while (1) { | ||
| 269 | + av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder\n", stream_index); | ||
| 270 | + ret = encode_write_frame(NULL, stream_index, &got_frame); | ||
| 271 | + if (ret < 0) | ||
| 272 | + break; | ||
| 273 | + if (!got_frame) | ||
| 274 | + return 0; | ||
| 275 | + } | ||
| 276 | + return ret; | ||
| 277 | +} | ||
| 278 | +#endif |
pip/AVTranscoder.h
0 → 100644
| 1 | +#pragma once | ||
| 2 | +#include "AVDecoder.h" | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +class CAVTranscoder | ||
| 6 | +{ | ||
| 7 | +public: | ||
| 8 | + CAVTranscoder(); | ||
| 9 | + virtual ~CAVTranscoder(); | ||
| 10 | + | ||
| 11 | + int add(media_info & info); | ||
| 12 | + int64_t transcode(); | ||
| 13 | + bool all_processed(); | ||
| 14 | + int close(); | ||
| 15 | + | ||
| 16 | +protected: | ||
| 17 | + int open_output_file(const char *filename); | ||
| 18 | + | ||
| 19 | +protected: | ||
| 20 | + vector < CAVDecoder *> _decoders; | ||
| 21 | + | ||
| 22 | + AVFormatContext *ofmt_ctx; | ||
| 23 | + int64_t _start_time; | ||
| 24 | + int64_t _cur_a_time; | ||
| 25 | + int64_t _cur_v_time; | ||
| 26 | +private: | ||
| 27 | + int mix_and_output_vframe(vector<CAVDecoder *> & decoders_got_frame); | ||
| 28 | + int mix_and_output_aframe(vector<CAVDecoder *> & decoders_got_frame); | ||
| 29 | + bool _all_processed; | ||
| 30 | +}; | ||
| 31 | + |
pip/AudioDecoder.cpp
0 → 100644
| 1 | +#include "AudioDecoder.h" | ||
| 2 | +AVRational timebase_ms = { 1, 1000 }; | ||
| 3 | + | ||
| 4 | +CAudioDecoder::CAudioDecoder() : | ||
| 5 | +_start_time(-10.0), | ||
| 6 | +_is_finished(false) | ||
| 7 | +{ | ||
| 8 | + | ||
| 9 | +} | ||
| 10 | + | ||
| 11 | +CAudioDecoder::~CAudioDecoder() | ||
| 12 | +{ | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +int CAudioDecoder::add(media_info &info) | ||
| 16 | +{ | ||
| 17 | + if (_start_time < -1.0) {//the the start time of this decoder | ||
| 18 | + _start_time = info.start_time; | ||
| 19 | + } | ||
| 20 | + _info.push_back(info); | ||
| 21 | + | ||
| 22 | + int ret; | ||
| 23 | + | ||
| 24 | + do{ | ||
| 25 | + if ((ret = open_input_file(info.name.c_str())) < 0) | ||
| 26 | + break; | ||
| 27 | + | ||
| 28 | + if ((ret = init_filters()) < 0) | ||
| 29 | + break; | ||
| 30 | + | ||
| 31 | + _is_finished = false; | ||
| 32 | + } while (0); | ||
| 33 | + | ||
| 34 | + return ret ? 1 : 0; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +int CAudioDecoder::init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, | ||
| 40 | + const char *filter_spec) | ||
| 41 | +{ | ||
| 42 | + char args[512]; | ||
| 43 | + int ret = 0; | ||
| 44 | + AVFilter *buffersrc = NULL; | ||
| 45 | + AVFilter *buffersink = NULL; | ||
| 46 | + AVFilterContext *buffersrc_ctx = NULL; | ||
| 47 | + AVFilterContext *buffersink_ctx = NULL; | ||
| 48 | + AVFilterInOut *outputs = avfilter_inout_alloc(); | ||
| 49 | + AVFilterInOut *inputs = avfilter_inout_alloc(); | ||
| 50 | + AVFilterGraph *filter_graph = avfilter_graph_alloc(); | ||
| 51 | + | ||
| 52 | + if (!outputs || !inputs || !filter_graph) { | ||
| 53 | + ret = AVERROR(ENOMEM); | ||
| 54 | + goto end; | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { | ||
| 58 | + buffersrc = avfilter_get_by_name("buffer"); | ||
| 59 | + buffersink = avfilter_get_by_name("buffersink"); | ||
| 60 | + if (!buffersrc || !buffersink) { | ||
| 61 | + av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n"); | ||
| 62 | + ret = AVERROR_UNKNOWN; | ||
| 63 | + goto end; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + snprintf(args, sizeof(args), | ||
| 67 | + "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", | ||
| 68 | + dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, | ||
| 69 | + dec_ctx->time_base.num, dec_ctx->time_base.den, | ||
| 70 | + dec_ctx->sample_aspect_ratio.num, | ||
| 71 | + dec_ctx->sample_aspect_ratio.den); | ||
| 72 | + | ||
| 73 | + ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", | ||
| 74 | + args, NULL, filter_graph); | ||
| 75 | + if (ret < 0) { | ||
| 76 | + av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n"); | ||
| 77 | + goto end; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", | ||
| 81 | + NULL, NULL, filter_graph); | ||
| 82 | + if (ret < 0) { | ||
| 83 | + av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n"); | ||
| 84 | + goto end; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + AVPixelFormat fmt = AV_PIX_FMT_YUV420P; | ||
| 88 | + ret = av_opt_set_bin(buffersink_ctx, "pix_fmts", | ||
| 89 | + (uint8_t*)&fmt, sizeof(AVPixelFormat), | ||
| 90 | + AV_OPT_SEARCH_CHILDREN); | ||
| 91 | + if (ret < 0) { | ||
| 92 | + av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n"); | ||
| 93 | + goto end; | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + else if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 97 | + buffersrc = avfilter_get_by_name("abuffer"); | ||
| 98 | + buffersink = avfilter_get_by_name("abuffersink"); | ||
| 99 | + if (!buffersrc || !buffersink) { | ||
| 100 | + av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n"); | ||
| 101 | + ret = AVERROR_UNKNOWN; | ||
| 102 | + goto end; | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + if (!dec_ctx->channel_layout) | ||
| 106 | + dec_ctx->channel_layout = | ||
| 107 | + av_get_default_channel_layout(dec_ctx->channels); | ||
| 108 | + sprintf(args, //sizeof(args), | ||
| 109 | + "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64, | ||
| 110 | + dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate, | ||
| 111 | + av_get_sample_fmt_name(dec_ctx->sample_fmt), | ||
| 112 | + dec_ctx->channel_layout); | ||
| 113 | + ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", | ||
| 114 | + args, NULL, filter_graph); | ||
| 115 | + if (ret < 0) { | ||
| 116 | + av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n"); | ||
| 117 | + goto end; | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", | ||
| 121 | + NULL, NULL, filter_graph); | ||
| 122 | + if (ret < 0) { | ||
| 123 | + av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n"); | ||
| 124 | + goto end; | ||
| 125 | + } | ||
| 126 | + enum AVSampleFormat sample_fmt = AV_SAMPLE_FMT_S16; | ||
| 127 | + ret = av_opt_set_bin(buffersink_ctx, "sample_fmts", | ||
| 128 | + (uint8_t*)&sample_fmt, sizeof(sample_fmt), | ||
| 129 | + AV_OPT_SEARCH_CHILDREN); | ||
| 130 | + if (ret < 0) { | ||
| 131 | + av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n"); | ||
| 132 | + goto end; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + uint64_t channel_layout = AV_CH_LAYOUT_MONO; | ||
| 136 | + ret = av_opt_set_bin(buffersink_ctx, "channel_layouts", | ||
| 137 | + (uint8_t*)&channel_layout, | ||
| 138 | + sizeof(channel_layout), AV_OPT_SEARCH_CHILDREN); | ||
| 139 | + if (ret < 0) { | ||
| 140 | + av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n"); | ||
| 141 | + goto end; | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + int sample_rate = 46000; | ||
| 145 | + ret = av_opt_set_bin(buffersink_ctx, "sample_rates", | ||
| 146 | + (uint8_t*)&sample_rate, sizeof(sample_rate), | ||
| 147 | + AV_OPT_SEARCH_CHILDREN); | ||
| 148 | + if (ret < 0) { | ||
| 149 | + av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n"); | ||
| 150 | + goto end; | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + else { | ||
| 154 | + ret = AVERROR_UNKNOWN; | ||
| 155 | + goto end; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + /* Endpoints for the filter graph. */ | ||
| 159 | + outputs->name = av_strdup("in"); | ||
| 160 | + outputs->filter_ctx = buffersrc_ctx; | ||
| 161 | + outputs->pad_idx = 0; | ||
| 162 | + outputs->next = NULL; | ||
| 163 | + | ||
| 164 | + inputs->name = av_strdup("out"); | ||
| 165 | + inputs->filter_ctx = buffersink_ctx; | ||
| 166 | + inputs->pad_idx = 0; | ||
| 167 | + inputs->next = NULL; | ||
| 168 | + | ||
| 169 | + if (!outputs->name || !inputs->name) { | ||
| 170 | + ret = AVERROR(ENOMEM); | ||
| 171 | + goto end; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_spec, | ||
| 175 | + &inputs, &outputs, NULL)) < 0) | ||
| 176 | + goto end; | ||
| 177 | + | ||
| 178 | + if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) | ||
| 179 | + goto end; | ||
| 180 | + | ||
| 181 | + /* Fill FilteringContext */ | ||
| 182 | + fctx->buffersrc_ctx = buffersrc_ctx; | ||
| 183 | + fctx->buffersink_ctx = buffersink_ctx; | ||
| 184 | + fctx->filter_graph = filter_graph; | ||
| 185 | + | ||
| 186 | +end: | ||
| 187 | + avfilter_inout_free(&inputs); | ||
| 188 | + avfilter_inout_free(&outputs); | ||
| 189 | + | ||
| 190 | + return ret; | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | +int CAudioDecoder::init_filters(void) | ||
| 194 | +{ | ||
| 195 | + const char *filter_spec; | ||
| 196 | + unsigned int i; | ||
| 197 | + int ret; | ||
| 198 | + filter_ctx = (FilteringContext *)av_malloc_array(ifmt_ctx->nb_streams, sizeof(*filter_ctx)); | ||
| 199 | + if (!filter_ctx) | ||
| 200 | + return AVERROR(ENOMEM); | ||
| 201 | + | ||
| 202 | + for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 203 | + filter_ctx[i].buffersrc_ctx = NULL; | ||
| 204 | + filter_ctx[i].buffersink_ctx = NULL; | ||
| 205 | + filter_ctx[i].filter_graph = NULL; | ||
| 206 | + if (!(ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO | ||
| 207 | + || ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)) | ||
| 208 | + continue; | ||
| 209 | + | ||
| 210 | + | ||
| 211 | + if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { | ||
| 212 | + filter_spec = "fps=fps=20"; /* passthrough (dummy) filter for video */ | ||
| 213 | + _codec_timebase.num = 1; | ||
| 214 | + _codec_timebase.den = 20; | ||
| 215 | + } | ||
| 216 | + else | ||
| 217 | + filter_spec = "anull"; /* passthrough (dummy) filter for audio */ | ||
| 218 | + ret = init_filter(&filter_ctx[i], ifmt_ctx->streams[i]->codec, | ||
| 219 | + filter_spec); | ||
| 220 | + if (ret) | ||
| 221 | + return ret; | ||
| 222 | + } | ||
| 223 | + return 0; | ||
| 224 | +} | ||
| 225 | + | ||
| 226 | +int CAudioDecoder::open_input_file(const char *filename) | ||
| 227 | +{ | ||
| 228 | + int ret; | ||
| 229 | + unsigned int i; | ||
| 230 | + | ||
| 231 | + ifmt_ctx = NULL; | ||
| 232 | + if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) { | ||
| 233 | + av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n"); | ||
| 234 | + return ret; | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) { | ||
| 238 | + av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n"); | ||
| 239 | + return ret; | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 243 | + AVStream *stream; | ||
| 244 | + AVCodecContext *codec_ctx; | ||
| 245 | + stream = ifmt_ctx->streams[i]; | ||
| 246 | + codec_ctx = stream->codec; | ||
| 247 | + /* Reencode video & audio and remux subtitles etc. */ | ||
| 248 | + if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO | ||
| 249 | + || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 250 | + /* Open decoder */ | ||
| 251 | + ret = avcodec_open2(codec_ctx, | ||
| 252 | + avcodec_find_decoder(codec_ctx->codec_id), NULL); | ||
| 253 | + if (ret < 0) { | ||
| 254 | + av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i); | ||
| 255 | + return ret; | ||
| 256 | + } | ||
| 257 | + } | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + av_dump_format(ifmt_ctx, 0, filename, 0); | ||
| 261 | + return 0; | ||
| 262 | +} | ||
| 263 | + | ||
| 264 | + | ||
| 265 | +int CAudioDecoder::filter_encode_write_frame(AVFrame *frame, unsigned int stream_index) | ||
| 266 | +{ | ||
| 267 | + int ret; | ||
| 268 | + AVFrame *filt_frame; | ||
| 269 | + | ||
| 270 | + //av_log(NULL, AV_LOG_INFO, "Pushing decoded frame to filters\n"); | ||
| 271 | + /* push the decoded frame into the filtergraph */ | ||
| 272 | + ret = av_buffersrc_add_frame_flags(filter_ctx[stream_index].buffersrc_ctx, | ||
| 273 | + frame, 0); | ||
| 274 | + if (ret < 0) { | ||
| 275 | + av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n"); | ||
| 276 | + return ret; | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + /* pull filtered frames from the filtergraph */ | ||
| 280 | + while (1) { | ||
| 281 | + filt_frame = av_frame_alloc(); | ||
| 282 | + if (!filt_frame) { | ||
| 283 | + ret = AVERROR(ENOMEM); | ||
| 284 | + break; | ||
| 285 | + } | ||
| 286 | + //av_log(NULL, AV_LOG_INFO, "Pulling filtered frame from filters\n"); | ||
| 287 | + ret = av_buffersink_get_frame(filter_ctx[stream_index].buffersink_ctx, | ||
| 288 | + filt_frame); | ||
| 289 | + if (ret < 0) { | ||
| 290 | + /* if no more frames for output - returns AVERROR(EAGAIN) | ||
| 291 | + * if flushed and no more frames for output - returns AVERROR_EOF | ||
| 292 | + * rewrite retcode to 0 to show it as normal procedure completion | ||
| 293 | + */ | ||
| 294 | + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | ||
| 295 | + ret = 0; | ||
| 296 | + av_frame_free(&filt_frame); | ||
| 297 | + break; | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + filt_frame->pict_type = AV_PICTURE_TYPE_NONE; | ||
| 301 | + //todo: callback filted frame | ||
| 302 | + _decoded_frames.push_back(filt_frame); | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + return ret; | ||
| 306 | +} | ||
| 307 | + | ||
| 308 | +unsigned int CAudioDecoder::getuid() | ||
| 309 | +{ | ||
| 310 | + return 0; | ||
| 311 | +} | ||
| 312 | + | ||
| 313 | +int CAudioDecoder::get_one_frame(AVFrame ** pFrame, int64_t & ts) | ||
| 314 | +{ | ||
| 315 | + int ret; | ||
| 316 | + AVPacket packet; | ||
| 317 | + AVFrame *frame = NULL; | ||
| 318 | + enum AVMediaType type; | ||
| 319 | + unsigned int stream_index; | ||
| 320 | + unsigned int i; | ||
| 321 | + int got_frame; | ||
| 322 | + | ||
| 323 | + if (_decoded_frames.size() > 0) { | ||
| 324 | + *pFrame = _decoded_frames.front(); | ||
| 325 | + ts = av_rescale_q((*pFrame)->pts, _codec_timebase, timebase_ms); | ||
| 326 | + _decoded_frames.pop_front(); | ||
| 327 | + | ||
| 328 | + return 0; | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + if (_is_finished){ | ||
| 332 | + return -1; | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + int(*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket *); | ||
| 336 | + memset(&packet, 0, sizeof(AVPacket)); | ||
| 337 | + | ||
| 338 | + /* read all packets */ | ||
| 339 | + while (1) { | ||
| 340 | + if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0) | ||
| 341 | + break; | ||
| 342 | + stream_index = packet.stream_index; | ||
| 343 | + type = ifmt_ctx->streams[packet.stream_index]->codec->codec_type; | ||
| 344 | + av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u\n", | ||
| 345 | + stream_index); | ||
| 346 | + | ||
| 347 | + if (filter_ctx[stream_index].filter_graph) { | ||
| 348 | + av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n"); | ||
| 349 | + frame = av_frame_alloc(); | ||
| 350 | + if (!frame) { | ||
| 351 | + ret = AVERROR(ENOMEM); | ||
| 352 | + break; | ||
| 353 | + } | ||
| 354 | + | ||
| 355 | + av_packet_rescale_ts(&packet, | ||
| 356 | + ifmt_ctx->streams[stream_index]->time_base, | ||
| 357 | + ifmt_ctx->streams[stream_index]->codec->time_base); | ||
| 358 | + dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 : | ||
| 359 | + avcodec_decode_audio4; | ||
| 360 | + ret = dec_func(ifmt_ctx->streams[stream_index]->codec, frame, | ||
| 361 | + &got_frame, &packet); | ||
| 362 | + if (ret < 0) { | ||
| 363 | + av_frame_free(&frame); | ||
| 364 | + av_log(NULL, AV_LOG_ERROR, "Decoding failed\n"); | ||
| 365 | + break; | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + if (got_frame) { | ||
| 369 | + frame->pts = av_frame_get_best_effort_timestamp(frame); | ||
| 370 | + ret = filter_encode_write_frame(frame, stream_index); | ||
| 371 | + if (_decoded_frames.size() > 0) { | ||
| 372 | + *pFrame = _decoded_frames.front(); | ||
| 373 | + _decoded_frames.pop_front(); | ||
| 374 | + ts = av_rescale_q((*pFrame)->pts, _codec_timebase, timebase_ms); | ||
| 375 | + return 0; | ||
| 376 | + } | ||
| 377 | + if (ret < 0) | ||
| 378 | + goto end; | ||
| 379 | + } | ||
| 380 | + else { | ||
| 381 | + av_frame_free(&frame); | ||
| 382 | + } | ||
| 383 | + } | ||
| 384 | + av_packet_unref(&packet); | ||
| 385 | + | ||
| 386 | + } | ||
| 387 | + | ||
| 388 | + /* flush filters and encoders */ | ||
| 389 | + for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 390 | + /* flush filter */ | ||
| 391 | + if (!filter_ctx[i].filter_graph) | ||
| 392 | + continue; | ||
| 393 | + ret = filter_encode_write_frame(NULL, i); | ||
| 394 | + if (ret < 0) { | ||
| 395 | + av_log(NULL, AV_LOG_ERROR, "Flushing filter failed\n"); | ||
| 396 | + goto end; | ||
| 397 | + } | ||
| 398 | + | ||
| 399 | + } | ||
| 400 | +end:; | ||
| 401 | + av_packet_unref(&packet); | ||
| 402 | + av_frame_free(&frame); | ||
| 403 | + for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 404 | + avcodec_close(ifmt_ctx->streams[i]->codec); | ||
| 405 | + if (filter_ctx && filter_ctx[i].filter_graph) | ||
| 406 | + avfilter_graph_free(&filter_ctx[i].filter_graph); | ||
| 407 | + } | ||
| 408 | + av_free(filter_ctx); | ||
| 409 | + avformat_close_input(&ifmt_ctx); | ||
| 410 | + _is_finished = true; | ||
| 411 | + | ||
| 412 | + return 0; | ||
| 413 | +} |
pip/AudioDecoder.h
0 → 100644
| 1 | +#pragma once | ||
| 2 | +#include "media_info.h" | ||
| 3 | +#include <list> | ||
| 4 | +class CAudioDecoder | ||
| 5 | +{ | ||
| 6 | +public: | ||
| 7 | + CAudioDecoder(); | ||
| 8 | + virtual ~CAudioDecoder(); | ||
| 9 | + | ||
| 10 | + int add(media_info &info); | ||
| 11 | + | ||
| 12 | + unsigned int getuid(); | ||
| 13 | + | ||
| 14 | + int get_one_frame(AVFrame ** pFrame, int64_t & ts); | ||
| 15 | + | ||
| 16 | +protected: | ||
| 17 | + int open_input_file(const char *filename); | ||
| 18 | + int init_filters(void); | ||
| 19 | + int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, const char *filter_spec); | ||
| 20 | + int filter_encode_write_frame(AVFrame *frame, unsigned int stream_index); | ||
| 21 | + | ||
| 22 | +protected: | ||
| 23 | + vector<media_info> _info; | ||
| 24 | + list<AVFrame * > _decoded_frames; | ||
| 25 | + float _start_time; | ||
| 26 | + | ||
| 27 | + AVFormatContext *ifmt_ctx; | ||
| 28 | + | ||
| 29 | + FilteringContext *filter_ctx; | ||
| 30 | + | ||
| 31 | + bool _is_finished; | ||
| 32 | + AVRational _codec_timebase; | ||
| 33 | +}; |
pip/AudioEncoder.cpp
0 → 100644
pip/AudioEncoder.h
0 → 100644
| 1 | #include "VideoDecoder.h" | 1 | #include "VideoDecoder.h" |
| 2 | +extern AVRational timebase_ms; | ||
| 2 | 3 | ||
| 3 | -CVideoDecoder::CVideoDecoder() | 4 | +CVideoDecoder::CVideoDecoder() : |
| 5 | +_start_time(-10.0), | ||
| 6 | +_is_finished(false) | ||
| 4 | { | 7 | { |
| 5 | 8 | ||
| 6 | } | 9 | } |
| @@ -11,10 +14,400 @@ CVideoDecoder::~CVideoDecoder() | @@ -11,10 +14,400 @@ CVideoDecoder::~CVideoDecoder() | ||
| 11 | 14 | ||
| 12 | int CVideoDecoder::add(media_info &info) | 15 | int CVideoDecoder::add(media_info &info) |
| 13 | { | 16 | { |
| 17 | + if (_start_time < -1.0) {//the the start time of this decoder | ||
| 18 | + _start_time = info.start_time; | ||
| 19 | + } | ||
| 20 | + _info.push_back(info); | ||
| 21 | + | ||
| 22 | + int ret; | ||
| 23 | + | ||
| 24 | + do{ | ||
| 25 | + if ((ret = open_input_file(info.name.c_str())) < 0) | ||
| 26 | + break; | ||
| 27 | + | ||
| 28 | + if ((ret = init_filters()) < 0) | ||
| 29 | + break; | ||
| 30 | + | ||
| 31 | + _is_finished = false; | ||
| 32 | + } while (0); | ||
| 33 | + | ||
| 34 | + return ret ? 1 : 0; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +int CVideoDecoder::init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, | ||
| 40 | + const char *filter_spec) | ||
| 41 | +{ | ||
| 42 | + char args[512]; | ||
| 43 | + int ret = 0; | ||
| 44 | + AVFilter *buffersrc = NULL; | ||
| 45 | + AVFilter *buffersink = NULL; | ||
| 46 | + AVFilterContext *buffersrc_ctx = NULL; | ||
| 47 | + AVFilterContext *buffersink_ctx = NULL; | ||
| 48 | + AVFilterInOut *outputs = avfilter_inout_alloc(); | ||
| 49 | + AVFilterInOut *inputs = avfilter_inout_alloc(); | ||
| 50 | + AVFilterGraph *filter_graph = avfilter_graph_alloc(); | ||
| 51 | + | ||
| 52 | + if (!outputs || !inputs || !filter_graph) { | ||
| 53 | + ret = AVERROR(ENOMEM); | ||
| 54 | + goto end; | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { | ||
| 58 | + buffersrc = avfilter_get_by_name("buffer"); | ||
| 59 | + buffersink = avfilter_get_by_name("buffersink"); | ||
| 60 | + if (!buffersrc || !buffersink) { | ||
| 61 | + av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n"); | ||
| 62 | + ret = AVERROR_UNKNOWN; | ||
| 63 | + goto end; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + snprintf(args, sizeof(args), | ||
| 67 | + "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", | ||
| 68 | + dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, | ||
| 69 | + dec_ctx->time_base.num, dec_ctx->time_base.den, | ||
| 70 | + dec_ctx->sample_aspect_ratio.num, | ||
| 71 | + dec_ctx->sample_aspect_ratio.den); | ||
| 72 | + | ||
| 73 | + ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", | ||
| 74 | + args, NULL, filter_graph); | ||
| 75 | + if (ret < 0) { | ||
| 76 | + av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n"); | ||
| 77 | + goto end; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", | ||
| 81 | + NULL, NULL, filter_graph); | ||
| 82 | + if (ret < 0) { | ||
| 83 | + av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n"); | ||
| 84 | + goto end; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + AVPixelFormat fmt = AV_PIX_FMT_YUV420P; | ||
| 88 | + ret = av_opt_set_bin(buffersink_ctx, "pix_fmts", | ||
| 89 | + (uint8_t*)&fmt, sizeof(AVPixelFormat), | ||
| 90 | + AV_OPT_SEARCH_CHILDREN); | ||
| 91 | + if (ret < 0) { | ||
| 92 | + av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n"); | ||
| 93 | + goto end; | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | +#if 0 | ||
| 97 | + else if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 98 | + buffersrc = avfilter_get_by_name("abuffer"); | ||
| 99 | + buffersink = avfilter_get_by_name("abuffersink"); | ||
| 100 | + if (!buffersrc || !buffersink) { | ||
| 101 | + av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n"); | ||
| 102 | + ret = AVERROR_UNKNOWN; | ||
| 103 | + goto end; | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + if (!dec_ctx->channel_layout) | ||
| 107 | + dec_ctx->channel_layout = | ||
| 108 | + av_get_default_channel_layout(dec_ctx->channels); | ||
| 109 | + sprintf(args, //sizeof(args), | ||
| 110 | + "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64, | ||
| 111 | + dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate, | ||
| 112 | + av_get_sample_fmt_name(dec_ctx->sample_fmt), | ||
| 113 | + dec_ctx->channel_layout); | ||
| 114 | + ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", | ||
| 115 | + args, NULL, filter_graph); | ||
| 116 | + if (ret < 0) { | ||
| 117 | + av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n"); | ||
| 118 | + goto end; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", | ||
| 122 | + NULL, NULL, filter_graph); | ||
| 123 | + if (ret < 0) { | ||
| 124 | + av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n"); | ||
| 125 | + goto end; | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + ret = av_opt_set_bin(buffersink_ctx, "sample_fmts", | ||
| 129 | + (uint8_t*)&enc_ctx->sample_fmt, sizeof(enc_ctx->sample_fmt), | ||
| 130 | + AV_OPT_SEARCH_CHILDREN); | ||
| 131 | + if (ret < 0) { | ||
| 132 | + av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n"); | ||
| 133 | + goto end; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + ret = av_opt_set_bin(buffersink_ctx, "channel_layouts", | ||
| 137 | + (uint8_t*)&enc_ctx->channel_layout, | ||
| 138 | + sizeof(enc_ctx->channel_layout), AV_OPT_SEARCH_CHILDREN); | ||
| 139 | + if (ret < 0) { | ||
| 140 | + av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n"); | ||
| 141 | + goto end; | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + ret = av_opt_set_bin(buffersink_ctx, "sample_rates", | ||
| 145 | + (uint8_t*)&enc_ctx->sample_rate, sizeof(enc_ctx->sample_rate), | ||
| 146 | + AV_OPT_SEARCH_CHILDREN); | ||
| 147 | + if (ret < 0) { | ||
| 148 | + av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n"); | ||
| 149 | + goto end; | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | +#endif | ||
| 153 | + else { | ||
| 154 | + ret = AVERROR_UNKNOWN; | ||
| 155 | + goto end; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + /* Endpoints for the filter graph. */ | ||
| 159 | + outputs->name = av_strdup("in"); | ||
| 160 | + outputs->filter_ctx = buffersrc_ctx; | ||
| 161 | + outputs->pad_idx = 0; | ||
| 162 | + outputs->next = NULL; | ||
| 163 | + | ||
| 164 | + inputs->name = av_strdup("out"); | ||
| 165 | + inputs->filter_ctx = buffersink_ctx; | ||
| 166 | + inputs->pad_idx = 0; | ||
| 167 | + inputs->next = NULL; | ||
| 168 | + | ||
| 169 | + if (!outputs->name || !inputs->name) { | ||
| 170 | + ret = AVERROR(ENOMEM); | ||
| 171 | + goto end; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_spec, | ||
| 175 | + &inputs, &outputs, NULL)) < 0) | ||
| 176 | + goto end; | ||
| 177 | + | ||
| 178 | + if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) | ||
| 179 | + goto end; | ||
| 180 | + | ||
| 181 | + /* Fill FilteringContext */ | ||
| 182 | + fctx->buffersrc_ctx = buffersrc_ctx; | ||
| 183 | + fctx->buffersink_ctx = buffersink_ctx; | ||
| 184 | + fctx->filter_graph = filter_graph; | ||
| 185 | + | ||
| 186 | +end: | ||
| 187 | + avfilter_inout_free(&inputs); | ||
| 188 | + avfilter_inout_free(&outputs); | ||
| 189 | + | ||
| 190 | + return ret; | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | +int CVideoDecoder::init_filters(void) | ||
| 194 | +{ | ||
| 195 | + const char *filter_spec; | ||
| 196 | + unsigned int i; | ||
| 197 | + int ret; | ||
| 198 | + filter_ctx = (FilteringContext *)av_malloc_array(ifmt_ctx->nb_streams, sizeof(*filter_ctx)); | ||
| 199 | + if (!filter_ctx) | ||
| 200 | + return AVERROR(ENOMEM); | ||
| 201 | + | ||
| 202 | + for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 203 | + filter_ctx[i].buffersrc_ctx = NULL; | ||
| 204 | + filter_ctx[i].buffersink_ctx = NULL; | ||
| 205 | + filter_ctx[i].filter_graph = NULL; | ||
| 206 | + if (!(ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO | ||
| 207 | + || ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)) | ||
| 208 | + continue; | ||
| 209 | + | ||
| 210 | + | ||
| 211 | + if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { | ||
| 212 | + filter_spec = "fps=fps=20"; /* passthrough (dummy) filter for video */ | ||
| 213 | + _codec_timebase.num = 1; | ||
| 214 | + _codec_timebase.den = 20; | ||
| 215 | + } | ||
| 216 | + else | ||
| 217 | + filter_spec = "anull"; /* passthrough (dummy) filter for audio */ | ||
| 218 | + ret = init_filter(&filter_ctx[i], ifmt_ctx->streams[i]->codec, | ||
| 219 | + filter_spec); | ||
| 220 | + if (ret) | ||
| 221 | + return ret; | ||
| 222 | + } | ||
| 14 | return 0; | 223 | return 0; |
| 15 | } | 224 | } |
| 16 | 225 | ||
| 226 | +int CVideoDecoder::open_input_file(const char *filename) | ||
| 227 | +{ | ||
| 228 | + int ret; | ||
| 229 | + unsigned int i; | ||
| 230 | + | ||
| 231 | + ifmt_ctx = NULL; | ||
| 232 | + if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) { | ||
| 233 | + av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n"); | ||
| 234 | + return ret; | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) { | ||
| 238 | + av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n"); | ||
| 239 | + return ret; | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 243 | + AVStream *stream; | ||
| 244 | + AVCodecContext *codec_ctx; | ||
| 245 | + stream = ifmt_ctx->streams[i]; | ||
| 246 | + codec_ctx = stream->codec; | ||
| 247 | + /* Reencode video & audio and remux subtitles etc. */ | ||
| 248 | + if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO | ||
| 249 | + || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 250 | + /* Open decoder */ | ||
| 251 | + ret = avcodec_open2(codec_ctx, | ||
| 252 | + avcodec_find_decoder(codec_ctx->codec_id), NULL); | ||
| 253 | + if (ret < 0) { | ||
| 254 | + av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i); | ||
| 255 | + return ret; | ||
| 256 | + } | ||
| 257 | + } | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + av_dump_format(ifmt_ctx, 0, filename, 0); | ||
| 261 | + return 0; | ||
| 262 | +} | ||
| 263 | + | ||
| 264 | + | ||
| 265 | +int CVideoDecoder::filter_encode_write_frame(AVFrame *frame, unsigned int stream_index) | ||
| 266 | +{ | ||
| 267 | + int ret; | ||
| 268 | + AVFrame *filt_frame; | ||
| 269 | + | ||
| 270 | + //av_log(NULL, AV_LOG_INFO, "Pushing decoded frame to filters\n"); | ||
| 271 | + /* push the decoded frame into the filtergraph */ | ||
| 272 | + ret = av_buffersrc_add_frame_flags(filter_ctx[stream_index].buffersrc_ctx, | ||
| 273 | + frame, 0); | ||
| 274 | + if (ret < 0) { | ||
| 275 | + av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n"); | ||
| 276 | + return ret; | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + /* pull filtered frames from the filtergraph */ | ||
| 280 | + while (1) { | ||
| 281 | + filt_frame = av_frame_alloc(); | ||
| 282 | + if (!filt_frame) { | ||
| 283 | + ret = AVERROR(ENOMEM); | ||
| 284 | + break; | ||
| 285 | + } | ||
| 286 | + //av_log(NULL, AV_LOG_INFO, "Pulling filtered frame from filters\n"); | ||
| 287 | + ret = av_buffersink_get_frame(filter_ctx[stream_index].buffersink_ctx, | ||
| 288 | + filt_frame); | ||
| 289 | + if (ret < 0) { | ||
| 290 | + /* if no more frames for output - returns AVERROR(EAGAIN) | ||
| 291 | + * if flushed and no more frames for output - returns AVERROR_EOF | ||
| 292 | + * rewrite retcode to 0 to show it as normal procedure completion | ||
| 293 | + */ | ||
| 294 | + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | ||
| 295 | + ret = 0; | ||
| 296 | + av_frame_free(&filt_frame); | ||
| 297 | + break; | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + filt_frame->pict_type = AV_PICTURE_TYPE_NONE; | ||
| 301 | + //todo: callback filted frame | ||
| 302 | + _decoded_frames.push_back(filt_frame); | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + return ret; | ||
| 306 | +} | ||
| 307 | + | ||
| 17 | unsigned int CVideoDecoder::getuid() | 308 | unsigned int CVideoDecoder::getuid() |
| 18 | { | 309 | { |
| 19 | return 0; | 310 | return 0; |
| 20 | } | 311 | } |
| 312 | + | ||
| 313 | +int CVideoDecoder::get_one_frame(AVFrame ** pFrame, int64_t & ts) | ||
| 314 | +{ | ||
| 315 | + int ret; | ||
| 316 | + AVPacket packet; | ||
| 317 | + AVFrame *frame = NULL; | ||
| 318 | + enum AVMediaType type; | ||
| 319 | + unsigned int stream_index; | ||
| 320 | + unsigned int i; | ||
| 321 | + int got_frame; | ||
| 322 | + | ||
| 323 | + if (_decoded_frames.size() > 0) { | ||
| 324 | + *pFrame = _decoded_frames.front(); | ||
| 325 | + ts = av_rescale_q((*pFrame)->pts, _codec_timebase, timebase_ms); | ||
| 326 | + _decoded_frames.pop_front(); | ||
| 327 | + | ||
| 328 | + return 0; | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + if (_is_finished){ | ||
| 332 | + return -1; | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + int(*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket *); | ||
| 336 | + memset(&packet, 0, sizeof(AVPacket)); | ||
| 337 | + | ||
| 338 | + /* read all packets */ | ||
| 339 | + while (1) { | ||
| 340 | + if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0) | ||
| 341 | + break; | ||
| 342 | + stream_index = packet.stream_index; | ||
| 343 | + type = ifmt_ctx->streams[packet.stream_index]->codec->codec_type; | ||
| 344 | + av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u\n", | ||
| 345 | + stream_index); | ||
| 346 | + | ||
| 347 | + if (filter_ctx[stream_index].filter_graph) { | ||
| 348 | + av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n"); | ||
| 349 | + frame = av_frame_alloc(); | ||
| 350 | + if (!frame) { | ||
| 351 | + ret = AVERROR(ENOMEM); | ||
| 352 | + break; | ||
| 353 | + } | ||
| 354 | + | ||
| 355 | + av_packet_rescale_ts(&packet, | ||
| 356 | + ifmt_ctx->streams[stream_index]->time_base, | ||
| 357 | + ifmt_ctx->streams[stream_index]->codec->time_base); | ||
| 358 | + dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 : | ||
| 359 | + avcodec_decode_audio4; | ||
| 360 | + ret = dec_func(ifmt_ctx->streams[stream_index]->codec, frame, | ||
| 361 | + &got_frame, &packet); | ||
| 362 | + if (ret < 0) { | ||
| 363 | + av_frame_free(&frame); | ||
| 364 | + av_log(NULL, AV_LOG_ERROR, "Decoding failed\n"); | ||
| 365 | + break; | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + if (got_frame) { | ||
| 369 | + frame->pts = av_frame_get_best_effort_timestamp(frame); | ||
| 370 | + ret = filter_encode_write_frame(frame, stream_index); | ||
| 371 | + if (_decoded_frames.size() > 0) { | ||
| 372 | + *pFrame = _decoded_frames.front(); | ||
| 373 | + _decoded_frames.pop_front(); | ||
| 374 | + ts = av_rescale_q((*pFrame)->pts, _codec_timebase, timebase_ms); | ||
| 375 | + return 0; | ||
| 376 | + } | ||
| 377 | + if (ret < 0) | ||
| 378 | + goto end; | ||
| 379 | + } | ||
| 380 | + else { | ||
| 381 | + av_frame_free(&frame); | ||
| 382 | + } | ||
| 383 | + } | ||
| 384 | + av_packet_unref(&packet); | ||
| 385 | + | ||
| 386 | + } | ||
| 387 | + | ||
| 388 | + /* flush filters and encoders */ | ||
| 389 | + for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 390 | + /* flush filter */ | ||
| 391 | + if (!filter_ctx[i].filter_graph) | ||
| 392 | + continue; | ||
| 393 | + ret = filter_encode_write_frame(NULL, i); | ||
| 394 | + if (ret < 0) { | ||
| 395 | + av_log(NULL, AV_LOG_ERROR, "Flushing filter failed\n"); | ||
| 396 | + goto end; | ||
| 397 | + } | ||
| 398 | + | ||
| 399 | + } | ||
| 400 | +end:; | ||
| 401 | + av_packet_unref(&packet); | ||
| 402 | + av_frame_free(&frame); | ||
| 403 | + for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 404 | + avcodec_close(ifmt_ctx->streams[i]->codec); | ||
| 405 | + if (filter_ctx && filter_ctx[i].filter_graph) | ||
| 406 | + avfilter_graph_free(&filter_ctx[i].filter_graph); | ||
| 407 | + } | ||
| 408 | + av_free(filter_ctx); | ||
| 409 | + avformat_close_input(&ifmt_ctx); | ||
| 410 | + _is_finished = true; | ||
| 411 | + | ||
| 412 | + return 0; | ||
| 413 | +} |
| 1 | #pragma once | 1 | #pragma once |
| 2 | -#include <string> | ||
| 3 | -#include <vector> | ||
| 4 | -using namespace std; | ||
| 5 | - | ||
| 6 | -enum media_type{ | ||
| 7 | - mt_audio = 0, | ||
| 8 | - mt_video = 1, | ||
| 9 | - mt_av = 3, | ||
| 10 | -}; | ||
| 11 | - | ||
| 12 | -enum media_role { | ||
| 13 | - mr_teacher = 0, | ||
| 14 | - mr_student = 1, | ||
| 15 | -}; | ||
| 16 | - | ||
| 17 | -enum timestamp_type{ | ||
| 18 | - tt_start = 0, | ||
| 19 | - tt_end = 1, | ||
| 20 | -}; | ||
| 21 | - | ||
| 22 | -class media_info { | ||
| 23 | -public: | ||
| 24 | - float type_time;//the time for start or end according to the m_type | ||
| 25 | - float start_time; | ||
| 26 | - float end_time; | ||
| 27 | - string name; | ||
| 28 | - int rotate; | ||
| 29 | - | ||
| 30 | - float duration; | ||
| 31 | - int index; | ||
| 32 | - unsigned int uid; | ||
| 33 | - media_type m_type; | ||
| 34 | - media_role m_role; | ||
| 35 | - timestamp_type t_type; | ||
| 36 | -}; | ||
| 37 | - | 2 | +#include "media_info.h" |
| 3 | +#include <list> | ||
| 38 | class CVideoDecoder | 4 | class CVideoDecoder |
| 39 | { | 5 | { |
| 40 | public: | 6 | public: |
| @@ -45,7 +11,24 @@ public: | @@ -45,7 +11,24 @@ public: | ||
| 45 | 11 | ||
| 46 | unsigned int getuid(); | 12 | unsigned int getuid(); |
| 47 | 13 | ||
| 14 | + int get_one_frame(AVFrame ** pFrame, int64_t & ts); | ||
| 15 | + | ||
| 16 | +protected: | ||
| 17 | + int open_input_file(const char *filename); | ||
| 18 | + int init_filters(void); | ||
| 19 | + int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, const char *filter_spec); | ||
| 20 | + int filter_encode_write_frame(AVFrame *frame, unsigned int stream_index); | ||
| 21 | + | ||
| 48 | protected: | 22 | protected: |
| 49 | vector<media_info> _info; | 23 | vector<media_info> _info; |
| 24 | + list<AVFrame * > _decoded_frames; | ||
| 25 | + float _start_time; | ||
| 26 | + | ||
| 27 | + AVFormatContext *ifmt_ctx; | ||
| 28 | + | ||
| 29 | + FilteringContext *filter_ctx; | ||
| 30 | + | ||
| 31 | + bool _is_finished; | ||
| 32 | + AVRational _codec_timebase; | ||
| 50 | }; | 33 | }; |
| 51 | 34 |
pip/VideoEncoder.cpp
0 → 100644
pip/VideoEncoder.h
0 → 100644
pip/VideoTranscoder.cpp
已删除
100644 → 0
| 1 | -#include "VideoTranscoder.h" | ||
| 2 | - | ||
| 3 | - | ||
| 4 | -CVideoTranscoder::CVideoTranscoder() | ||
| 5 | -{ | ||
| 6 | -} | ||
| 7 | - | ||
| 8 | - | ||
| 9 | -CVideoTranscoder::~CVideoTranscoder() | ||
| 10 | -{ | ||
| 11 | -} | ||
| 12 | - | ||
| 13 | -int CVideoTranscoder::add(media_info & info) | ||
| 14 | -{ | ||
| 15 | - vector < CVideoDecoder *>::iterator it = _decoders.begin(); | ||
| 16 | - for (; it != _decoders.end(); it++) { | ||
| 17 | - if ((*it)->getuid() == info.uid){ | ||
| 18 | - (*it)->add(info); | ||
| 19 | - break; | ||
| 20 | - } | ||
| 21 | - } | ||
| 22 | - if (it == _decoders.end()) { | ||
| 23 | - CVideoDecoder * pVideoDecoder = new CVideoDecoder(); | ||
| 24 | - pVideoDecoder->add(info); | ||
| 25 | - _decoders.push_back(pVideoDecoder); | ||
| 26 | - } | ||
| 27 | - return 0; | ||
| 28 | -} | ||
| 29 | - | ||
| 30 | -float CVideoTranscoder::transcode() | ||
| 31 | -{ | ||
| 32 | - throw std::logic_error("The method or operation is not implemented."); | ||
| 33 | -} | ||
| 34 | - | ||
| 35 | -bool CVideoTranscoder::all_processed() | ||
| 36 | -{ | ||
| 37 | - throw std::logic_error("The method or operation is not implemented."); | ||
| 38 | -} | ||
| 39 | - | ||
| 40 | -int CVideoTranscoder::close() | ||
| 41 | -{ | ||
| 42 | - throw std::logic_error("The method or operation is not implemented."); | ||
| 43 | -} |
pip/VideoTranscoder.h
已删除
100644 → 0
| 1 | -#pragma once | ||
| 2 | -#include "VideoDecoder.h" | ||
| 3 | - | ||
| 4 | - | ||
| 5 | -class CVideoTranscoder | ||
| 6 | -{ | ||
| 7 | -public: | ||
| 8 | - CVideoTranscoder(); | ||
| 9 | - virtual ~CVideoTranscoder(); | ||
| 10 | - | ||
| 11 | - int add(media_info & info); | ||
| 12 | - float transcode(); | ||
| 13 | - bool all_processed(); | ||
| 14 | - int close(); | ||
| 15 | - | ||
| 16 | -protected: | ||
| 17 | - vector < CVideoDecoder *> _decoders; | ||
| 18 | -}; | ||
| 19 | - |
pip/media_info.h
0 → 100644
| 1 | +#pragma once | ||
| 2 | + | ||
| 3 | +#include <string> | ||
| 4 | +#include <vector> | ||
| 5 | +#include <stdint.h> | ||
| 6 | +using namespace std; | ||
| 7 | + | ||
| 8 | +enum media_type{ | ||
| 9 | + mt_audio = 0, | ||
| 10 | + mt_video = 1, | ||
| 11 | + mt_av = 3, | ||
| 12 | +}; | ||
| 13 | + | ||
| 14 | +enum media_role { | ||
| 15 | + mr_teacher = 0, | ||
| 16 | + mr_student = 1, | ||
| 17 | +}; | ||
| 18 | + | ||
| 19 | +enum timestamp_type{ | ||
| 20 | + tt_start = 0, | ||
| 21 | + tt_end = 1, | ||
| 22 | +}; | ||
| 23 | + | ||
| 24 | +class media_info { | ||
| 25 | +public: | ||
| 26 | + float type_time;//the time for start or end according to the m_type | ||
| 27 | + float start_time; | ||
| 28 | + int64_t start_time_ms; | ||
| 29 | + float end_time; | ||
| 30 | + int64_t end_time_ms; | ||
| 31 | + string name; | ||
| 32 | + int rotate; | ||
| 33 | + | ||
| 34 | + float duration; | ||
| 35 | + int index; | ||
| 36 | + unsigned int uid; | ||
| 37 | + media_type m_type; | ||
| 38 | + media_role m_role; | ||
| 39 | + timestamp_type t_type; | ||
| 40 | +}; | ||
| 41 | + | ||
| 42 | +#define __STDC_FORMAT_MACROS | ||
| 43 | +#include <stdint.h> | ||
| 44 | +#include <inttypes.h> | ||
| 45 | +extern "C" { | ||
| 46 | +#include <libavcodec/avcodec.h> | ||
| 47 | +#include <libavformat/avformat.h> | ||
| 48 | +#include <libavfilter/avfiltergraph.h> | ||
| 49 | +#include <libavfilter/buffersink.h> | ||
| 50 | +#include <libavfilter/buffersrc.h> | ||
| 51 | +#include <libavutil/opt.h> | ||
| 52 | +#include <libavutil/pixdesc.h> | ||
| 53 | +} | ||
| 54 | +#ifdef WIN32 | ||
| 55 | +#pragma comment(lib, "avcodec.lib") | ||
| 56 | +#pragma comment(lib, "avdevice.lib") | ||
| 57 | +#pragma comment(lib, "avfilter.lib") | ||
| 58 | +#pragma comment(lib, "avformat.lib") | ||
| 59 | +#pragma comment(lib, "avutil.lib") | ||
| 60 | +#pragma comment(lib, "postproc.lib") | ||
| 61 | +#pragma comment(lib, "swresample.lib") | ||
| 62 | +#pragma comment(lib, "swscale.lib") | ||
| 63 | +#endif | ||
| 64 | + | ||
| 65 | +#if _MSC_VER | ||
| 66 | +#define snprintf _snprintf | ||
| 67 | +#define PRIu64 "I64u" | ||
| 68 | +#define PRId64 "I64d" | ||
| 69 | +#define PRIx64 "I64x" | ||
| 70 | +#define PRIX64 "I64X" | ||
| 71 | +#endif | ||
| 72 | + | ||
| 73 | +typedef struct FilteringContext { | ||
| 74 | + AVFilterContext *buffersink_ctx; | ||
| 75 | + AVFilterContext *buffersrc_ctx; | ||
| 76 | + AVFilterGraph *filter_graph; | ||
| 77 | +} FilteringContext; | ||
| 78 | + | ||
| 79 | + | ||
| 80 | +#define AFRAME_DURATION_MS 20 | ||
| 81 | +#define VFRAME_DURATION_MS 50 |
| @@ -8,7 +8,7 @@ | @@ -8,7 +8,7 @@ | ||
| 8 | #include <list> | 8 | #include <list> |
| 9 | #include <deque> | 9 | #include <deque> |
| 10 | #include "tools.h" | 10 | #include "tools.h" |
| 11 | -#include "VideoTranscoder.h" | 11 | +#include "AVTranscoder.h" |
| 12 | 12 | ||
| 13 | bool only_print = false; | 13 | bool only_print = false; |
| 14 | bool keep_tmp_files = false; | 14 | bool keep_tmp_files = false; |
| @@ -594,6 +594,8 @@ void add_media_infos() | @@ -594,6 +594,8 @@ void add_media_infos() | ||
| 594 | m.rotate = f.rotate; | 594 | m.rotate = f.rotate; |
| 595 | m.m_role = f.m_role; | 595 | m.m_role = f.m_role; |
| 596 | m.uid = f.uid; | 596 | m.uid = f.uid; |
| 597 | + m.start_time_ms = f.start_time * 1000; | ||
| 598 | + m.end_time_ms = f.end_time * 1000; | ||
| 597 | add_media_info(m); | 599 | add_media_info(m); |
| 598 | m.t_type = tt_end; | 600 | m.t_type = tt_end; |
| 599 | m.type_time = m.end_time; | 601 | m.type_time = m.end_time; |
| @@ -871,608 +873,22 @@ int load_record_info(char * record_info) | @@ -871,608 +873,22 @@ int load_record_info(char * record_info) | ||
| 871 | 873 | ||
| 872 | 874 | ||
| 873 | 875 | ||
| 874 | -#define __STDC_FORMAT_MACROS | ||
| 875 | -#include <stdint.h> | ||
| 876 | -#include <inttypes.h> | ||
| 877 | -extern "C" { | ||
| 878 | -#include <libavcodec/avcodec.h> | ||
| 879 | -#include <libavformat/avformat.h> | ||
| 880 | -#include <libavfilter/avfiltergraph.h> | ||
| 881 | -#include <libavfilter/buffersink.h> | ||
| 882 | -#include <libavfilter/buffersrc.h> | ||
| 883 | -#include <libavutil/opt.h> | ||
| 884 | -#include <libavutil/pixdesc.h> | ||
| 885 | -} | ||
| 886 | -#ifdef WIN32 | ||
| 887 | -#pragma comment(lib, "avcodec.lib") | ||
| 888 | -#pragma comment(lib, "avdevice.lib") | ||
| 889 | -#pragma comment(lib, "avfilter.lib") | ||
| 890 | -#pragma comment(lib, "avformat.lib") | ||
| 891 | -#pragma comment(lib, "avutil.lib") | ||
| 892 | -#pragma comment(lib, "postproc.lib") | ||
| 893 | -#pragma comment(lib, "swresample.lib") | ||
| 894 | -#pragma comment(lib, "swscale.lib") | ||
| 895 | -#endif | ||
| 896 | - | ||
| 897 | -#if _MSC_VER | ||
| 898 | -#define snprintf _snprintf | ||
| 899 | -#define PRIu64 "I64u" | ||
| 900 | -#define PRId64 "I64d" | ||
| 901 | -#define PRIx64 "I64x" | ||
| 902 | -#define PRIX64 "I64X" | ||
| 903 | -#endif | ||
| 904 | - | ||
| 905 | - | ||
| 906 | -static AVFormatContext *ifmt_ctx; | ||
| 907 | -static AVFormatContext *ofmt_ctx; | ||
| 908 | -typedef struct FilteringContext { | ||
| 909 | - AVFilterContext *buffersink_ctx; | ||
| 910 | - AVFilterContext *buffersrc_ctx; | ||
| 911 | - AVFilterGraph *filter_graph; | ||
| 912 | -} FilteringContext; | ||
| 913 | -static FilteringContext *filter_ctx; | ||
| 914 | - | ||
| 915 | -static int open_input_file(const char *filename) | ||
| 916 | -{ | ||
| 917 | - int ret; | ||
| 918 | - unsigned int i; | ||
| 919 | - | ||
| 920 | - ifmt_ctx = NULL; | ||
| 921 | - if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) { | ||
| 922 | - av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n"); | ||
| 923 | - return ret; | ||
| 924 | - } | ||
| 925 | - | ||
| 926 | - if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) { | ||
| 927 | - av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n"); | ||
| 928 | - return ret; | ||
| 929 | - } | ||
| 930 | - | ||
| 931 | - for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 932 | - AVStream *stream; | ||
| 933 | - AVCodecContext *codec_ctx; | ||
| 934 | - stream = ifmt_ctx->streams[i]; | ||
| 935 | - codec_ctx = stream->codec; | ||
| 936 | - /* Reencode video & audio and remux subtitles etc. */ | ||
| 937 | - if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO | ||
| 938 | - || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 939 | - /* Open decoder */ | ||
| 940 | - ret = avcodec_open2(codec_ctx, | ||
| 941 | - avcodec_find_decoder(codec_ctx->codec_id), NULL); | ||
| 942 | - if (ret < 0) { | ||
| 943 | - av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i); | ||
| 944 | - return ret; | ||
| 945 | - } | ||
| 946 | - } | ||
| 947 | - } | ||
| 948 | - | ||
| 949 | - av_dump_format(ifmt_ctx, 0, filename, 0); | ||
| 950 | - return 0; | ||
| 951 | -} | ||
| 952 | - | ||
| 953 | -static int open_output_file(const char *filename) | ||
| 954 | -{ | ||
| 955 | - AVStream *out_stream; | ||
| 956 | - AVStream *in_stream; | ||
| 957 | - AVCodecContext *dec_ctx, *enc_ctx; | ||
| 958 | - AVCodec *encoder; | ||
| 959 | - int ret; | ||
| 960 | - unsigned int i; | ||
| 961 | - | ||
| 962 | - ofmt_ctx = NULL; | ||
| 963 | - avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename); | ||
| 964 | - if (!ofmt_ctx) { | ||
| 965 | - av_log(NULL, AV_LOG_ERROR, "Could not create output context\n"); | ||
| 966 | - return AVERROR_UNKNOWN; | ||
| 967 | - } | ||
| 968 | - | ||
| 969 | - | ||
| 970 | - for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 971 | - out_stream = avformat_new_stream(ofmt_ctx, NULL); | ||
| 972 | - if (!out_stream) { | ||
| 973 | - av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n"); | ||
| 974 | - return AVERROR_UNKNOWN; | ||
| 975 | - } | ||
| 976 | - | ||
| 977 | - in_stream = ifmt_ctx->streams[i]; | ||
| 978 | - dec_ctx = in_stream->codec; | ||
| 979 | - enc_ctx = out_stream->codec; | ||
| 980 | - | ||
| 981 | - if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) | ||
| 982 | - enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; | ||
| 983 | - | ||
| 984 | - | ||
| 985 | - if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO | ||
| 986 | - || dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 987 | - /* in this example, we choose transcoding to same codec */ | ||
| 988 | - encoder = avcodec_find_encoder(dec_ctx->codec_id); | ||
| 989 | - if (!encoder) { | ||
| 990 | - av_log(NULL, AV_LOG_FATAL, "Necessary encoder not found\n"); | ||
| 991 | - return AVERROR_INVALIDDATA; | ||
| 992 | - } | ||
| 993 | - | ||
| 994 | - /* In this example, we transcode to same properties (picture size, | ||
| 995 | - * sample rate etc.). These properties can be changed for output | ||
| 996 | - * streams easily using filters */ | ||
| 997 | - if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { | ||
| 998 | - enc_ctx->height = dec_ctx->height; | ||
| 999 | - enc_ctx->width = dec_ctx->width; | ||
| 1000 | - enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio; | ||
| 1001 | - /* take first format from list of supported formats */ | ||
| 1002 | - enc_ctx->pix_fmt = encoder->pix_fmts[0]; | ||
| 1003 | - /* video time_base can be set to whatever is handy and supported by encoder */ | ||
| 1004 | - enc_ctx->time_base = dec_ctx->time_base; | ||
| 1005 | - | ||
| 1006 | - enc_ctx->me_range = 16; | ||
| 1007 | - enc_ctx->max_qdiff = 4; | ||
| 1008 | - enc_ctx->qmin = 10; | ||
| 1009 | - enc_ctx->qmax = 30; | ||
| 1010 | - enc_ctx->qcompress = 0.6; | ||
| 1011 | - } | ||
| 1012 | - else { | ||
| 1013 | - enc_ctx->sample_rate = dec_ctx->sample_rate; | ||
| 1014 | - enc_ctx->channel_layout = dec_ctx->channel_layout; | ||
| 1015 | - enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout); | ||
| 1016 | - /* take first format from list of supported formats */ | ||
| 1017 | - enc_ctx->sample_fmt = encoder->sample_fmts[0]; | ||
| 1018 | - enc_ctx->time_base.num = 1; | ||
| 1019 | - enc_ctx->time_base.den = enc_ctx->sample_rate; | ||
| 1020 | - } | ||
| 1021 | - | ||
| 1022 | - /* Third parameter can be used to pass settings to encoder */ | ||
| 1023 | - ret = avcodec_open2(enc_ctx, encoder, NULL); | ||
| 1024 | - if (ret < 0) { | ||
| 1025 | - av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i); | ||
| 1026 | - return ret; | ||
| 1027 | - } | ||
| 1028 | - } | ||
| 1029 | - else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) { | ||
| 1030 | - av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, cannot proceed\n", i); | ||
| 1031 | - return AVERROR_INVALIDDATA; | ||
| 1032 | - } | ||
| 1033 | - else { | ||
| 1034 | - /* if this stream must be remuxed */ | ||
| 1035 | - ret = avcodec_copy_context(ofmt_ctx->streams[i]->codec, | ||
| 1036 | - ifmt_ctx->streams[i]->codec); | ||
| 1037 | - if (ret < 0) { | ||
| 1038 | - av_log(NULL, AV_LOG_ERROR, "Copying stream context failed\n"); | ||
| 1039 | - return ret; | ||
| 1040 | - } | ||
| 1041 | - } | ||
| 1042 | - } | ||
| 1043 | - av_dump_format(ofmt_ctx, 0, filename, 1); | ||
| 1044 | - | ||
| 1045 | - if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) { | ||
| 1046 | - ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE); | ||
| 1047 | - if (ret < 0) { | ||
| 1048 | - av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", filename); | ||
| 1049 | - return ret; | ||
| 1050 | - } | ||
| 1051 | - } | ||
| 1052 | - | ||
| 1053 | - /* init muxer, write output file header */ | ||
| 1054 | - ret = avformat_write_header(ofmt_ctx, NULL); | ||
| 1055 | - if (ret < 0) { | ||
| 1056 | - av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file\n"); | ||
| 1057 | - return ret; | ||
| 1058 | - } | ||
| 1059 | - | ||
| 1060 | - return 0; | ||
| 1061 | -} | ||
| 1062 | - | ||
| 1063 | -static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, | ||
| 1064 | - AVCodecContext *enc_ctx, const char *filter_spec) | ||
| 1065 | -{ | ||
| 1066 | - char args[512]; | ||
| 1067 | - int ret = 0; | ||
| 1068 | - AVFilter *buffersrc = NULL; | ||
| 1069 | - AVFilter *buffersink = NULL; | ||
| 1070 | - AVFilterContext *buffersrc_ctx = NULL; | ||
| 1071 | - AVFilterContext *buffersink_ctx = NULL; | ||
| 1072 | - AVFilterInOut *outputs = avfilter_inout_alloc(); | ||
| 1073 | - AVFilterInOut *inputs = avfilter_inout_alloc(); | ||
| 1074 | - AVFilterGraph *filter_graph = avfilter_graph_alloc(); | ||
| 1075 | - | ||
| 1076 | - if (!outputs || !inputs || !filter_graph) { | ||
| 1077 | - ret = AVERROR(ENOMEM); | ||
| 1078 | - goto end; | ||
| 1079 | - } | ||
| 1080 | - | ||
| 1081 | - if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { | ||
| 1082 | - buffersrc = avfilter_get_by_name("buffer"); | ||
| 1083 | - buffersink = avfilter_get_by_name("buffersink"); | ||
| 1084 | - if (!buffersrc || !buffersink) { | ||
| 1085 | - av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n"); | ||
| 1086 | - ret = AVERROR_UNKNOWN; | ||
| 1087 | - goto end; | ||
| 1088 | - } | ||
| 1089 | - | ||
| 1090 | - snprintf(args, sizeof(args), | ||
| 1091 | - "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", | ||
| 1092 | - dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, | ||
| 1093 | - dec_ctx->time_base.num, dec_ctx->time_base.den, | ||
| 1094 | - dec_ctx->sample_aspect_ratio.num, | ||
| 1095 | - dec_ctx->sample_aspect_ratio.den); | ||
| 1096 | - | ||
| 1097 | - ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", | ||
| 1098 | - args, NULL, filter_graph); | ||
| 1099 | - if (ret < 0) { | ||
| 1100 | - av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n"); | ||
| 1101 | - goto end; | ||
| 1102 | - } | ||
| 1103 | - | ||
| 1104 | - ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", | ||
| 1105 | - NULL, NULL, filter_graph); | ||
| 1106 | - if (ret < 0) { | ||
| 1107 | - av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n"); | ||
| 1108 | - goto end; | ||
| 1109 | - } | ||
| 1110 | - | ||
| 1111 | - ret = av_opt_set_bin(buffersink_ctx, "pix_fmts", | ||
| 1112 | - (uint8_t*)&enc_ctx->pix_fmt, sizeof(enc_ctx->pix_fmt), | ||
| 1113 | - AV_OPT_SEARCH_CHILDREN); | ||
| 1114 | - if (ret < 0) { | ||
| 1115 | - av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n"); | ||
| 1116 | - goto end; | ||
| 1117 | - } | ||
| 1118 | - } | ||
| 1119 | - else if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 1120 | - buffersrc = avfilter_get_by_name("abuffer"); | ||
| 1121 | - buffersink = avfilter_get_by_name("abuffersink"); | ||
| 1122 | - if (!buffersrc || !buffersink) { | ||
| 1123 | - av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n"); | ||
| 1124 | - ret = AVERROR_UNKNOWN; | ||
| 1125 | - goto end; | ||
| 1126 | - } | ||
| 1127 | - | ||
| 1128 | - if (!dec_ctx->channel_layout) | ||
| 1129 | - dec_ctx->channel_layout = | ||
| 1130 | - av_get_default_channel_layout(dec_ctx->channels); | ||
| 1131 | - sprintf(args, //sizeof(args), | ||
| 1132 | - "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64, | ||
| 1133 | - dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate, | ||
| 1134 | - av_get_sample_fmt_name(dec_ctx->sample_fmt), | ||
| 1135 | - dec_ctx->channel_layout); | ||
| 1136 | - ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", | ||
| 1137 | - args, NULL, filter_graph); | ||
| 1138 | - if (ret < 0) { | ||
| 1139 | - av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n"); | ||
| 1140 | - goto end; | ||
| 1141 | - } | ||
| 1142 | - | ||
| 1143 | - ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", | ||
| 1144 | - NULL, NULL, filter_graph); | ||
| 1145 | - if (ret < 0) { | ||
| 1146 | - av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n"); | ||
| 1147 | - goto end; | ||
| 1148 | - } | ||
| 1149 | - | ||
| 1150 | - ret = av_opt_set_bin(buffersink_ctx, "sample_fmts", | ||
| 1151 | - (uint8_t*)&enc_ctx->sample_fmt, sizeof(enc_ctx->sample_fmt), | ||
| 1152 | - AV_OPT_SEARCH_CHILDREN); | ||
| 1153 | - if (ret < 0) { | ||
| 1154 | - av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n"); | ||
| 1155 | - goto end; | ||
| 1156 | - } | ||
| 1157 | - | ||
| 1158 | - ret = av_opt_set_bin(buffersink_ctx, "channel_layouts", | ||
| 1159 | - (uint8_t*)&enc_ctx->channel_layout, | ||
| 1160 | - sizeof(enc_ctx->channel_layout), AV_OPT_SEARCH_CHILDREN); | ||
| 1161 | - if (ret < 0) { | ||
| 1162 | - av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n"); | ||
| 1163 | - goto end; | ||
| 1164 | - } | ||
| 1165 | - | ||
| 1166 | - ret = av_opt_set_bin(buffersink_ctx, "sample_rates", | ||
| 1167 | - (uint8_t*)&enc_ctx->sample_rate, sizeof(enc_ctx->sample_rate), | ||
| 1168 | - AV_OPT_SEARCH_CHILDREN); | ||
| 1169 | - if (ret < 0) { | ||
| 1170 | - av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n"); | ||
| 1171 | - goto end; | ||
| 1172 | - } | ||
| 1173 | - } | ||
| 1174 | - else { | ||
| 1175 | - ret = AVERROR_UNKNOWN; | ||
| 1176 | - goto end; | ||
| 1177 | - } | ||
| 1178 | - | ||
| 1179 | - /* Endpoints for the filter graph. */ | ||
| 1180 | - outputs->name = av_strdup("in"); | ||
| 1181 | - outputs->filter_ctx = buffersrc_ctx; | ||
| 1182 | - outputs->pad_idx = 0; | ||
| 1183 | - outputs->next = NULL; | ||
| 1184 | - | ||
| 1185 | - inputs->name = av_strdup("out"); | ||
| 1186 | - inputs->filter_ctx = buffersink_ctx; | ||
| 1187 | - inputs->pad_idx = 0; | ||
| 1188 | - inputs->next = NULL; | ||
| 1189 | - | ||
| 1190 | - if (!outputs->name || !inputs->name) { | ||
| 1191 | - ret = AVERROR(ENOMEM); | ||
| 1192 | - goto end; | ||
| 1193 | - } | ||
| 1194 | - | ||
| 1195 | - if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_spec, | ||
| 1196 | - &inputs, &outputs, NULL)) < 0) | ||
| 1197 | - goto end; | ||
| 1198 | - | ||
| 1199 | - if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) | ||
| 1200 | - goto end; | ||
| 1201 | - | ||
| 1202 | - /* Fill FilteringContext */ | ||
| 1203 | - fctx->buffersrc_ctx = buffersrc_ctx; | ||
| 1204 | - fctx->buffersink_ctx = buffersink_ctx; | ||
| 1205 | - fctx->filter_graph = filter_graph; | ||
| 1206 | - | ||
| 1207 | -end: | ||
| 1208 | - avfilter_inout_free(&inputs); | ||
| 1209 | - avfilter_inout_free(&outputs); | ||
| 1210 | - | ||
| 1211 | - return ret; | ||
| 1212 | -} | ||
| 1213 | - | ||
| 1214 | -static int init_filters(void) | ||
| 1215 | -{ | ||
| 1216 | - const char *filter_spec; | ||
| 1217 | - unsigned int i; | ||
| 1218 | - int ret; | ||
| 1219 | - filter_ctx = (FilteringContext *)av_malloc_array(ifmt_ctx->nb_streams, sizeof(*filter_ctx)); | ||
| 1220 | - if (!filter_ctx) | ||
| 1221 | - return AVERROR(ENOMEM); | ||
| 1222 | - | ||
| 1223 | - for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 1224 | - filter_ctx[i].buffersrc_ctx = NULL; | ||
| 1225 | - filter_ctx[i].buffersink_ctx = NULL; | ||
| 1226 | - filter_ctx[i].filter_graph = NULL; | ||
| 1227 | - if (!(ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO | ||
| 1228 | - || ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)) | ||
| 1229 | - continue; | ||
| 1230 | - | ||
| 1231 | - | ||
| 1232 | - if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) | ||
| 1233 | - filter_spec = "null"; /* passthrough (dummy) filter for video */ | ||
| 1234 | - else | ||
| 1235 | - filter_spec = "anull"; /* passthrough (dummy) filter for audio */ | ||
| 1236 | - ret = init_filter(&filter_ctx[i], ifmt_ctx->streams[i]->codec, | ||
| 1237 | - ofmt_ctx->streams[i]->codec, filter_spec); | ||
| 1238 | - if (ret) | ||
| 1239 | - return ret; | ||
| 1240 | - } | ||
| 1241 | - return 0; | ||
| 1242 | -} | ||
| 1243 | - | ||
| 1244 | -static int encode_write_frame(AVFrame *filt_frame, unsigned int stream_index, int *got_frame) { | ||
| 1245 | - int ret; | ||
| 1246 | - int got_frame_local; | ||
| 1247 | - AVPacket enc_pkt; | ||
| 1248 | - int(*enc_func)(AVCodecContext *, AVPacket *, const AVFrame *, int *) = | ||
| 1249 | - (ifmt_ctx->streams[stream_index]->codec->codec_type == | ||
| 1250 | - AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2; | ||
| 1251 | - | ||
| 1252 | - if (!got_frame) | ||
| 1253 | - got_frame = &got_frame_local; | ||
| 1254 | - | ||
| 1255 | - av_log(NULL, AV_LOG_INFO, "Encoding frame\n"); | ||
| 1256 | - /* encode filtered frame */ | ||
| 1257 | - enc_pkt.data = NULL; | ||
| 1258 | - enc_pkt.size = 0; | ||
| 1259 | - av_init_packet(&enc_pkt); | ||
| 1260 | - ret = enc_func(ofmt_ctx->streams[stream_index]->codec, &enc_pkt, | ||
| 1261 | - filt_frame, got_frame); | ||
| 1262 | - av_frame_free(&filt_frame); | ||
| 1263 | - if (ret < 0) | ||
| 1264 | - return ret; | ||
| 1265 | - if (!(*got_frame)) | ||
| 1266 | - return 0; | ||
| 1267 | - | ||
| 1268 | - /* prepare packet for muxing */ | ||
| 1269 | - enc_pkt.stream_index = stream_index; | ||
| 1270 | - av_packet_rescale_ts(&enc_pkt, | ||
| 1271 | - ofmt_ctx->streams[stream_index]->codec->time_base, | ||
| 1272 | - ofmt_ctx->streams[stream_index]->time_base); | ||
| 1273 | - | ||
| 1274 | - av_log(NULL, AV_LOG_DEBUG, "Muxing frame\n"); | ||
| 1275 | - /* mux encoded frame */ | ||
| 1276 | - ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt); | ||
| 1277 | - return ret; | ||
| 1278 | -} | ||
| 1279 | - | ||
| 1280 | -static int filter_encode_write_frame(AVFrame *frame, unsigned int stream_index) | ||
| 1281 | -{ | ||
| 1282 | - int ret; | ||
| 1283 | - AVFrame *filt_frame; | ||
| 1284 | - | ||
| 1285 | - av_log(NULL, AV_LOG_INFO, "Pushing decoded frame to filters\n"); | ||
| 1286 | - /* push the decoded frame into the filtergraph */ | ||
| 1287 | - ret = av_buffersrc_add_frame_flags(filter_ctx[stream_index].buffersrc_ctx, | ||
| 1288 | - frame, 0); | ||
| 1289 | - if (ret < 0) { | ||
| 1290 | - av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n"); | ||
| 1291 | - return ret; | ||
| 1292 | - } | ||
| 1293 | - | ||
| 1294 | - /* pull filtered frames from the filtergraph */ | ||
| 1295 | - while (1) { | ||
| 1296 | - filt_frame = av_frame_alloc(); | ||
| 1297 | - if (!filt_frame) { | ||
| 1298 | - ret = AVERROR(ENOMEM); | ||
| 1299 | - break; | ||
| 1300 | - } | ||
| 1301 | - av_log(NULL, AV_LOG_INFO, "Pulling filtered frame from filters\n"); | ||
| 1302 | - ret = av_buffersink_get_frame(filter_ctx[stream_index].buffersink_ctx, | ||
| 1303 | - filt_frame); | ||
| 1304 | - if (ret < 0) { | ||
| 1305 | - /* if no more frames for output - returns AVERROR(EAGAIN) | ||
| 1306 | - * if flushed and no more frames for output - returns AVERROR_EOF | ||
| 1307 | - * rewrite retcode to 0 to show it as normal procedure completion | ||
| 1308 | - */ | ||
| 1309 | - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | ||
| 1310 | - ret = 0; | ||
| 1311 | - av_frame_free(&filt_frame); | ||
| 1312 | - break; | ||
| 1313 | - } | ||
| 1314 | - | ||
| 1315 | - filt_frame->pict_type = AV_PICTURE_TYPE_NONE; | ||
| 1316 | - ret = encode_write_frame(filt_frame, stream_index, NULL); | ||
| 1317 | - if (ret < 0) | ||
| 1318 | - break; | ||
| 1319 | - } | ||
| 1320 | - | ||
| 1321 | - return ret; | ||
| 1322 | -} | ||
| 1323 | - | ||
| 1324 | -static int flush_encoder(unsigned int stream_index) | ||
| 1325 | -{ | ||
| 1326 | - int ret; | ||
| 1327 | - int got_frame; | ||
| 1328 | - | ||
| 1329 | - if (!(ofmt_ctx->streams[stream_index]->codec->codec->capabilities & | ||
| 1330 | - CODEC_CAP_DELAY)) | ||
| 1331 | - return 0; | ||
| 1332 | - | ||
| 1333 | - while (1) { | ||
| 1334 | - av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder\n", stream_index); | ||
| 1335 | - ret = encode_write_frame(NULL, stream_index, &got_frame); | ||
| 1336 | - if (ret < 0) | ||
| 1337 | - break; | ||
| 1338 | - if (!got_frame) | ||
| 1339 | - return 0; | ||
| 1340 | - } | ||
| 1341 | - return ret; | ||
| 1342 | -} | ||
| 1343 | - | ||
| 1344 | -int transcode(const char * input){ | ||
| 1345 | - int ret; | ||
| 1346 | - AVPacket packet; | ||
| 1347 | - AVFrame *frame = NULL; | ||
| 1348 | - enum AVMediaType type; | ||
| 1349 | - unsigned int stream_index; | ||
| 1350 | - unsigned int i; | ||
| 1351 | - int got_frame; | ||
| 1352 | - int(*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket *); | ||
| 1353 | - memset(&packet, 0, sizeof(AVPacket)); | ||
| 1354 | - | ||
| 1355 | - char output[1024]; | ||
| 1356 | - get_output_file_name(input, "pip_", output); | ||
| 1357 | - | ||
| 1358 | - if ((ret = open_input_file(input)) < 0) | ||
| 1359 | - goto end; | ||
| 1360 | - if ((ret = open_output_file(output)) < 0) | ||
| 1361 | - goto end; | ||
| 1362 | - if ((ret = init_filters()) < 0) | ||
| 1363 | - goto end; | ||
| 1364 | - | ||
| 1365 | - /* read all packets */ | ||
| 1366 | - while (1) { | ||
| 1367 | - if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0) | ||
| 1368 | - break; | ||
| 1369 | - stream_index = packet.stream_index; | ||
| 1370 | - type = ifmt_ctx->streams[packet.stream_index]->codec->codec_type; | ||
| 1371 | - av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u\n", | ||
| 1372 | - stream_index); | ||
| 1373 | - | ||
| 1374 | - if (filter_ctx[stream_index].filter_graph) { | ||
| 1375 | - av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n"); | ||
| 1376 | - frame = av_frame_alloc(); | ||
| 1377 | - if (!frame) { | ||
| 1378 | - ret = AVERROR(ENOMEM); | ||
| 1379 | - break; | ||
| 1380 | - } | ||
| 1381 | - av_packet_rescale_ts(&packet, | ||
| 1382 | - ifmt_ctx->streams[stream_index]->time_base, | ||
| 1383 | - ifmt_ctx->streams[stream_index]->codec->time_base); | ||
| 1384 | - dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 : | ||
| 1385 | - avcodec_decode_audio4; | ||
| 1386 | - ret = dec_func(ifmt_ctx->streams[stream_index]->codec, frame, | ||
| 1387 | - &got_frame, &packet); | ||
| 1388 | - if (ret < 0) { | ||
| 1389 | - av_frame_free(&frame); | ||
| 1390 | - av_log(NULL, AV_LOG_ERROR, "Decoding failed\n"); | ||
| 1391 | - break; | ||
| 1392 | - } | ||
| 1393 | - | ||
| 1394 | - if (got_frame) { | ||
| 1395 | - frame->pts = av_frame_get_best_effort_timestamp(frame); | ||
| 1396 | - ret = filter_encode_write_frame(frame, stream_index); | ||
| 1397 | - av_frame_free(&frame); | ||
| 1398 | - if (ret < 0) | ||
| 1399 | - goto end; | ||
| 1400 | - } | ||
| 1401 | - else { | ||
| 1402 | - av_frame_free(&frame); | ||
| 1403 | - } | ||
| 1404 | - } | ||
| 1405 | - else { | ||
| 1406 | - /* remux this frame without reencoding */ | ||
| 1407 | - av_packet_rescale_ts(&packet, | ||
| 1408 | - ifmt_ctx->streams[stream_index]->time_base, | ||
| 1409 | - ofmt_ctx->streams[stream_index]->time_base); | ||
| 1410 | - | ||
| 1411 | - ret = av_interleaved_write_frame(ofmt_ctx, &packet); | ||
| 1412 | - if (ret < 0) | ||
| 1413 | - goto end; | ||
| 1414 | - } | ||
| 1415 | - av_packet_unref(&packet); | ||
| 1416 | - } | ||
| 1417 | - | ||
| 1418 | - /* flush filters and encoders */ | ||
| 1419 | - for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 1420 | - /* flush filter */ | ||
| 1421 | - if (!filter_ctx[i].filter_graph) | ||
| 1422 | - continue; | ||
| 1423 | - ret = filter_encode_write_frame(NULL, i); | ||
| 1424 | - if (ret < 0) { | ||
| 1425 | - av_log(NULL, AV_LOG_ERROR, "Flushing filter failed\n"); | ||
| 1426 | - goto end; | ||
| 1427 | - } | ||
| 1428 | - | ||
| 1429 | - /* flush encoder */ | ||
| 1430 | - ret = flush_encoder(i); | ||
| 1431 | - if (ret < 0) { | ||
| 1432 | - av_log(NULL, AV_LOG_ERROR, "Flushing encoder failed\n"); | ||
| 1433 | - goto end; | ||
| 1434 | - } | ||
| 1435 | - } | ||
| 1436 | - | ||
| 1437 | - av_write_trailer(ofmt_ctx); | ||
| 1438 | -end: | ||
| 1439 | - av_packet_unref(&packet); | ||
| 1440 | - av_frame_free(&frame); | ||
| 1441 | - for (i = 0; i < ifmt_ctx->nb_streams; i++) { | ||
| 1442 | - avcodec_close(ifmt_ctx->streams[i]->codec); | ||
| 1443 | - if (ofmt_ctx && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] && ofmt_ctx->streams[i]->codec) | ||
| 1444 | - avcodec_close(ofmt_ctx->streams[i]->codec); | ||
| 1445 | - if (filter_ctx && filter_ctx[i].filter_graph) | ||
| 1446 | - avfilter_graph_free(&filter_ctx[i].filter_graph); | ||
| 1447 | - } | ||
| 1448 | - av_free(filter_ctx); | ||
| 1449 | - avformat_close_input(&ifmt_ctx); | ||
| 1450 | - if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) | ||
| 1451 | - avio_closep(&ofmt_ctx->pb); | ||
| 1452 | - avformat_free_context(ofmt_ctx); | ||
| 1453 | - | ||
| 1454 | - //if (ret < 0) | ||
| 1455 | - // av_log(NULL, AV_LOG_ERROR, "Error occurred: %s\n", av_err2str(ret)); | ||
| 1456 | - | ||
| 1457 | - return ret ? 1 : 0; | ||
| 1458 | -} | ||
| 1459 | - | ||
| 1460 | -#define MIN_TIME_INTERVAL 0.2 | 876 | +#define MIN_TIME_INTERVAL 25 |
| 1461 | 877 | ||
| 1462 | int process_av_files() | 878 | int process_av_files() |
| 1463 | { | 879 | { |
| 1464 | av_register_all(); | 880 | av_register_all(); |
| 1465 | avfilter_register_all(); | 881 | avfilter_register_all(); |
| 1466 | 882 | ||
| 1467 | - CVideoTranscoder videoTranscoder; | 883 | + CAVTranscoder videoTranscoder; |
| 1468 | 884 | ||
| 1469 | - float cur_time = 0.0; | 885 | + int64_t cur_time = 0; |
| 1470 | bool has_file = sorted_media.size(); | 886 | bool has_file = sorted_media.size(); |
| 1471 | while (has_file){ | 887 | while (has_file){ |
| 1472 | while (has_file){ | 888 | while (has_file){ |
| 1473 | media_info info = sorted_media.front(); | 889 | media_info info = sorted_media.front(); |
| 1474 | 890 | ||
| 1475 | - if (info.start_time - cur_time < MIN_TIME_INTERVAL) { | 891 | + if (info.start_time_ms - cur_time < MIN_TIME_INTERVAL) { |
| 1476 | sorted_media.pop_front(); | 892 | sorted_media.pop_front(); |
| 1477 | videoTranscoder.add(info); | 893 | videoTranscoder.add(info); |
| 1478 | } | 894 | } |
| @@ -82,14 +82,23 @@ | @@ -82,14 +82,23 @@ | ||
| 82 | <Text Include="ReadMe.txt" /> | 82 | <Text Include="ReadMe.txt" /> |
| 83 | </ItemGroup> | 83 | </ItemGroup> |
| 84 | <ItemGroup> | 84 | <ItemGroup> |
| 85 | + <ClCompile Include="AudioDecoder.cpp" /> | ||
| 86 | + <ClCompile Include="AudioEncoder.cpp" /> | ||
| 87 | + <ClCompile Include="AVDecoder.cpp" /> | ||
| 85 | <ClCompile Include="merge_pip.cpp" /> | 88 | <ClCompile Include="merge_pip.cpp" /> |
| 86 | <ClCompile Include="tools.cpp" /> | 89 | <ClCompile Include="tools.cpp" /> |
| 87 | <ClCompile Include="VideoDecoder.cpp" /> | 90 | <ClCompile Include="VideoDecoder.cpp" /> |
| 88 | - <ClCompile Include="VideoTranscoder.cpp" /> | 91 | + <ClCompile Include="AVTranscoder.cpp" /> |
| 92 | + <ClCompile Include="VideoEncoder.cpp" /> | ||
| 89 | </ItemGroup> | 93 | </ItemGroup> |
| 90 | <ItemGroup> | 94 | <ItemGroup> |
| 95 | + <ClInclude Include="AudioDecoder.h" /> | ||
| 96 | + <ClInclude Include="AudioEncoder.h" /> | ||
| 97 | + <ClInclude Include="AVDecoder.h" /> | ||
| 98 | + <ClInclude Include="media_info.h" /> | ||
| 91 | <ClInclude Include="VideoDecoder.h" /> | 99 | <ClInclude Include="VideoDecoder.h" /> |
| 92 | - <ClInclude Include="VideoTranscoder.h" /> | 100 | + <ClInclude Include="AVTranscoder.h" /> |
| 101 | + <ClInclude Include="VideoEncoder.h" /> | ||
| 93 | </ItemGroup> | 102 | </ItemGroup> |
| 94 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | 103 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |
| 95 | <ImportGroup Label="ExtensionTargets"> | 104 | <ImportGroup Label="ExtensionTargets"> |
| @@ -24,18 +24,45 @@ | @@ -24,18 +24,45 @@ | ||
| 24 | <ClCompile Include="merge_pip.cpp"> | 24 | <ClCompile Include="merge_pip.cpp"> |
| 25 | <Filter>源文件</Filter> | 25 | <Filter>源文件</Filter> |
| 26 | </ClCompile> | 26 | </ClCompile> |
| 27 | - <ClCompile Include="VideoTranscoder.cpp"> | 27 | + <ClCompile Include="VideoDecoder.cpp"> |
| 28 | <Filter>源文件</Filter> | 28 | <Filter>源文件</Filter> |
| 29 | </ClCompile> | 29 | </ClCompile> |
| 30 | - <ClCompile Include="VideoDecoder.cpp"> | 30 | + <ClCompile Include="AVDecoder.cpp"> |
| 31 | + <Filter>源文件</Filter> | ||
| 32 | + </ClCompile> | ||
| 33 | + <ClCompile Include="AudioDecoder.cpp"> | ||
| 34 | + <Filter>源文件</Filter> | ||
| 35 | + </ClCompile> | ||
| 36 | + <ClCompile Include="AVTranscoder.cpp"> | ||
| 37 | + <Filter>源文件</Filter> | ||
| 38 | + </ClCompile> | ||
| 39 | + <ClCompile Include="VideoEncoder.cpp"> | ||
| 40 | + <Filter>源文件</Filter> | ||
| 41 | + </ClCompile> | ||
| 42 | + <ClCompile Include="AudioEncoder.cpp"> | ||
| 31 | <Filter>源文件</Filter> | 43 | <Filter>源文件</Filter> |
| 32 | </ClCompile> | 44 | </ClCompile> |
| 33 | </ItemGroup> | 45 | </ItemGroup> |
| 34 | <ItemGroup> | 46 | <ItemGroup> |
| 35 | - <ClInclude Include="VideoTranscoder.h"> | 47 | + <ClInclude Include="VideoDecoder.h"> |
| 36 | <Filter>头文件</Filter> | 48 | <Filter>头文件</Filter> |
| 37 | </ClInclude> | 49 | </ClInclude> |
| 38 | - <ClInclude Include="VideoDecoder.h"> | 50 | + <ClInclude Include="AVDecoder.h"> |
| 51 | + <Filter>头文件</Filter> | ||
| 52 | + </ClInclude> | ||
| 53 | + <ClInclude Include="AudioDecoder.h"> | ||
| 54 | + <Filter>头文件</Filter> | ||
| 55 | + </ClInclude> | ||
| 56 | + <ClInclude Include="media_info.h"> | ||
| 57 | + <Filter>头文件</Filter> | ||
| 58 | + </ClInclude> | ||
| 59 | + <ClInclude Include="AVTranscoder.h"> | ||
| 60 | + <Filter>头文件</Filter> | ||
| 61 | + </ClInclude> | ||
| 62 | + <ClInclude Include="VideoEncoder.h"> | ||
| 63 | + <Filter>头文件</Filter> | ||
| 64 | + </ClInclude> | ||
| 65 | + <ClInclude Include="AudioEncoder.h"> | ||
| 39 | <Filter>头文件</Filter> | 66 | <Filter>头文件</Filter> |
| 40 | </ClInclude> | 67 | </ClInclude> |
| 41 | </ItemGroup> | 68 | </ItemGroup> |
-
请 注册 或 登录 后发表评论