1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "host-common/MediaFfmpegVideoHelper.h"
16 #include "host-common/YuvConverter.h"
17 #include "android/utils/debug.h"
18
19 #define MEDIA_FFMPEG_DEBUG 0
20
21 #if MEDIA_FFMPEG_DEBUG
22 #define MEDIA_DPRINT(fmt, ...) \
23 fprintf(stderr, "media-ffmpeg-video-helper: %s:%d " fmt "\n", __func__, \
24 __LINE__, ##__VA_ARGS__);
25 #else
26 #define MEDIA_DPRINT(fmt, ...)
27 #endif
28
29 namespace android {
30 namespace emulation {
31
32 using FrameInfo = MediaSnapshotState::FrameInfo;
33 using ColorAspects = MediaSnapshotState::ColorAspects;
34
MediaFfmpegVideoHelper(int type,int threads)35 MediaFfmpegVideoHelper::MediaFfmpegVideoHelper(int type, int threads)
36 : mType(type), mThreadCount(threads) {}
37
init()38 bool MediaFfmpegVideoHelper::init() {
39 // standard ffmpeg codec stuff
40 avcodec_register_all();
41 bool print_all_decoder = false;
42 #if MEDIA_FFMPEG_DEBUG
43 print_all_decoder = false;
44 #endif
45 if (print_all_decoder) {
46 AVCodec* current_codec = NULL;
47
48 current_codec = av_codec_next(current_codec);
49 while (current_codec != NULL) {
50 if (av_codec_is_decoder(current_codec)) {
51 MEDIA_DPRINT("codec decoder found %s long name %s",
52 current_codec->name, current_codec->long_name);
53 }
54 current_codec = av_codec_next(current_codec);
55 }
56 }
57
58 AVCodecID codecType;
59 switch (mType) {
60 case 8:
61 codecType = AV_CODEC_ID_VP8;
62 MEDIA_DPRINT("create vp8 ffmpeg decoder%d", (int)mType);
63 break;
64 case 9:
65 codecType = AV_CODEC_ID_VP9;
66 MEDIA_DPRINT("create vp9 ffmpeg decoder%d", (int)mType);
67 break;
68 case 264:
69 codecType = AV_CODEC_ID_H264;
70 MEDIA_DPRINT("create h264 ffmpeg decoder%d", (int)mType);
71 break;
72 default:
73 MEDIA_DPRINT("invalid codec %d", (int)mType);
74 return false;
75 }
76
77 mCodec = NULL;
78 mCodec = avcodec_find_decoder(codecType);
79 if (!mCodec) {
80 MEDIA_DPRINT("cannot find codec");
81 return false;
82 }
83
84 mCodecCtx = avcodec_alloc_context3(mCodec);
85
86 if (mThreadCount > 1) {
87 mCodecCtx->thread_count = std::min(mThreadCount, 4);
88 mCodecCtx->thread_type = FF_THREAD_FRAME;
89 mCodecCtx->active_thread_type = FF_THREAD_FRAME;
90 }
91 avcodec_open2(mCodecCtx, mCodec, 0);
92 mFrame = av_frame_alloc();
93
94 MEDIA_DPRINT("Successfully created software h264 decoder context %p",
95 mCodecCtx);
96
97 dprint("successfully created ffmpeg video decoder for %s",
98 mType == 264 ? "H264" : (mType == 8 ? "VP8" : "VP9"));
99
100 return true;
101 }
102
~MediaFfmpegVideoHelper()103 MediaFfmpegVideoHelper::~MediaFfmpegVideoHelper() {
104 deInit();
105 }
106
deInit()107 void MediaFfmpegVideoHelper::deInit() {
108 MEDIA_DPRINT("Destroy %p", this);
109 mSavedDecodedFrames.clear();
110 if (mCodecCtx) {
111 avcodec_flush_buffers(mCodecCtx);
112 avcodec_close(mCodecCtx);
113 avcodec_free_context(&mCodecCtx);
114 mCodecCtx = NULL;
115 mCodec = NULL;
116 }
117 if (mFrame) {
118 av_frame_free(&mFrame);
119 mFrame = NULL;
120 }
121 }
122
copyFrame()123 void MediaFfmpegVideoHelper::copyFrame() {
124 int w = mFrame->width;
125 int h = mFrame->height;
126 mDecodedFrame.resize(w * h * 3 / 2);
127 MEDIA_DPRINT("w %d h %d Y line size %d U line size %d V line size %d", w, h,
128 mFrame->linesize[0], mFrame->linesize[1], mFrame->linesize[2]);
129 for (int i = 0; i < h; ++i) {
130 memcpy(mDecodedFrame.data() + i * w,
131 mFrame->data[0] + i * mFrame->linesize[0], w);
132 }
133 MEDIA_DPRINT("format is %d and NV21 is %d NV12 is %d", mFrame->format,
134 (int)AV_PIX_FMT_NV21, (int)AV_PIX_FMT_NV12);
135 if (mFrame->format == AV_PIX_FMT_NV12) {
136 for (int i = 0; i < h / 2; ++i) {
137 memcpy(w * h + mDecodedFrame.data() + i * w,
138 mFrame->data[1] + i * mFrame->linesize[1], w);
139 }
140 YuvConverter<uint8_t> convert8(w, h);
141 convert8.UVInterleavedToPlanar(mDecodedFrame.data());
142 } else {
143 for (int i = 0; i < h / 2; ++i) {
144 memcpy(w * h + mDecodedFrame.data() + i * w / 2,
145 mFrame->data[1] + i * mFrame->linesize[1], w / 2);
146 }
147 for (int i = 0; i < h / 2; ++i) {
148 memcpy(w * h + w * h / 4 + mDecodedFrame.data() + i * w / 2,
149 mFrame->data[2] + i * mFrame->linesize[2], w / 2);
150 }
151 }
152 MEDIA_DPRINT("copied Frame and it has presentation time at %lld",
153 (long long)(mFrame->pts));
154 }
155
flush()156 void MediaFfmpegVideoHelper::flush() {
157 MEDIA_DPRINT("flushing");
158 avcodec_send_packet(mCodecCtx, NULL);
159 fetchAllFrames();
160 MEDIA_DPRINT("flushing done");
161 }
162
fetchAllFrames()163 void MediaFfmpegVideoHelper::fetchAllFrames() {
164 while (true) {
165 int retframe = avcodec_receive_frame(mCodecCtx, mFrame);
166 if (retframe != 0) {
167 MEDIA_DPRINT("no more frames");
168 char tmp[1024];
169 av_strerror(retframe, tmp, sizeof(tmp));
170 MEDIA_DPRINT("WARNING: some unknown error %d: %s", retframe, tmp);
171 return;
172
173 break;
174 }
175 if (mIgnoreDecoderOutput) {
176 continue;
177 }
178 copyFrame();
179 MEDIA_DPRINT("save frame");
180 mSavedDecodedFrames.push_back(MediaSnapshotState::FrameInfo{
181 std::move(mDecodedFrame), std::vector<uint32_t>{},
182 (int)mFrame->width, (int)mFrame->height, (uint64_t)mFrame->pts,
183 ColorAspects{mFrame->color_primaries, mFrame->color_range,
184 mFrame->color_trc, mFrame->colorspace}});
185 }
186 }
187
decode(const uint8_t * data,size_t len,uint64_t pts)188 void MediaFfmpegVideoHelper::decode(const uint8_t* data,
189 size_t len,
190 uint64_t pts) {
191 MEDIA_DPRINT("calling with size %d", (int)len);
192 av_init_packet(&mPacket);
193 mPacket.data = (unsigned char*)data;
194 mPacket.size = len;
195 mPacket.pts = pts;
196 avcodec_send_packet(mCodecCtx, &mPacket);
197 fetchAllFrames();
198 MEDIA_DPRINT("done with size %d", (int)len);
199 }
200
201 } // namespace emulation
202 } // namespace android
203