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 : VideoTrackSupplier() 29 , fVideoTrack(track) 30 31 , fPerformanceTime(0) 32 , fDuration(0) 33 , fCurrentFrame(0) 34 35 , fTrackIndex(trackIndex) 36 { 37 if (!fVideoTrack) { 38 printf("MediaTrackVideoSupplier() - no video track\n"); 39 return; 40 } 41 42 initStatus = _SwitchFormat(B_NO_COLOR_SPACE, 0); 43 44 fDuration = fVideoTrack->Duration(); 45 46 // for (bigtime_t time = 0; time < fDuration; time += 10000) { 47 // bigtime_t keyFrameTime = time; 48 // fVideoTrack->FindKeyFrameForTime(&keyFrameTime, 49 // B_MEDIA_SEEK_CLOSEST_BACKWARD); 50 // printf("keyframe time for time: %lld -> %lld\n", time, keyFrameTime); 51 // } 52 } 53 54 // destructor 55 MediaTrackVideoSupplier::~MediaTrackVideoSupplier() 56 { 57 } 58 59 60 const media_format& 61 MediaTrackVideoSupplier::Format() const 62 { 63 return fFormat; 64 } 65 66 67 status_t 68 MediaTrackVideoSupplier::GetEncodedFormat(media_format* format) const 69 { 70 if (!fVideoTrack) 71 return B_NO_INIT; 72 return fVideoTrack->EncodedFormat(format); 73 } 74 75 76 status_t 77 MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const 78 { 79 if (!fVideoTrack) 80 return B_NO_INIT; 81 return fVideoTrack->GetCodecInfo(info); 82 } 83 84 85 status_t 86 MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime, 87 const media_format* format, bool& wasCached) 88 { 89 if (!fVideoTrack) 90 return B_NO_INIT; 91 if (!buffer) 92 return B_BAD_VALUE; 93 94 status_t ret = B_OK; 95 if (format->u.raw_video.display.format 96 != fFormat.u.raw_video.display.format 97 || fFormat.u.raw_video.display.bytes_per_row 98 != format->u.raw_video.display.bytes_per_row) { 99 ret = _SwitchFormat(format->u.raw_video.display.format, 100 format->u.raw_video.display.bytes_per_row); 101 if (ret < B_OK) { 102 fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - " 103 "unable to switch media format: %s\n", strerror(ret)); 104 return ret; 105 } 106 } 107 108 // read a frame 109 int64 frameCount = 1; 110 // TODO: how does this work for interlaced video (field count > 1)? 111 media_header mediaHeader; 112 ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader); 113 114 if (ret < B_OK) { 115 if (ret != B_LAST_BUFFER_ERROR) { 116 fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - " 117 "error while reading frame of track: %s\n", strerror(ret)); 118 } 119 } else { 120 fPerformanceTime = mediaHeader.start_time; 121 } 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::SeekToTime(bigtime_t* performanceTime) 146 { 147 if (!fVideoTrack) 148 return B_NO_INIT; 149 150 bigtime_t _performanceTime = *performanceTime; 151 status_t ret = fVideoTrack->FindKeyFrameForTime(performanceTime, 152 B_MEDIA_SEEK_CLOSEST_BACKWARD); 153 if (ret < B_OK) 154 return ret; 155 156 ret = fVideoTrack->SeekToTime(performanceTime); 157 if (ret == B_OK) { 158 if (_performanceTime != *performanceTime) 159 printf("seeked by time: %lld -> %lld\n", _performanceTime, *performanceTime); 160 fPerformanceTime = *performanceTime; 161 fCurrentFrame = fVideoTrack->CurrentFrame(); 162 } 163 164 return ret; 165 } 166 167 168 status_t 169 MediaTrackVideoSupplier::SeekToFrame(int64* frame) 170 { 171 if (!fVideoTrack) 172 return B_NO_INIT; 173 174 int64 wantFrame = *frame; 175 int64 currentFrame = fVideoTrack->CurrentFrame(); 176 177 if (wantFrame == currentFrame) 178 return B_OK; 179 180 status_t ret = fVideoTrack->FindKeyFrameForFrame(frame, 181 B_MEDIA_SEEK_CLOSEST_BACKWARD); 182 if (ret < B_OK) 183 return ret; 184 185 if (*frame < currentFrame && wantFrame > currentFrame) { 186 *frame = currentFrame; 187 return B_OK; 188 } 189 190 if (wantFrame != *frame) { 191 printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame, 192 currentFrame); 193 } 194 195 ret = fVideoTrack->SeekToFrame(frame); 196 if (ret == B_OK) { 197 fCurrentFrame = *frame; 198 fPerformanceTime = fVideoTrack->CurrentTime(); 199 } 200 201 return ret; 202 } 203 204 205 // #pragma mark - 206 207 208 BRect 209 MediaTrackVideoSupplier::Bounds() const 210 { 211 return BRect(0, 0, fFormat.u.raw_video.display.line_width - 1, 212 fFormat.u.raw_video.display.line_count - 1); 213 } 214 215 216 color_space 217 MediaTrackVideoSupplier::ColorSpace() const 218 { 219 return fFormat.u.raw_video.display.format; 220 } 221 222 223 uint32 224 MediaTrackVideoSupplier::BytesPerRow() const 225 { 226 return fFormat.u.raw_video.display.bytes_per_row; 227 } 228 229 230 // #pragma mark - 231 232 233 status_t 234 MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow) 235 { 236 // get the encoded format 237 memset(&fFormat, 0, sizeof(media_format)); 238 status_t ret = fVideoTrack->EncodedFormat(&fFormat); 239 if (ret < B_OK) { 240 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 241 "fVideoTrack->EncodedFormat(): %s\n", strerror(ret)); 242 return ret; 243 } 244 245 // get ouput video frame size 246 uint32 width = fFormat.u.encoded_video.output.display.line_width; 247 uint32 height = fFormat.u.encoded_video.output.display.line_count; 248 if (format == B_NO_COLOR_SPACE) { 249 format = fFormat.u.encoded_video.output.display.format; 250 if (format == B_NO_COLOR_SPACE) { 251 // if still no preferred format, try the most commonly 252 // supported overlay format 253 format = B_YCbCr422; 254 } else { 255 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 256 "preferred color space: %s\n", 257 color_space_to_string(format)); 258 } 259 } 260 261 uint32 minBytesPerRow; 262 if (format == B_YCbCr422) 263 minBytesPerRow = ((width * 2 + 3) / 4) * 4; 264 else 265 minBytesPerRow = width * 4; 266 bytesPerRow = max_c(bytesPerRow, minBytesPerRow); 267 268 ret = _SetDecodedFormat(width, height, format, bytesPerRow); 269 if (ret < B_OK) { 270 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 271 "fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n", 272 strerror(ret)); 273 format = B_RGB32; 274 bytesPerRow = max_c(bytesPerRow, width * 4); 275 276 ret = _SetDecodedFormat(width, height, format, bytesPerRow); 277 if (ret < B_OK) { 278 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 279 "fVideoTrack->DecodedFormat(): %s - giving up\n", 280 strerror(ret)); 281 return ret; 282 } 283 } 284 285 if (fFormat.u.raw_video.display.format != format) { 286 printf("MediaTrackVideoSupplier::_SwitchFormat() - " 287 " codec changed colorspace of decoded format (%s -> %s)!\n", 288 color_space_to_string(format), 289 color_space_to_string(fFormat.u.raw_video.display.format)); 290 // check if the codec forgot to adjust bytes_per_row 291 format = fFormat.u.raw_video.display.format; 292 if (format == B_YCbCr422) 293 minBytesPerRow = ((width * 2 + 3) / 4) * 4; 294 else 295 minBytesPerRow = width * 4; 296 if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) { 297 printf(" -> stupid codec forgot to adjust bytes_per_row!\n"); 298 299 ret = _SetDecodedFormat(width, height, format, minBytesPerRow); 300 } 301 } 302 303 if (fFormat.u.raw_video.last_active != height - 1) { 304 printf("should skip %ld lines at bottom!\n", 305 (height - 1) - fFormat.u.raw_video.last_active); 306 } 307 308 return ret; 309 } 310 311 312 status_t 313 MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height, 314 color_space format, uint32 bytesPerRow) 315 { 316 // specifiy the decoded format. we derive this information from 317 // the encoded format (width & height). 318 memset(&fFormat, 0, sizeof(media_format)); 319 // fFormat.u.raw_video.last_active = height - 1; 320 // fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; 321 // fFormat.u.raw_video.pixel_width_aspect = 1; 322 // fFormat.u.raw_video.pixel_height_aspect = 1; 323 fFormat.u.raw_video.display.format = format; 324 fFormat.u.raw_video.display.line_width = width; 325 fFormat.u.raw_video.display.line_count = height; 326 fFormat.u.raw_video.display.bytes_per_row = bytesPerRow; 327 328 return fVideoTrack->DecodedFormat(&fFormat); 329 } 330 331