1 /* 2 * Copyright 2007-2008, Haiku. Stephan Aßmus <superstippi@gmx.de> 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 #include "MediaTrackVideoSupplier.h" 6 7 #include <new> 8 #include <stdio.h> 9 #include <string.h> 10 11 #include <MediaTrack.h> 12 13 #include "ColorSpaceToString.h" 14 15 using std::nothrow; 16 17 #define DEBUG_DECODED_FRAME 0 18 #if DEBUG_DECODED_FRAME 19 # include <Bitmap.h> 20 # include <BitmapStream.h> 21 # include <File.h> 22 # include <TranslatorRoster.h> 23 #endif // DEBUG_DECODED_FRAME 24 25 // constructor 26 MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track, 27 int32 trackIndex, status_t& initStatus) 28 : 29 VideoTrackSupplier(), 30 fVideoTrack(track), 31 32 fPerformanceTime(0), 33 fDuration(0), 34 fCurrentFrame(0), 35 36 fTrackIndex(trackIndex) 37 { 38 if (!fVideoTrack) { 39 printf("MediaTrackVideoSupplier() - no video track\n"); 40 return; 41 } 42 43 initStatus = _SwitchFormat(B_NO_COLOR_SPACE, 0); 44 45 fDuration = fVideoTrack->Duration(); 46 47 // for (bigtime_t time = 0; time < fDuration; time += 10000) { 48 // bigtime_t keyFrameTime = time; 49 // fVideoTrack->FindKeyFrameForTime(&keyFrameTime, 50 // B_MEDIA_SEEK_CLOSEST_BACKWARD); 51 // printf("keyframe time for time: %lld -> %lld\n", time, keyFrameTime); 52 // } 53 } 54 55 // destructor 56 MediaTrackVideoSupplier::~MediaTrackVideoSupplier() 57 { 58 } 59 60 61 const media_format& 62 MediaTrackVideoSupplier::Format() const 63 { 64 return fFormat; 65 } 66 67 68 status_t 69 MediaTrackVideoSupplier::GetEncodedFormat(media_format* format) const 70 { 71 if (!fVideoTrack) 72 return B_NO_INIT; 73 return fVideoTrack->EncodedFormat(format); 74 } 75 76 77 status_t 78 MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const 79 { 80 if (!fVideoTrack) 81 return B_NO_INIT; 82 return fVideoTrack->GetCodecInfo(info); 83 } 84 85 86 status_t 87 MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime, 88 const media_raw_video_format& format, bool& wasCached) 89 { 90 if (!fVideoTrack) 91 return B_NO_INIT; 92 if (!buffer) 93 return B_BAD_VALUE; 94 95 status_t ret = B_OK; 96 if (format.display.format 97 != fFormat.u.raw_video.display.format 98 || fFormat.u.raw_video.display.bytes_per_row 99 != format.display.bytes_per_row) { 100 ret = _SwitchFormat(format.display.format, 101 format.display.bytes_per_row); 102 if (ret < B_OK) { 103 fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - " 104 "unable to switch media format: %s\n", strerror(ret)); 105 return ret; 106 } 107 } 108 109 // read a frame 110 int64 frameCount = 1; 111 // TODO: how does this work for interlaced video (field count > 1)? 112 media_header mediaHeader; 113 ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader); 114 115 if (ret < B_OK) { 116 if (ret != B_LAST_BUFFER_ERROR) { 117 fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - " 118 "error while reading frame of track: %s\n", strerror(ret)); 119 } 120 } else 121 fPerformanceTime = mediaHeader.start_time; 122 123 fCurrentFrame = fVideoTrack->CurrentFrame(); 124 if (performanceTime) 125 *performanceTime = fPerformanceTime; 126 127 #if DEBUG_DECODED_FRAME 128 if (modifiers() & B_SHIFT_KEY) { 129 BFile fileStream("/boot/home/Desktop/decoded.png", B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 130 BTranslatorRoster* roster = BTranslatorRoster::Default(); 131 BBitmap* bitmap = new BBitmap(Bounds(), 0, ColorSpace(), BytesPerRow()); 132 memcpy(bitmap->Bits(), buffer, bitmap->BitsLength()); 133 BBitmapStream bitmapStream(bitmap); 134 roster->Translate(&bitmapStream, NULL, NULL, &fileStream, B_PNG_FORMAT, 0); 135 bitmapStream.DetachBitmap(&bitmap); 136 delete bitmap; 137 } 138 #endif // DEBUG_DECODED_FRAME 139 140 return ret; 141 } 142 143 144 status_t 145 MediaTrackVideoSupplier::FindKeyFrameForFrame(int64* frame) 146 { 147 if (!fVideoTrack) 148 return B_NO_INIT; 149 150 //int64 wantedFrame = *frame; 151 status_t ret = fVideoTrack->FindKeyFrameForFrame(frame, 152 B_MEDIA_SEEK_CLOSEST_BACKWARD); 153 //printf("found keyframe for frame %lld -> %lld\n", wantedFrame, *frame); 154 return ret; 155 } 156 157 158 status_t 159 MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime) 160 { 161 if (!fVideoTrack) 162 return B_NO_INIT; 163 164 bigtime_t _performanceTime = *performanceTime; 165 status_t ret = fVideoTrack->FindKeyFrameForTime(performanceTime, 166 B_MEDIA_SEEK_CLOSEST_BACKWARD); 167 if (ret < B_OK) 168 return ret; 169 170 ret = fVideoTrack->SeekToTime(performanceTime); 171 if (ret == B_OK) { 172 if (_performanceTime != *performanceTime) { 173 printf("seeked by time: %" B_PRIdBIGTIME " -> %" B_PRIdBIGTIME 174 "\n", _performanceTime, *performanceTime); 175 } 176 fPerformanceTime = *performanceTime; 177 fCurrentFrame = fVideoTrack->CurrentFrame(); 178 } 179 180 return ret; 181 } 182 183 184 status_t 185 MediaTrackVideoSupplier::SeekToFrame(int64* frame) 186 { 187 if (!fVideoTrack) 188 return B_NO_INIT; 189 190 int64 wantFrame = *frame; 191 192 if (wantFrame == fCurrentFrame) 193 return B_OK; 194 195 status_t ret = fVideoTrack->FindKeyFrameForFrame(frame, 196 B_MEDIA_SEEK_CLOSEST_BACKWARD); 197 if (ret != B_OK) 198 return ret; 199 if (wantFrame > *frame) { 200 // Work around a rounding problem with some extractors and 201 // converting frames <-> time <-> internal time. 202 int64 nextWantFrame = wantFrame + 1; 203 if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame, 204 B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) { 205 if (nextWantFrame == wantFrame) { 206 wantFrame++; 207 *frame = wantFrame; 208 } 209 } 210 } 211 212 //if (wantFrame != *frame) { 213 // printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame); 214 //} 215 216 if (*frame <= fCurrentFrame && wantFrame >= fCurrentFrame) { 217 // The current frame is already closer to the wanted frame 218 // than the next keyframe before it. 219 *frame = fCurrentFrame; 220 return B_OK; 221 } 222 223 ret = fVideoTrack->SeekToFrame(frame); 224 if (ret != B_OK) 225 return ret; 226 227 //if (wantFrame != *frame) { 228 // printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame, 229 // fCurrentFrame); 230 //} 231 232 fCurrentFrame = *frame; 233 fPerformanceTime = fVideoTrack->CurrentTime(); 234 235 return ret; 236 } 237 238 239 // #pragma mark - 240 241 242 BRect 243 MediaTrackVideoSupplier::Bounds() const 244 { 245 return BRect(0, 0, fFormat.u.raw_video.display.line_width - 1, 246 fFormat.u.raw_video.display.line_count - 1); 247 } 248 249 250 color_space 251 MediaTrackVideoSupplier::ColorSpace() const 252 { 253 return fFormat.u.raw_video.display.format; 254 } 255 256 257 uint32 258 MediaTrackVideoSupplier::BytesPerRow() const 259 { 260 return fFormat.u.raw_video.display.bytes_per_row; 261 } 262 263 264 // #pragma mark - 265 266 267 status_t 268 MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow) 269 { 270 // get the encoded format 271 fFormat.Clear(); 272 status_t ret = fVideoTrack->EncodedFormat(&fFormat); 273 if (ret < B_OK) { 274 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 275 "fVideoTrack->EncodedFormat(): %s\n", strerror(ret)); 276 return ret; 277 } 278 279 // get ouput video frame size 280 uint32 width = fFormat.u.encoded_video.output.display.line_width; 281 uint32 height = fFormat.u.encoded_video.output.display.line_count; 282 if (format == B_NO_COLOR_SPACE) { 283 format = fFormat.u.encoded_video.output.display.format; 284 if (format == B_NO_COLOR_SPACE) { 285 // if still no preferred format, try the most commonly 286 // supported overlay format 287 format = B_YCbCr422; 288 } else { 289 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 290 "preferred color space: %s\n", 291 color_space_to_string(format)); 292 } 293 } 294 295 uint32 minBytesPerRow; 296 if (format == B_YCbCr422) 297 minBytesPerRow = ((width * 2 + 3) / 4) * 4; 298 else 299 minBytesPerRow = width * 4; 300 bytesPerRow = max_c(bytesPerRow, minBytesPerRow); 301 302 ret = _SetDecodedFormat(width, height, format, bytesPerRow); 303 if (ret < B_OK) { 304 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 305 "fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n", 306 strerror(ret)); 307 format = B_RGB32; 308 bytesPerRow = max_c(bytesPerRow, width * 4); 309 310 ret = _SetDecodedFormat(width, height, format, bytesPerRow); 311 if (ret < B_OK) { 312 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 313 "fVideoTrack->DecodedFormat(): %s - giving up\n", 314 strerror(ret)); 315 return ret; 316 } 317 } 318 319 if (fFormat.u.raw_video.display.format != format) { 320 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 321 " codec changed colorspace of decoded format (%s -> %s)!\n", 322 color_space_to_string(format), 323 color_space_to_string(fFormat.u.raw_video.display.format)); 324 // check if the codec forgot to adjust bytes_per_row 325 format = fFormat.u.raw_video.display.format; 326 if (format == B_YCbCr422) 327 minBytesPerRow = ((width * 2 + 3) / 4) * 4; 328 else 329 minBytesPerRow = width * 4; 330 if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) { 331 printf(" -> stupid codec forgot to adjust bytes_per_row!\n"); 332 333 ret = _SetDecodedFormat(width, height, format, minBytesPerRow); 334 } 335 } 336 337 if (fFormat.u.raw_video.last_active != height - 1) { 338 printf("should skip %" B_PRId32 " lines at bottom!\n", 339 (height - 1) - fFormat.u.raw_video.last_active); 340 } 341 342 return ret; 343 } 344 345 346 status_t 347 MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height, 348 color_space format, uint32 bytesPerRow) 349 { 350 // specifiy the decoded format. we derive this information from 351 // the encoded format (width & height). 352 fFormat.Clear(); 353 // fFormat.u.raw_video.last_active = height - 1; 354 // fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; 355 // fFormat.u.raw_video.pixel_width_aspect = 1; 356 // fFormat.u.raw_video.pixel_height_aspect = 1; 357 fFormat.u.raw_video.display.format = format; 358 fFormat.u.raw_video.display.line_width = width; 359 fFormat.u.raw_video.display.line_count = height; 360 fFormat.u.raw_video.display.bytes_per_row = bytesPerRow; 361 362 return fVideoTrack->DecodedFormat(&fFormat); 363 } 364 365